diff options
112 files changed, 13382 insertions, 45 deletions
diff --git a/firmware/microblaze/.gitignore b/firmware/microblaze/.gitignore index 068f01838..e867fe87c 100644 --- a/firmware/microblaze/.gitignore +++ b/firmware/microblaze/.gitignore @@ -5,6 +5,8 @@  /*.log  /*.rom  /.deps +/*.guess +/*.sub  /Makefile  /Makefile.in  /aclocal.m4 diff --git a/firmware/microblaze/Makefile.am b/firmware/microblaze/Makefile.am index 676c4fe42..ffc59192d 100644 --- a/firmware/microblaze/Makefile.am +++ b/firmware/microblaze/Makefile.am @@ -22,6 +22,7 @@ include $(top_srcdir)/Makefile.common  EXTRA_DIST = \  	u2_flash_tool -SUBDIRS = include lib apps - - +SUBDIRS = \ +	usrp2 \ +	usrp2p \ +	usrp2p/bootloader diff --git a/firmware/microblaze/Makefile.common b/firmware/microblaze/Makefile.common index 3d0f540d8..ceb6a553a 100644 --- a/firmware/microblaze/Makefile.common +++ b/firmware/microblaze/Makefile.common @@ -17,6 +17,8 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  # +include $(top_srcdir)/lib/Makefile.inc +  ########################################################################  # lwIP header include dirs  ######################################################################## @@ -29,21 +31,12 @@ LWIP_INCLUDES = \  	-I$(LWIPDIR)/src/include/ipv4  ######################################################################## -# local include dirs -######################################################################## -LOCAL_INCLUDES = \ -	-I$(top_srcdir)/include \ -	-I$(top_srcdir)/lib - -########################################################################  # misc flags for the mb-gcc compiler  ########################################################################  MBGCC_CFLAGS = \  	--std=gnu99 -Wall -Werror-implicit-function-declaration \  	-mxl-soft-div -msoft-float -mxl-soft-mul -mxl-barrel-shift -MBGCC_LFLAGS = -Wl,-defsym -Wl,_STACK_SIZE=3072 -  ########################################################################  # define for the hal io (FIXME move?)  ######################################################################## @@ -51,31 +44,39 @@ MBGCC_LFLAGS = -Wl,-defsym -Wl,_STACK_SIZE=3072  HAL_IO = -DHAL_IO_USES_UART   ######################################################################## -# set the cflags and ldflags +# common cflags and ldflags  ######################################################################## -AM_CFLAGS = $(MBGCC_CFLAGS) $(LOCAL_INCLUDES) $(LWIP_INCLUDES) $(HAL_IO) +COMMON_CFLAGS = \ +	-I$(top_srcdir)/../../host/lib/usrp \ +	-I$(top_srcdir)/lib \ +	$(MBGCC_CFLAGS) \ +	$(LWIP_INCLUDES) \ +	$(HAL_IO) -AM_LDFLAGS = $(MBGCC_LFLAGS) +COMMON_LFLAGS = \ +	-Wl,-Map -Wl,$(@:.elf=.map)  ########################################################################  # Common stuff for building top level microblaze images  ######################################################################## -AM_LDFLAGS += -Wl,-Map -Wl,$(@:.elf=.map) - -%.bin : %.elf +.elf.bin:  	$(MB_OBJCOPY) -O binary $< $@ -%.dump : %.elf +.elf.dump:  	$(MB_OBJDUMP) -DSC $< > $@ -%.rom : %.bin +.bin.rom:  	$(HEXDUMP) -v -e'1/1 "%.2X\n"' $< > $@ +.elf.ihx: +	$(MB_OBJCOPY) -O ihex $(COMMON_IHX_ARGS) $< $@ +  _generated_from_elf = \  	$(noinst_PROGRAMS:.elf=.map) \  	$(noinst_PROGRAMS:.elf=.bin) \  	$(noinst_PROGRAMS:.elf=.dump) \ -	$(noinst_PROGRAMS:.elf=.rom) +	$(noinst_PROGRAMS:.elf=.rom) \ +	$(noinst_PROGRAMS:.elf=.ihx)  noinst_DATA = $(_generated_from_elf) 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/cruft/Makefile.am b/firmware/microblaze/apps/cruft/Makefile.am new file mode 100644 index 000000000..a4f79935b --- /dev/null +++ b/firmware/microblaze/apps/cruft/Makefile.am @@ -0,0 +1,82 @@ +# +# Copyright 2010 Ettus Research LLC +# +# Copyright 2007,2008 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +LDADD = $(top_srcdir)/lib/libu2fw.a + +AM_CFLAGS += -I$(top_srcdir)/../../host/lib/usrp + +noinst_PROGRAMS = txrx_uhd.elf + +#	blink_leds \ +#	blink_leds2 \ +#	buf_ram_test \ +#	burn_dbsrx_eeprom \ +#	can_i_sub \ +#	echo \ +#	hello \ +#	read_dbids \ +#	set_hw_rev \ +#	test1 \ +#	test_db_spi \ +#	test_i2c \ +#	test_sd \ +#	test_ram \ +#	test_phy_comm \ +#	test_lsadc \ +#	test_lsdac \ +#	timer_test \ +#	txrx \ +#	burnrev30 \ +#	burnrev31 \ +#	burnrev40 \ +#	sd_gentest \ +#	sd_bounce +# + +#nononono =  \ +#	eth_serdes \ +#	gen_eth_packets \ +#	rcv_eth_packets \ +#	tx_standalone \ +#	factory_test \ +#	serdes_txrx \ +#	mimo_tx \ +#	mimo_tx_slave \ +#	ibs_rx_test \ +#	ibs_tx_test + +# tx_drop_SOURCES = tx_drop.c app_common.c +# tx_drop_rate_limited_SOURCES = tx_drop_rate_limited.c app_common.c +# tx_drop2_SOURCES = tx_drop2.c app_common.c +txrx_uhd_elf_SOURCES = txrx_uhd.c +# app_common_v2.c +#factory_test_SOURCES = factory_test.c app_common_v2.c +#eth_serdes_SOURCES = eth_serdes.c app_passthru_v2.c +#serdes_txrx_SOURCES = serdes_txrx.c app_common_v2.c +#mimo_tx_SOURCES = mimo_tx.c mimo_app_common_v2.c +#mimo_tx_slave_SOURCES = mimo_tx_slave.c app_common_v2.c + +#noinst_HEADERS = \ +#        app_common_v2.h \ +#        app_passthru_v2.h \ +#        mimo_app_common_v2.h +# + diff --git a/firmware/microblaze/apps/cruft/app_passthru_v2.c b/firmware/microblaze/apps/cruft/app_passthru_v2.c new file mode 100644 index 000000000..406c56b3b --- /dev/null +++ b/firmware/microblaze/apps/cruft/app_passthru_v2.c @@ -0,0 +1,251 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app_passthru_v2.h" +#include "buffer_pool.h" +#include "memcpy_wa.h" +#include "ethernet.h" +#include "nonstdio.h" +#include "print_rmon_regs.h" +#include "db.h" +#include "clocks.h" +#include <string.h> + +volatile bool link_is_up = false;	// eth handler sets this + + +// If this is non-zero, this dbsm could be writing to the ethernet +dbsm_t *ac_could_be_sending_to_eth; + +//static unsigned char exp_seqno = 0; + +void +set_reply_hdr(u2_eth_packet_t *reply_pkt, u2_eth_packet_t const *cmd_pkt) +{ +  reply_pkt->ehdr.dst = cmd_pkt->ehdr.src; +  reply_pkt->ehdr.src = *ethernet_mac_addr(); +  reply_pkt->ehdr.ethertype = U2_ETHERTYPE; +  reply_pkt->thdr.flags = 0; +  reply_pkt->thdr.fifo_status = 0;	// written by protocol engine +  reply_pkt->thdr.seqno = 0;		// written by protocol engine +  reply_pkt->thdr.ack = 0;		// written by protocol engine +  u2p_set_word0(&reply_pkt->fixed, 0, CONTROL_CHAN); +  reply_pkt->fixed.timestamp = timer_regs->time; +} + +static void +send_reply(unsigned char *reply, size_t reply_len) +{ +  if (reply_len < 64) +    reply_len = 64; + +  // wait for buffer to become idle +  hal_set_leds(0x4, 0x4); +  while((buffer_pool_status->status & BPS_IDLE(CPU_TX_BUF)) == 0) +    ; +  hal_set_leds(0x0, 0x4); + +  // copy reply into CPU_TX_BUF +  memcpy_wa(buffer_ram(CPU_TX_BUF), reply, reply_len); + +  // wait until nobody else is sending to the ethernet +  if (ac_could_be_sending_to_eth){ +    hal_set_leds(0x8, 0x8); +    dbsm_wait_for_opening(ac_could_be_sending_to_eth); +    hal_set_leds(0x0, 0x8); +  } + +  // fire it off +  bp_send_from_buf(CPU_TX_BUF, PORT_ETH, 1, 0, reply_len/4); + +  // wait for it to complete (not long, it's a small pkt) +  while((buffer_pool_status->status & (BPS_DONE(CPU_TX_BUF) | BPS_ERROR(CPU_TX_BUF))) == 0) +    ; + +  bp_clear_buf(CPU_TX_BUF); +} + + +static size_t +op_id_cmd(const op_generic_t *p, +	  void *reply_payload, size_t reply_payload_space) +{ +  op_id_reply_t *r = (op_id_reply_t *) reply_payload; +  if (reply_payload_space < sizeof(*r))	// no room +    return 0; + +  // Build reply subpacket + +  r->opcode = OP_ID_REPLY; +  r->len = sizeof(op_id_reply_t); +  r->rid = p->rid; +  r->addr = *ethernet_mac_addr(); +  r->hw_rev = 0x0000;	// FIXME +  // r->fpga_md5sum = ;	// FIXME +  // r->sw_md5sum = ;	// FIXME + +  // FIXME Add d'board info, including dbid, min/max gain, min/max freq + +  return r->len; +} + +static size_t +add_eop(void *reply_payload, size_t reply_payload_space) +{ +  op_generic_t *r = (op_generic_t *) reply_payload; +  if (reply_payload_space < sizeof(*r))		 +    return 0;					// no room + +  r->opcode = OP_EOP; +  r->len = sizeof(*r); +  r->rid = 0; +  r->ok =  0; + +  return r->len; +} + +bool +handle_control_chan_frame(u2_eth_packet_t *pkt, size_t len) +{ +  unsigned char reply[sizeof(u2_eth_packet_t) + 4 * sizeof(u2_subpkt_t)] _AL4; +  unsigned char *reply_payload = &reply[sizeof(u2_eth_packet_t)]; +  int reply_payload_space = sizeof(reply) - sizeof(u2_eth_packet_t); + +  bool handled_it = false; + +  // initialize reply +  memset(reply, 0, sizeof(reply)); +  set_reply_hdr((u2_eth_packet_t *) reply, pkt); + +  // point to beginning of payload (subpackets) +  unsigned char *payload = ((unsigned char *) pkt) + sizeof(u2_eth_packet_t); +  int payload_len = len - sizeof(u2_eth_packet_t); + +  size_t subpktlen = 0; + +  while (payload_len >= sizeof(op_generic_t)){ +    const op_generic_t *gp = (const op_generic_t *) payload; +    subpktlen = 0; + +    switch(gp->opcode){ +    case OP_EOP:		// end of subpackets +      goto end_of_subpackets; + +    case OP_ID: +      subpktlen = op_id_cmd(gp, reply_payload, reply_payload_space); +      handled_it = true; +      break; + +    default: +      if (0){ +	printf("\npassing on %d\n", gp->opcode); +      } +      break; +    } + +    int t = (gp->len + 3) & ~3;		// bump to a multiple of 4 +    payload += t; +    payload_len -= t; + +    subpktlen = (subpktlen + 3) & ~3;	// bump to a multiple of 4 +    reply_payload += subpktlen; +    reply_payload_space -= subpktlen; +  } + + end_of_subpackets: + +  if (handled_it){ +    // add the EOP marker +    subpktlen = add_eop(reply_payload, reply_payload_space); +    subpktlen = (subpktlen + 3) & ~3;	// bump to a multiple of 4 +    reply_payload += subpktlen; +    reply_payload_space -= subpktlen; + +    send_reply(reply, reply_payload - reply); +  } + +  return handled_it; +} + + +/* + * Called when an ethernet packet is received. + * Return true if we handled it here, otherwise + * it'll be passed on to the DSP Tx pipe + */ +bool +eth_pkt_inspector(dbsm_t *sm, int bufno) +{ +  u2_eth_packet_t *pkt = (u2_eth_packet_t *) buffer_ram(bufno); +  size_t byte_len = (buffer_pool_status->last_line[bufno] - 3) * 4; + +  //static size_t last_len = 0; + +  // hal_toggle_leds(0x1); + +  // inspect rcvd frame and figure out what do do. + +  if (pkt->ehdr.ethertype != U2_ETHERTYPE) +    return true;	// ignore, probably bogus PAUSE frame from MAC + +  int chan = u2p_chan(&pkt->fixed); + +  switch (chan){ +  case CONTROL_CHAN: +    return handle_control_chan_frame(pkt, byte_len); +    break; + +  case 0: +  default: +#if 0 +    if (last_len != 0){ +      if (byte_len != last_len){ +	printf("Len: %d last: %d\n", byte_len, last_len); +      } +    } +    last_len = byte_len; + +    if((pkt->thdr.seqno) == exp_seqno){ +      exp_seqno++; +      //putchar('.'); +    } +    else { +      // putchar('S'); +      //printf("S%d %d ",exp_seqno,pkt->thdr.seqno); +      exp_seqno = pkt->thdr.seqno + 1; +    } +#endif +    return false;	// pass it on to Tx DSP +    break; +  } +} + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ +  link_is_up = speed != 0; +  hal_set_leds(link_is_up ? LED_RJ45 : 0x0, LED_RJ45); +  printf("\neth link changed: speed = %d\n", speed); +} diff --git a/firmware/microblaze/apps/cruft/app_passthru_v2.h b/firmware/microblaze/apps/cruft/app_passthru_v2.h new file mode 100644 index 000000000..3904c670f --- /dev/null +++ b/firmware/microblaze/apps/cruft/app_passthru_v2.h @@ -0,0 +1,54 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_APP_COMMON_H +#define INCLUDED_APP_COMMON_H + +#include <stdbool.h> +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "memory_map.h" +#include "hal_io.h" +#include <stddef.h> +#include <db.h> + +#define CPU_TX_BUF 	7	// cpu -> eth + +#define	_AL4 __attribute__((aligned (4))) + +extern volatile bool link_is_up;	// eth handler sets this + + +// If there's a dbsm that sends to the ethernet, put it's address here +extern dbsm_t *ac_could_be_sending_to_eth; + + +void set_reply_hdr(u2_eth_packet_t *reply_pkt, u2_eth_packet_t const *cmd_pkt); + +/* + * Called when an ethernet packet is received. + * Return true if we handled it here, otherwise + * it'll be passed on to the DSP Tx pipe + */ +bool eth_pkt_inspector(dbsm_t *sm, int bufno); + +void link_changed_callback(int speed); + +bool handle_control_chan_frame(u2_eth_packet_t *pkt, size_t len); + +#endif /* INCLUDED_APP_COMMON_H */ diff --git a/firmware/microblaze/apps/cruft/blink_leds.c b/firmware/microblaze/apps/cruft/blink_leds.c new file mode 100644 index 000000000..682ca8db2 --- /dev/null +++ b/firmware/microblaze/apps/cruft/blink_leds.c @@ -0,0 +1,40 @@ +/* + * 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "hal_io.h" +#include "nonstdio.h" + +int +main(void) +{ +  int	counter = 0; + +  u2_init(); +   +  putstr("blink_leds\n"); +  while(1){ +    output_regs->leds = (counter++ & 0x3); +  } +   +  return 0; +} diff --git a/firmware/microblaze/apps/cruft/blink_leds2.c b/firmware/microblaze/apps/cruft/blink_leds2.c new file mode 100644 index 000000000..13e78afb3 --- /dev/null +++ b/firmware/microblaze/apps/cruft/blink_leds2.c @@ -0,0 +1,53 @@ +/* + * 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "hal_io.h" +#include "pic.h" +#include "nonstdio.h" + +//#define DELTA_T  (MASTER_CLK_RATE/2)	// 0.5s (10ns per tick) +#define DELTA_T  5000  		// 5 us (10ns per tick) + + +void +timer_handler(unsigned irq) +{ +  hal_set_timeout(DELTA_T);	// schedule next timeout +  hal_toggle_leds(0x2); +} + +int +main(void) +{ +  u2_init(); + +  putstr("blink_leds2\n"); +  pic_register_handler(IRQ_ONETIME, timer_handler); +  hal_set_timeout(DELTA_T);	// schedule next timeout + +  while(1){ +    hal_toggle_leds(0x1); +  } + +  return 0; +} diff --git a/firmware/microblaze/apps/cruft/buf_ram_test.c b/firmware/microblaze/apps/cruft/buf_ram_test.c new file mode 100644 index 000000000..1aca2aec5 --- /dev/null +++ b/firmware/microblaze/apps/cruft/buf_ram_test.c @@ -0,0 +1,89 @@ +/* + * 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/>. + */ + +#include "u2_init.h" +#include "memory_map.h" +#include <stdbool.h> +#include "nonstdio.h" +#include "hal_io.h" +#include "mdelay.h" + + +static void +write_bufs(void) +{ +  int	i, n; +  int	counter = 0; + +  for (n = 0; n < NBUFFERS; n++){ +    volatile int *p = buffer_ram(n); +    for (i = 0; i < BP_NLINES; i++) +      p[i] = counter++; +  } +} + +// return number of errors detected +static int +check_bufs(void) +{ +  int	i, n; +  int	counter = 0; +  int	nerrors = 0; + +  for (n = 0; n < NBUFFERS; n++){ +    volatile int *p = buffer_ram(n); +    for (i = 0; i < BP_NLINES; i++, counter++){ +      int rd = p[i]; +      if (rd != counter){ +	putchar('b'); +	putchar(n + '0'); +	putchar('['); +	puthex16(i); +	putstr("] exp: "); +	puthex32(counter); +	putstr(" got: "); +	puthex32_nl(rd); +	nerrors++; +      } +    } +  } +  return nerrors; +} + + +int +main(void) +{ +  u2_init(); + +  output_regs->leds = 0; + +  write_bufs(); +  int nerrors = check_bufs(); + +  if (nerrors == 0){ +    output_regs->leds = 0x3;		// leds on  -> PASS +    putstr("PASS\n"); +  } +  else { +    output_regs->leds = 0x0;		// leds off -> FAIL +    putstr("FAIL\n"); +  } + +  hal_finish(); +  return 0; +} diff --git a/firmware/microblaze/apps/cruft/burn_dbsrx_eeprom.c b/firmware/microblaze/apps/cruft/burn_dbsrx_eeprom.c new file mode 100644 index 000000000..116d4d8d0 --- /dev/null +++ b/firmware/microblaze/apps/cruft/burn_dbsrx_eeprom.c @@ -0,0 +1,106 @@ +/* -*- c++ -*- */ +/* + * 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "i2c.h" +#include "usrp2_i2c_addr.h" +#include "mdelay.h" +#include "hal_io.h" +#include "nonstdio.h" +#include <stdbool.h> + + + +int read_dboard_eeprom(int i2c_addr); + + +#define USRP_DBID_DBS_RX          	 0x0002 +#define USRP_DBID_DBS_RX_WITH_CLOCK_MOD  0x000d + +const char dbs_rx_rev2_eeprom[] = { +  0xdb, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18 +}; + +#define	LED_VALS   (LED_A | LED_B | LED_C | LED_D) +#define	LED_MASK   (LED_A | LED_B | LED_C | LED_D) + +int +main(void) +{ +  u2_init(); + +  puts("\nburn_dbsrx_eeprom\n"); + +  hal_set_leds(0, ~0);	// all off + +  int i2c_addr = I2C_ADDR_RX_A; +  int dbid = read_dboard_eeprom(i2c_addr); +  bool ok; +  const char *msg = 0; + +  switch (dbid){ +  case -1: +    msg = "No RX daughterboard found"; +    goto bad; + +  case -2: +    msg = "Invalid RX EEPROM contents"; +    goto bad; + +  case USRP_DBID_DBS_RX_WITH_CLOCK_MOD: +    msg = "RX Daughterboard already reports being a DBS RX w/ CLOCK_MOD"; +    goto good; + +  case USRP_DBID_DBS_RX: +    // Says it's a DBS_RX, attempt to burn the EEPROM +    ok = eeprom_write(i2c_addr, 0, +		      dbs_rx_rev2_eeprom, sizeof(dbs_rx_rev2_eeprom)); +    if (ok){ +      msg = "Successfully programmed db as DBS RX Rev 2.1"; +      goto good; +    } +    else { +      msg = "Failed to write daugherboard eeprom"; +      goto bad; +    } + +  default: +    msg = "Daughterboard is not a DBS RX; ignored"; +    goto bad; +  } +   + good: +  puts(msg); +  hal_set_leds(LED_VALS, LED_MASK); +  while (1) +    ; + + bad: +  puts(msg); +  while(1){ +    hal_toggle_leds(LED_VALS); +    mdelay(50); +  } +} diff --git a/firmware/microblaze/apps/cruft/burnrev30.c b/firmware/microblaze/apps/cruft/burnrev30.c new file mode 100644 index 000000000..40fa53e34 --- /dev/null +++ b/firmware/microblaze/apps/cruft/burnrev30.c @@ -0,0 +1,162 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <clocks.h> +#include "sd.h" +#include "mdelay.h" + +#define HW_REV_MAJOR 3 +#define HW_REV_MINOR 0 + +int test_ram() +{ +  int i,j,k; +  output_regs->ram_page = 1<<10; +   +  extram[0] = 0xDEADBEEF; +  extram[1] = 0xF00D1234; +  extram[7] = 0x76543210; +   +  output_regs->ram_page = 2<<10; +  extram[7] = 0x55555555; +  extram[1] = 0xaaaaaaaa; +  extram[0] = 0xeeeeeeee; +   +  output_regs->ram_page = 1<<10; +   +  i = extram[0]; +  k = extram[1]; +  j = extram[7]; +   +  if((i != 0xDEADBEEF)||(j!=0x76543210)||(k!=0xF00D1234)) { +    puts("RAM FAIL1!\n"); +    puthex32_nl(i); +    puthex32_nl(j); +    puthex32_nl(k); +    return 0; +  } +   +  output_regs->ram_page = 2<<10; + +  j = extram[7]; +  k = extram[1]; +  i = extram[0]; + +  if((i != 0xeeeeeeee)||(j!=0x55555555)||(k!=0xaaaaaaaa)) { +    puts("RAM FAIL2!\n"); +    puthex32_nl(i); +    puthex32_nl(j); +    puthex32_nl(k); +    return 0; +  } +  return 1; +} + +int test_sd() +{ +  int i = sd_init(); +  if(i==0) { +    puts("FAILED INIT of Card\n"); +    return 0; +  } +   +  unsigned char buf[512]; +  i = sd_read_block(2048,buf); +  if(i == 0) { +    puts("READ Command Rejected\n"); +    return 0; +  } +  if((buf[0]==0xb8)&&(buf[1]==0x08)&&(buf[2]==0x00)&&(buf[3]==0x50)) +    ; +  else { +    puts("Read bad data from SD Card\n"); +    return 0; +  } +  return 1; +} + +int +main(void) +{ +  u2_init(); + +  putstr("\nFactory Test, Board Rev 3.0\n"); + +  bool ok = true; +  unsigned char maj = HW_REV_MAJOR; +  unsigned char min = HW_REV_MINOR; +  ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_MSB, &maj, 1); +  ok &= eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_LSB, &min, 1); + +  putstr("\nset_hw_rev\n"); +  if (ok) +    printf("OK: set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); +  else { +    printf("FAILED to set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); +    hal_finish(); +    return 0; +  } + +  if(test_sd()) +    puts("SD OK\n"); +  else { +    puts("SD FAIL\n"); +    //hal_finish(); +    //return 0; +  } +  if(test_ram()) +    puts("RAM OK\n"); +  else { +    puts("RAM FAIL\n"); +    hal_finish(); +    return 0; +  } + +  print_mac_addr(ethernet_mac_addr()->addr); +  newline(); + +  clocks_mimo_config(MC_WE_LOCK_TO_SMA); + +  while (!clocks_lock_detect()) { +    puts("No Lock"); +    mdelay(1000); +  } +  puts("Clock Locked\n"); + +} diff --git a/firmware/microblaze/apps/cruft/burnrev31.c b/firmware/microblaze/apps/cruft/burnrev31.c new file mode 100644 index 000000000..f6b08d187 --- /dev/null +++ b/firmware/microblaze/apps/cruft/burnrev31.c @@ -0,0 +1,162 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <clocks.h> +#include "sd.h" +#include "mdelay.h" + +#define HW_REV_MAJOR 3 +#define HW_REV_MINOR 1 + +int test_ram() +{ +  int i,j,k; +  output_regs->ram_page = 1<<10; +   +  extram[0] = 0xDEADBEEF; +  extram[1] = 0xF00D1234; +  extram[7] = 0x76543210; +   +  output_regs->ram_page = 2<<10; +  extram[7] = 0x55555555; +  extram[1] = 0xaaaaaaaa; +  extram[0] = 0xeeeeeeee; +   +  output_regs->ram_page = 1<<10; +   +  i = extram[0]; +  k = extram[1]; +  j = extram[7]; +   +  if((i != 0xDEADBEEF)||(j!=0x76543210)||(k!=0xF00D1234)) { +    puts("RAM FAIL1!\n"); +    puthex32_nl(i); +    puthex32_nl(j); +    puthex32_nl(k); +    return 0; +  } +   +  output_regs->ram_page = 2<<10; + +  j = extram[7]; +  k = extram[1]; +  i = extram[0]; + +  if((i != 0xeeeeeeee)||(j!=0x55555555)||(k!=0xaaaaaaaa)) { +    puts("RAM FAIL2!\n"); +    puthex32_nl(i); +    puthex32_nl(j); +    puthex32_nl(k); +    return 0; +  } +  return 1; +} + +int test_sd() +{ +  int i = sd_init(); +  if(i==0) { +    puts("FAILED INIT of Card\n"); +    return 0; +  } +   +  unsigned char buf[512]; +  i = sd_read_block(2048,buf); +  if(i == 0) { +    puts("READ Command Rejected\n"); +    return 0; +  } +  if((buf[0]==0xb8)&&(buf[1]==0x08)&&(buf[2]==0x00)&&(buf[3]==0x50)) +    ; +  else { +    puts("Read bad data from SD Card\n"); +    return 0; +  } +  return 1; +} + +int +main(void) +{ +  u2_init(); + +  putstr("\nFactory Test, Board Rev 3.1\n"); + +  bool ok = true; +  unsigned char maj = HW_REV_MAJOR; +  unsigned char min = HW_REV_MINOR; +  ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_MSB, &maj, 1); +  ok &= eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_LSB, &min, 1); + +  putstr("\nset_hw_rev\n"); +  if (ok) +    printf("OK: set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); +  else { +    printf("FAILED to set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); +    hal_finish(); +    return 0; +  } + +  if(test_sd()) +    puts("SD OK\n"); +  else { +    puts("SD FAIL\n"); +    //hal_finish(); +    //return 0; +  } +  if(test_ram()) +    puts("RAM OK\n"); +  else { +    puts("RAM FAIL\n"); +    hal_finish(); +    return 0; +  } + +  print_mac_addr(ethernet_mac_addr()->addr); +  newline(); + +  clocks_mimo_config(MC_WE_LOCK_TO_SMA); + +  while (!clocks_lock_detect()) { +    puts("No Lock"); +    mdelay(1000); +  } +  puts("Clock Locked\n"); + +} diff --git a/firmware/microblaze/apps/cruft/can_i_sub.c b/firmware/microblaze/apps/cruft/can_i_sub.c new file mode 100644 index 000000000..ed49791f0 --- /dev/null +++ b/firmware/microblaze/apps/cruft/can_i_sub.c @@ -0,0 +1,25 @@ +#include <u2_init.h> +#include <nonstdio.h> + +//typedef long long int64_t; + + +int64_t sub(int64_t a, int64_t b); +void print(int64_t d); + +int main(void) +{ +  u2_init(); + +  int64_t d = sub(462550990848000LL, 462028800000000LL); +  print_uint64(d); +  newline(); +  return 0; +} + +int64_t sub(int64_t a, int64_t b) +{ +  return a - b; +} + + diff --git a/firmware/microblaze/apps/cruft/double_buffer_fragment.c b/firmware/microblaze/apps/cruft/double_buffer_fragment.c new file mode 100644 index 000000000..cfc061247 --- /dev/null +++ b/firmware/microblaze/apps/cruft/double_buffer_fragment.c @@ -0,0 +1,138 @@ +#if 0 +void  +double_buffering(int port) { +  unsigned int localstatus = buffer_pool_status->status; + +  if(localstatus & BPS_DONE_0) { +    bp_clear_buf(0); +    if(buffer_state[0] == FILLING) { +      buffer_state[0] = FULL; +      if(buffer_state[1] == EMPTY) { +	bp_receive_to_buf(1, 1, 1, 10, 509);  // DSP_RX to buffer 1, use 500 lines +	buffer_state[1] = FILLING; +      } +      else +	dsp_rx_idle = 1; +      if(serdes_tx_idle) { +	serdes_tx_idle = 0; +	bp_send_from_buf(0, port, 1, 10, 509);  // SERDES_TX from buffer 0 +	buffer_state[0] = EMPTYING; +      } +    } +    else {  // buffer was emptying +      buffer_state[0] = EMPTY; +      if(dsp_rx_idle) { +	dsp_rx_idle = 0; +	bp_receive_to_buf(0, 1, 1, 10, 509);  // DSP_RX to buffer 0, use 500 lines +	buffer_state[0] = FILLING; +      } +      if(buffer_state[1] == FULL) { +	bp_send_from_buf(1, port, 1, 10, 509);  // SERDES_TX from buffer 1 +	buffer_state[1] = EMPTYING; +      } +      else +	serdes_tx_idle = 1; +    } +    putstr("Int Proc'ed 0\n"); +  } + +  if(localstatus & BPS_DONE_1) { +    bp_clear_buf(1); +    if(buffer_state[1] == FILLING) { +      buffer_state[1] = FULL; +      if(buffer_state[0] == EMPTY) { +	bp_receive_to_buf(0, 1, 1, 10, 509);  // DSP_RX to buffer 1, use 500 lines +	buffer_state[0] = FILLING; +      } +      else +	dsp_rx_idle = 1; +      if(serdes_tx_idle) { +	serdes_tx_idle = 0; +	bp_send_from_buf(1, port, 1, 10, 509);  // SERDES_TX from buffer 1 +	buffer_state[1] = EMPTYING; +      } +    } +    else {  // buffer was emptying +      buffer_state[1] = EMPTY; +      if(dsp_rx_idle) { +	dsp_rx_idle = 0; +	bp_receive_to_buf(1, 1, 1, 10, 509);  // DSP_RX to buffer 1, use 500 lines +	buffer_state[1] = FILLING; +      } +      if(buffer_state[0] == FULL) { +	bp_send_from_buf(0, port, 1, 10, 509);  // SERDES_TX from buffer 0 +	buffer_state[0] = EMPTYING; +      } +      else +	serdes_tx_idle = 1; +    } +  putstr("Int Proc'ed 1\n"); +  } + +  if(localstatus & BPS_DONE_2) { +    bp_clear_buf(2); +    if(buffer_state[2] == FILLING) { +      buffer_state[2] = FULL; +      if(buffer_state[3] == EMPTY) { +	bp_receive_to_buf(3, port, 1, 5, 504);  // SERDES_RX to buffer 3, use 500 lines +	buffer_state[3] = FILLING; +      } +      else +	serdes_rx_idle = 1; +      if(dsp_tx_idle) { +	dsp_tx_idle = 0; +	bp_send_from_buf(2, 1, 1, 5, 504);  // DSP_TX from buffer 2 +	buffer_state[2] = EMPTYING; +      } +    } +    else {  // buffer was emptying +      buffer_state[2] = EMPTY; +      if(serdes_rx_idle) { +	serdes_rx_idle = 0; +	bp_receive_to_buf(2, port, 1, 5, 504);  // SERDES_RX to buffer 2 +	buffer_state[2] = FILLING; +      } +      if(buffer_state[3] == FULL) { +	bp_send_from_buf(3, 1, 1, 5, 504);  // DSP_TX from buffer 3 +	buffer_state[3] = EMPTYING; +      } +      else +	dsp_tx_idle = 1; +    } +  putstr("Int Proc'ed 2\n"); +  } + +  if(localstatus & BPS_DONE_3) { +    bp_clear_buf(3); +    if(buffer_state[3] == FILLING) { +      buffer_state[3] = FULL; +      if(buffer_state[2] == EMPTY) { +	bp_receive_to_buf(2, port, 1, 5, 504);  // SERDES_RX to buffer 2, use 500 lines +	buffer_state[2] = FILLING; +      } +      else +	serdes_rx_idle = 1; +      if(dsp_tx_idle) { +	dsp_tx_idle = 0; +	bp_send_from_buf(3, 1, 1, 5, 504);  // DSP_TX from buffer 3 +	buffer_state[3] = EMPTYING; +      } +    } +    else {  // buffer was emptying +      buffer_state[3] = EMPTY; +      if(serdes_rx_idle) { +	serdes_rx_idle = 0; +	bp_receive_to_buf(3, port, 1, 5, 504);  // SERDES_RX to buffer 3 +	buffer_state[3] = FILLING; +      } +      if(buffer_state[2] == FULL) { +	bp_send_from_buf(2, 1, 1, 5, 504);  // DSP_TX from buffer 2 +	buffer_state[2] = EMPTYING; +      } +      else +	dsp_tx_idle = 1; +    } +  putstr("Int Proc'ed 3\n"); +  } +} +#endif diff --git a/firmware/microblaze/apps/cruft/echo.c b/firmware/microblaze/apps/cruft/echo.c new file mode 100644 index 000000000..89108ee80 --- /dev/null +++ b/firmware/microblaze/apps/cruft/echo.c @@ -0,0 +1,34 @@ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "u2_init.h" +#include "stdio.h" + +int +main(void) +{ +  u2_init(); + +  puts("\n>>> echo <<<"); + +  while (1){ +    int ch = getchar(); +    putchar(ch); +  } + +  return 0; +} diff --git a/firmware/microblaze/apps/cruft/eth_serdes.c b/firmware/microblaze/apps/cruft/eth_serdes.c new file mode 100644 index 000000000..2d2ddc1ca --- /dev/null +++ b/firmware/microblaze/apps/cruft/eth_serdes.c @@ -0,0 +1,233 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_passthru_v2.h" +#include "memcpy_wa.h" +#include "clocks.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +#define FW_SETS_SEQNO	1	// define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno __attribute__((unused));	// used when f/w is filling in sequence numbers +#endif + + +/* + * Full duplex Tx and Rx between ethernet and serdes + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx  eth flow + */ +//#define CPU_RX_BUF	0	// eth -> cpu + +#define	DSP_RX_BUF_0	2	// serdes -> eth (double buffer) +#define	DSP_RX_BUF_1	3	// serdes -> eth +#define	DSP_TX_BUF_0	4	// eth -> serdes (double buffer) +#define	DSP_TX_BUF_1	5	// eth -> serdes + +/* + * ================================================================ + *   configure serdes double buffering state machine (eth -> serdes) + * ================================================================ + */ + + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { +  PORT_ETH, +  0, +  BP_LAST_LINE +}; + +// send to serdes +buf_cmd_args_t dsp_tx_send_args = { +  PORT_SERDES, +  0, +  0			// filled in from last_line register +}; + +dbsm_t dsp_tx_sm;	// the state machine + +/* + * ==================================================================== + *   configure serdes RX double buffering state machine (serdes -> eth) + * ==================================================================== + */ + +// receive from serdes +buf_cmd_args_t dsp_rx_recv_args = { +  PORT_SERDES, +  0, +  BP_LAST_LINE +}; + +// send to ETH +buf_cmd_args_t dsp_rx_send_args = { +  PORT_ETH, +  0,		// starts with ethernet header in line 0 +  0,		// filled in from list_line register +}; + +dbsm_t dsp_rx_sm;	// the state machine + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +// ---------------------------------------------------------------- + + +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY.  This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet.  A bit of a kludge + * but it should work. + */ + +bool  +fw_sets_seqno_inspector(dbsm_t *sm, int buf_this)	// returns false +{ +#if 0 +  uint32_t *p = buffer_ram(buf_this); +  uint32_t last_line = buffer_pool_status->last_line[buf_this] - sm->last_line_adj; +  printf("fw_sets_seqno_inspector: buf_this = %d, last_line = %d\n", +	 buf_this, last_line); + +  print_buffer(p, (last_line + 1)); +#endif + +#if 0 +  uint32_t *p = buffer_ram(buf_this); +  uint32_t seqno = fw_seqno++; + +  // KLUDGE all kinds of nasty magic numbers and embedded knowledge +  uint32_t t = p[4]; +  t = (t & 0xffff00ff) | ((seqno & 0xff) << 8); +  p[4] = t; +#endif + +  return false;		// we didn't handle the packet +} +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ +  uint32_t  status = buffer_pool_status->status; + +  if (0 && (status & ~BPS_IDLE_ALL)){ +    putstr("status = "); +    puthex32_nl(status); +  } +     +  dbsm_process_status(&dsp_tx_sm, status); +  dbsm_process_status(&dsp_rx_sm, status); +} + +int +main(void) +{ +  u2_init(); + +  output_regs->led_src = 0x3;		// h/w controls bottom two bits +  clocks_enable_test_clk(true, 1); + +  putstr("\neth <-> serdes\n"); + +  ethernet_register_link_changed_callback(link_changed_callback); +  ethernet_init(); + +  // clocks_mimo_config(MC_WE_LOCK_TO_SMA | MC_PROVIDE_CLK_TO_MIMO); +  clocks_mimo_config(MC_WE_DONT_LOCK | MC_PROVIDE_CLK_TO_MIMO); + +#if 0 +  // make bit 15 of Tx gpio's be a s/w output +  hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); +  hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + +#if 1 +  output_regs->debug_mux_ctrl = 1; +  hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); +  hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); +  hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); +  hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + +  // initialize double buffering state machine for ethernet -> serdes + +  dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, +	    &dsp_tx_recv_args, &dsp_tx_send_args, +	    eth_pkt_inspector); + + +  // initialize double buffering state machine for serdes -> ethernet + +  if (FW_SETS_SEQNO){ +    dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	      &dsp_rx_recv_args, &dsp_rx_send_args, +	      fw_sets_seqno_inspector); +  } +  else { +    dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	      &dsp_rx_recv_args, &dsp_rx_send_args, +	      dbsm_nop_inspector); +  } + +  // tell app_common that this dbsm could be sending to the ethernet +  ac_could_be_sending_to_eth = &dsp_rx_sm; + + +  // kick off the state machines +  dbsm_start(&dsp_tx_sm); +  dbsm_start(&dsp_rx_sm); + +  //int which = 0; + +  while(1){ +    // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); +    // which ^= 0x8000; + +    buffer_irq_handler(0); +  } +} diff --git a/firmware/microblaze/apps/cruft/factory_test.c b/firmware/microblaze/apps/cruft/factory_test.c new file mode 100644 index 000000000..e1fbb0e40 --- /dev/null +++ b/firmware/microblaze/apps/cruft/factory_test.c @@ -0,0 +1,438 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <clocks.h> +#include "sd.h" + +#define FW_SETS_SEQNO	1	// define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno;	// used when f/w is filling in sequence numbers +#endif + + +/* + * Full duplex Tx and Rx between ethernet and DSP pipelines + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx  eth flow + */ +//#define CPU_RX_BUF	0	// eth -> cpu + +#define	DSP_RX_BUF_0	2	// dsp rx -> eth (double buffer) +#define	DSP_RX_BUF_1	3	// dsp rx -> eth +#define	DSP_TX_BUF_0	4	// eth -> dsp tx (double buffer) +#define	DSP_TX_BUF_1	5	// eth -> dsp tx + +/* + * ================================================================ + *   configure DSP TX double buffering state machine (eth -> dsp) + * ================================================================ + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4) + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { +  PORT_ETH, +  0, +  BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { +  PORT_DSP, +  DSP_TX_FIRST_LINE,	// starts just past transport header +  0			// filled in from last_line register +}; + +dbsm_t dsp_tx_sm;	// the state machine + +/* + * ================================================================ + *   configure DSP RX double buffering state machine (dsp -> eth) + * ================================================================ + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4 + 1) + +// receive from DSP +buf_cmd_args_t dsp_rx_recv_args = { +  PORT_DSP, +  DSP_RX_FIRST_LINE, +  BP_LAST_LINE +}; + +// send to ETH +buf_cmd_args_t dsp_rx_send_args = { +  PORT_ETH, +  0,		// starts with ethernet header in line 0 +  0,		// filled in from list_line register +}; + +dbsm_t dsp_rx_sm;	// the state machine + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +// variables for streaming mode + +static bool         streaming_p = false; +static unsigned int streaming_items_per_frame = 0; +static int          streaming_frame_count = 0; +#define FRAMES_PER_CMD	1000 + +bool is_streaming(void){ return streaming_p; } + +// ---------------------------------------------------------------- + + +void +restart_streaming(void) +{ +  // setup RX DSP regs +  dsp_rx_regs->clear_state = 1;			// reset + +  streaming_p = true; +  streaming_frame_count = FRAMES_PER_CMD; + +  dsp_rx_regs->rx_command = +    MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, +	      streaming_items_per_frame, +	      1, 1);			// set "chain" bit + +  // kick off the state machine +  dbsm_start(&dsp_rx_sm); + +  dsp_rx_regs->rx_time = 0;		// enqueue first of two commands + +  // make sure this one and the rest have the "now" and "chain" bits set. +  dsp_rx_regs->rx_command = +    MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, +	      streaming_items_per_frame, +	      1, 1);				 + +  dsp_rx_regs->rx_time = 0;		// enqueue second command +} + +void +start_rx_streaming_cmd(const eth_mac_addr_t *host, op_start_rx_streaming_t *p) +{ +  host_mac_addr = *host;	// remember who we're sending to + +  /* +   * Construct  ethernet header and word0 and preload into two buffers +   */ +  u2_eth_packet_t	pkt; +  memset(&pkt, 0, sizeof(pkt)); +  pkt.ehdr.dst = *host; +  pkt.ehdr.src = *ethernet_mac_addr(); +  pkt.ehdr.ethertype = U2_ETHERTYPE; +  u2p_set_word0(&pkt.fixed, 0, 0); +  // DSP RX will fill in timestamp + +  memcpy_wa(buffer_ram(DSP_RX_BUF_0), &pkt, sizeof(pkt)); +  memcpy_wa(buffer_ram(DSP_RX_BUF_1), &pkt, sizeof(pkt)); + + +  if (FW_SETS_SEQNO) +    fw_seqno = 0; + +  streaming_items_per_frame = p->items_per_frame; +  restart_streaming(); +} + + +void +stop_rx_cmd(void) +{ +  streaming_p = false; +  dsp_rx_regs->clear_state = 1;	// flush cmd queue +  bp_clear_buf(DSP_RX_BUF_0); +  bp_clear_buf(DSP_RX_BUF_1); +} + + +static void +setup_tx() +{ +  dsp_tx_regs->clear_state = 1; +  bp_clear_buf(DSP_TX_BUF_0); +  bp_clear_buf(DSP_TX_BUF_1); + +  int tx_scale = 256; +  int interp = 32; + +  // setup some defaults + +  dsp_tx_regs->freq = 0; +  dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; +  dsp_tx_regs->interp_rate = interp; +} + + +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY.  This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet.  A bit of a kludge + * but it should work. + */ +bool  +fw_sets_seqno_inspector(dbsm_t *sm, int buf_this)	// returns false +{ +  uint32_t *p = buffer_ram(buf_this); +  uint32_t seqno = fw_seqno++; + +  // KLUDGE all kinds of nasty magic numbers and embedded knowledge +  uint32_t t = p[4]; +  t = (t & 0xffff00ff) | ((seqno & 0xff) << 8); +  p[4] = t; + +  // queue up another rx command when required +  if (streaming_p && --streaming_frame_count == 0){ +    streaming_frame_count = FRAMES_PER_CMD; +    dsp_rx_regs->rx_time = 0; +  } + +  return false;		// we didn't handle the packet +} +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ +  uint32_t  status = buffer_pool_status->status; + +  dbsm_process_status(&dsp_tx_sm, status); +  dbsm_process_status(&dsp_rx_sm, status); +} + +int test_ram() +{ +  int i,j,k; +  output_regs->ram_page = 1<<10; +   +  extram[0] = 0xDEADBEEF; +  extram[1] = 0xF00D1234; +  extram[7] = 0x76543210; +   +  output_regs->ram_page = 2<<10; +  extram[7] = 0x55555555; +  extram[1] = 0xaaaaaaaa; +  extram[0] = 0xeeeeeeee; +   +  output_regs->ram_page = 1<<10; +   +  i = extram[0]; +  k = extram[1]; +  j = extram[7]; +   +  if((i != 0xDEADBEEF)||(j!=0x76543210)||(k!=0xF00D1234)) { +    puts("RAM FAIL1!\n"); +    puthex32_nl(i); +    puthex32_nl(j); +    puthex32_nl(k); +    return 0; +  } +   +  output_regs->ram_page = 2<<10; + +  j = extram[7]; +  k = extram[1]; +  i = extram[0]; + +  if((i != 0xeeeeeeee)||(j!=0x55555555)||(k!=0xaaaaaaaa)) { +    puts("RAM FAIL2!\n"); +    puthex32_nl(i); +    puthex32_nl(j); +    puthex32_nl(k); +    return 0; +  } +  return 1; +} + +int test_sd() +{ +  int i = sd_init(); +  if(i==0) { +    puts("FAILED INIT of Card\n"); +    return 0; +  } +   +  unsigned char buf[512]; +  i = sd_read_block(2048,buf); +  if(i == 0) { +    puts("READ Command Rejected\n"); +    return 0; +  } +  if((buf[0]==0xb8)&&(buf[1]==0x08)&&(buf[2]==0x00)&&(buf[3]==0x50)) +    ; +  else { +    puts("Read bad data from SD Card\n"); +    return 0; +  } +  return 1; +} + +int +main(void) +{ +  u2_init(); + +  putstr("\nFactory Test\n"); + +  print_mac_addr(ethernet_mac_addr()->addr); +  newline(); + +  if(test_sd()) +    puts("SD OK\n"); +  else { +    puts("SD FAIL\n"); +    //    hal_finish(); +    //return 0; +  } +  if(test_ram()) +    puts("RAM OK\n"); +  else { +    puts("RAM FAIL\n"); +    hal_finish(); +    return 0; +  } + +  print_mac_addr(ethernet_mac_addr()->addr); +  newline(); + +  output_regs->led_src = 0x7;  // make bottom 3 controlled by HW + +  ethernet_register_link_changed_callback(link_changed_callback); +  ethernet_init(); + +  clocks_enable_tx_dboard(true,1); +  clocks_mimo_config(MC_WE_LOCK_TO_SMA); +#if 0 +  // make bit 15 of Tx gpio's be a s/w output +  hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); +  hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + +  output_regs->debug_mux_ctrl = 1; +#if 0 +  hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); +  hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); +  hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); +  hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + +  // initialize double buffering state machine for ethernet -> DSP Tx + +  dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, +	    &dsp_tx_recv_args, &dsp_tx_send_args, +	    eth_pkt_inspector); + + +  // initialize double buffering state machine for DSP RX -> Ethernet + +  if (FW_SETS_SEQNO){ +    dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	      &dsp_rx_recv_args, &dsp_rx_send_args, +	      fw_sets_seqno_inspector); +  } +  else { +    dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	      &dsp_rx_recv_args, &dsp_rx_send_args, +	      dbsm_nop_inspector); +  } + +  // tell app_common that this dbsm could be sending to the ethernet +  ac_could_be_sending_to_eth = &dsp_rx_sm; + + +  // program tx registers +  setup_tx(); + +  // kick off the state machine +  dbsm_start(&dsp_tx_sm); + +  //int which = 0; + +  while(1){ +    // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); +    // which ^= 0x8000; + +    buffer_irq_handler(0); + +    int pending = pic_regs->pending;		// poll for under or overrun + +    if (pending & PIC_UNDERRUN_INT){ +      dbsm_handle_tx_underrun(&dsp_tx_sm); +      pic_regs->pending = PIC_UNDERRUN_INT;	// clear interrupt +      putchar('U'); +    } + +    if (pending & PIC_OVERRUN_INT){ +      dbsm_handle_rx_overrun(&dsp_rx_sm); +      pic_regs->pending = PIC_OVERRUN_INT;	// clear pending interrupt + +      // FIXME Figure out how to handle this robustly. +      // Any buffers that are emptying should be allowed to drain... + +      if (streaming_p){ +	// restart_streaming(); +	// FIXME report error +      } +      else { +	// FIXME report error +      } +      putchar('O'); +    } +  } +} diff --git a/firmware/microblaze/apps/cruft/gen_eth_packets.c b/firmware/microblaze/apps/cruft/gen_eth_packets.c new file mode 100644 index 000000000..4d521f6bf --- /dev/null +++ b/firmware/microblaze/apps/cruft/gen_eth_packets.c @@ -0,0 +1,187 @@ +/* + * 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "memcpy_wa.h" +#include "print_rmon_regs.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +// ---------------------------------------------------------------- + +static eth_mac_addr_t dst_mac_addr = +  {{  0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}; + +// ---------------------------------------------------------------- + +// #define	PACKET_SIZE 1500		// bytes +// #define ETH_DATA_RATE 1000000		// 1MB/s +// #define	ETH_PACKET_RATE (ETH_DATA_RATE/PACKET_SIZE)	// 13,3333 pkts/s + +// static int timer_delta = MASTER_CLK_RATE/ETH_PACKET_RATE;	// ticks between interrupts + +static int timer_delta = (int)(MASTER_CLK_RATE * 1e-3);		// tick at 1 kHz +static int sim_timer_delta = (int)(MASTER_CLK_RATE * 100e-6);	// tick at 10 kHz + +static volatile bool send_packet_now = false;   // timer handler sets this +static volatile bool link_is_up = false;	// eth handler sets this + +int packet_number = 0; + + +#define CPU_TX_BUF	0	// cpu xmits ethernet frames from here +#define CPU_RX_BUF	1	// receive ethernet frames here + +// ---------------------------------------------------------------- + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ +  link_is_up = speed == 0 ? false : true; +  hal_set_leds(link_is_up ? 0x2 : 0x0, 0x2); +  printf("\neth link changed: speed = %d\n", speed); +} + +void +timer_irq_handler(unsigned irq) +{ +  hal_set_timeout(timer_delta);	// schedule next timeout +  send_packet_now = 1; +} + + +static void +init_packet(int *buf, const u2_eth_packet_t *pkt, int bufnum) +{ +  int i = 0; +  int mark = ((bufnum & 0xff) << 24) | 0x005A0000; + +  for (i = 0; i < BP_NLINES; i++){ +    buf[i] = mark | i; +    mark ^= 0x00FF0000; +  } + +  // copy header into buffer +  memcpy_wa(buf, pkt, sizeof(*pkt)); +} + +static void +init_packets(void) +{ +  u2_eth_packet_t	pkt __attribute__((aligned (4))); + +  memset(&pkt, 0, sizeof(pkt)); + +  pkt.ehdr.dst = dst_mac_addr; +  pkt.ehdr.src = *ethernet_mac_addr(); +  pkt.ehdr.ethertype = U2_ETHERTYPE; +  pkt.fixed.word0 = 0x01234567; +  pkt.fixed.timestamp = 0xffffffff; + +  // init just the one we're using +  init_packet((void *)buffer_ram(CPU_TX_BUF), &pkt, CPU_TX_BUF); +} + +int +main(void) +{ +  int npackets_sent = 0; + +  u2_init(); + +  // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output +  //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); +  //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + +  putstr("\ngen_eth_packets\n"); +   +  hal_set_leds(0x0, 0x3); + +  init_packets(); + +  pic_register_handler(IRQ_TIMER, timer_irq_handler); + +  if (hwconfig_simulation_p()) +    timer_delta = sim_timer_delta; + +  hal_set_timeout(timer_delta); + +  ethernet_register_link_changed_callback(link_changed_callback); +  ethernet_init(); + +  /* +  if (hwconfig_simulation_p()){ +    eth_mac->speed = 4;	// hardcode mac speed to 1000 +    link_is_up = true; +  } +  */ + +  // fire off a receive from the ethernet +  bp_receive_to_buf(CPU_RX_BUF, PORT_ETH, 1, 0, BP_LAST_LINE); + +  while(1){ +    uint32_t status = buffer_pool_status->status; + +    if (status & (BPS_DONE(CPU_RX_BUF) | BPS_ERROR(CPU_RX_BUF))){ +      bp_clear_buf(CPU_RX_BUF); +      // ignore incoming ethernet packets; they were looped back in sim +      bp_receive_to_buf(CPU_RX_BUF, PORT_ETH, 1, 0, BP_LAST_LINE); +    } + +    if (status & (BPS_DONE(CPU_TX_BUF) | BPS_ERROR(CPU_TX_BUF))){ +      if (status & BPS_ERROR(CPU_TX_BUF)){ +	putchar('E'); +      } +      bp_clear_buf(CPU_TX_BUF); +      npackets_sent++; +      if ((npackets_sent & 0xF) == 0){	// print after every 16 packets +	//print_rmon_regs(); +	putchar('.'); +      } +    } + +    if (link_is_up && send_packet_now && (status & BPS_IDLE(CPU_TX_BUF))){ +      send_packet_now = false; + +      // kick off the next packet +      // FIXME set packet number in packet + +      bp_send_from_buf(CPU_TX_BUF, PORT_ETH, 1, 0, 255);	// 1KB total +      hal_toggle_leds(0x1); +    } +  } + +  hal_finish(); +  return 1; +} diff --git a/firmware/microblaze/apps/cruft/gen_pause_frames.c b/firmware/microblaze/apps/cruft/gen_pause_frames.c new file mode 100644 index 000000000..0f81dafff --- /dev/null +++ b/firmware/microblaze/apps/cruft/gen_pause_frames.c @@ -0,0 +1,207 @@ +/* + * 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/>. + */ + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "u2_eth_packet.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> + + +// ---------------------------------------------------------------- + +unsigned char dst_mac_addr[6] = { +  0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +// ---------------------------------------------------------------- + +// #define	PACKET_SIZE 1500		// bytes +// #define ETH_DATA_RATE 1000000		// 1MB/s +// #define	ETH_PACKET_RATE (ETH_DATA_RATE/PACKET_SIZE)	// 13,3333 pkts/s + +// static int timer_delta = MASTER_CLK_RATE/ETH_PACKET_RATE;	// ticks between interrupts + +static int timer_delta = MASTER_CLK_RATE/1000;	// tick at 1kHz + +static volatile bool send_packet_now = false;   // timer handler sets this +static volatile bool link_is_up = false;	// eth handler sets this + +int packet_number = 0; + +// ---------------------------------------------------------------- + +// debugging output on tx pins +#define LS_MASK  0xE0000 +#define LS_1000  0x80000 +#define LS_100   0x40000 +#define LS_10    0x20000 + + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ +  int v = 0; +  switch(speed){ +  case 10: +    v = LS_10; +    link_is_up = true; +    break; +     +  case 100: +    v = LS_100; +    link_is_up = true; +    break; +     +  case 1000: +    v = LS_100; +    link_is_up = true; +    break; + +  default: +    v = 0; +    link_is_up = false; +    break; +  } + +  hal_gpio_set_tx(v, LS_MASK);	/* set debug bits on d'board */ + +  putstr("\neth link changed: speed = "); +  puthex16_nl(speed); +} + +void +timer_irq_handler(unsigned irq) +{ +  hal_set_timeout(timer_delta);	// schedule next timeout +  send_packet_now = 1; +} + + +void +buffer_irq_handler(unsigned irq) +{ +  // FIXME +} + +static void +init_packet(int *buf, const u2_eth_packet_t *pkt, int bufnum) +{ +  int i = 0; +  int mark = ((bufnum & 0xff) << 24) | 0x005A0000; + +  for (i = 0; i < BP_NLINES; i++){ +    buf[i] = mark | i; +    mark ^= 0x00FF0000; +  } + +  // copy header into buffer +  memcpy_wa(buf, pkt, sizeof(*pkt)); +} + +static void +init_packets(void) +{ +  int	i; +   +  u2_eth_packet_t	pkt __attribute__((aligned (4))); + +  for (i = 0; i < 6; i++){ +    pkt.ehdr.dst.addr[i] = dst_mac_addr[i]; +  } +  pkt.ehdr.src = *ethernet_mac_addr(); +  pkt.ehdr.ethertype = U2_ETHERTYPE; + +  // fill ALL buffers for debugging +  for (i = 0; i < 8; i++) +    init_packet((void *)buffer_ram(i), &pkt, i); +} + +static int led_counter = 0; + +int +main(void) +{ +  int send_pause = 1; +   +  u2_init(); + +  // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output +  //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); +  //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + +  putstr("\ngen_eth_packets\n"); +   +  // Control LEDs +  output_regs->leds = 0x00; + +  init_packets(); + +  // pic_register_handler(IRQ_BUFFER, buffer_irq_handler);  // poll for now +  pic_register_handler(IRQ_TIMER, timer_irq_handler); +  hal_set_timeout(timer_delta); + +  ethernet_register_link_changed_callback(link_changed_callback); + +  ethernet_init(); + +  eth_mac->pause_frame_send_en = 1; +  eth_mac->pause_quanta_set = 16384 / 512; + +  // eth_mac->speed = 4;	// FIXME hardcode mac speed to 1000 + +  while(1){ +    if (link_is_up && send_packet_now){ +      send_packet_now = false; + + +      if (send_pause) +	eth_mac->xon_cpu = 1; +      else +	eth_mac->xon_cpu = 0; + +      send_pause ^= 1; + +      // kick off the next packet +      // FIXME set packet number in packet + +#if 0 +      bp_send_from_buf(0, PORT_ETH, 1, 0, 255);	// 1KB total + +      while ((buffer_pool_status->status & (BPS_DONE_0|BPS_ERROR_0)) == 0) +	; +      bp_clear_buf(0); +#endif + +      output_regs->leds = ((++led_counter) & 0x1) | (link_is_up ? 0x2 : 0x0); +    } +  } + +  hal_finish(); +  return 1; +} diff --git a/firmware/microblaze/apps/cruft/hello.c b/firmware/microblaze/apps/cruft/hello.c new file mode 100644 index 000000000..bce843093 --- /dev/null +++ b/firmware/microblaze/apps/cruft/hello.c @@ -0,0 +1,30 @@ +/* + * 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/>. + */ + +#include "u2_init.h" +#include "stdio.h" + +int +main(void) +{ +  u2_init(); + +  puts("Hello World"); +  puts("Goodbye World"); + +  return 0; +} diff --git a/firmware/microblaze/apps/cruft/ibs_rx_test.c b/firmware/microblaze/apps/cruft/ibs_rx_test.c new file mode 100644 index 000000000..bdc04747e --- /dev/null +++ b/firmware/microblaze/apps/cruft/ibs_rx_test.c @@ -0,0 +1,82 @@ +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "nonstdio.h" + +#define PORT 2    // ethernet = 2, serdes = 0 +int dsp_rx_buf, dsp_tx_buf, serdes_rx_buf, serdes_tx_buf; +int dsp_rx_idle, dsp_tx_idle, serdes_rx_idle, serdes_tx_idle; + +int buffer_state[4]; + +static void __attribute__((unused)) +wait_until_status_nonzero(void)  +{ +  while (buffer_pool_status->status == 0) +    ; +} + +int +main(void) +{ +  int i; +   +  u2_init(); + +  output_regs->adc_ctrl = 0x0A; + +  dsp_rx_regs->freq = 0; +  dsp_rx_regs->scale_iq = (1 << 16) | 1; +  dsp_rx_regs->decim_rate = 8; + +  volatile unsigned int *buffer0 = buffer_ram(0); +  volatile unsigned int *buffer1 = buffer_ram(1); +  volatile unsigned int *buffer2 = buffer_ram(2); +   +  putstr("Starting RX\n"); +  bp_clear_buf(0); +  bp_receive_to_buf(0, 1, 1, 0, 99); + +  dsp_rx_regs->rx_command = (50 << 9) | 100;   // Numlines, lines per frame +  dsp_rx_regs->rx_time = 0x2000; + +  dsp_rx_regs->rx_command = (137 << 9) | 50;   // Numlines, lines per frame +  dsp_rx_regs->rx_time = 0x2200; + +  while (buffer_pool_status->status == 0) +    ; +  bp_clear_buf(0); +  bp_clear_buf(1); +  bp_receive_to_buf(1, 1, 1, 0, 99); +  while (buffer_pool_status->status == 0) +    ; +  bp_clear_buf(2); +  bp_receive_to_buf(2, 1, 1, 0, 99); +  while (buffer_pool_status->status == 0) +    ; +   +  for(i=0;i<100;i++) { +    puthex(i); +    putstr("   "); +    puthex_nl(buffer0[i]); +  } +  for(i=0;i<60;i++) { +    puthex(i); +    putstr("   "); +    puthex_nl(buffer1[i]); +  } +  for(i=0;i<60;i++) { +    puthex(i); +    putstr("   "); +    puthex_nl(buffer2[i]); +  } +  //while(timer_regs -> time < 0x6000) +  //  {} + +  putstr("Done\n"); +  hal_finish(); +   +  return 1; +} diff --git a/firmware/microblaze/apps/cruft/ibs_tx_test.c b/firmware/microblaze/apps/cruft/ibs_tx_test.c new file mode 100644 index 000000000..ff9446d92 --- /dev/null +++ b/firmware/microblaze/apps/cruft/ibs_tx_test.c @@ -0,0 +1,160 @@ +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "nonstdio.h" + +// Globals +#define EMPTY 0 +#define FILLING 1 +#define FULL 2 +#define EMPTYING 3 + +#define PORT 2    // ethernet = 2, serdes = 0 +int dsp_rx_buf, dsp_tx_buf, serdes_rx_buf, serdes_tx_buf; +int dsp_rx_idle, dsp_tx_idle, serdes_rx_idle, serdes_tx_idle; + +int buffer_state[4]; + +static void +wait_until_status_nonzero(void) +{ +  while (buffer_pool_status->status == 0) +    ; +} + +int +main(void) +{ +  int i; +   +  u2_init(); +   +  dsp_tx_regs->freq = 0; +  dsp_tx_regs->scale_iq = (1 << 16) | 1; +  dsp_tx_regs->interp_rate = 8; + +  // Write data to be sent into the first buffer +  volatile unsigned int *buffer0 = buffer_ram(0); +  volatile unsigned int *buffer1 = buffer_ram(1); +   + +  putstr("Starting to fill in RAM\n"); +  for(i=0;i<512;i++)  +    buffer0[i] = i; +  putstr("Filled in RAM\n"); +   +  buffer0[0] = 7; // start and end of buffer, send immediately +  buffer0[1] = 0x0000;  // start time +  bp_clear_buf(0); +  bp_send_from_buf(0, 1, 1, 0, 9); +  while (buffer_pool_status->status == 0) +    ; +  while(timer_regs -> time < 0x6000) +    {} + +  buffer0[0] = 3; // start and end of buffer +  buffer0[1] = 0x8000;  // start time +  bp_clear_buf(0); +  bp_send_from_buf(0, 1, 1, 0, 9); +  while (buffer_pool_status->status == 0) +    ; +  while(timer_regs -> time < 0x8400) +    {} + +  buffer0[0] = 3; // start and end of buffer +  buffer0[1] = 0x8800;  // start time +  bp_clear_buf(0); +  bp_send_from_buf(0, 1, 1, 0, 9); +  while (buffer_pool_status->status == 0) +    ; +  while(timer_regs -> time < 0x9000) +    {} + +  buffer0[0] = 0x2;  // not last +  buffer0[1] = 0x9100;  // start time +  bp_clear_buf(0); +  bp_send_from_buf(0, 1, 1, 0, 9); +  while (buffer_pool_status->status == 0) +    ; +  buffer0[0] = 0x1;  // last +  buffer0[1] = 0x0000;  // start time +  bp_clear_buf(0); +  bp_send_from_buf(0, 1, 1, 0, 9); +  while (buffer_pool_status->status == 0) +    ; + +   +  buffer0[0] = 0x3;  // first and last +  buffer0[1] = 0x8000;  // Time in the past +  bp_clear_buf(0); +  bp_send_from_buf(0, 1, 1, 0, 9); +  while (buffer_pool_status->status == 0) +    ; + +  /* +  buffer0[0] = 0x2; // not last +  buffer0[1] = 0x9600;  // start time +  bp_clear_buf(0); +  bp_send_from_buf(0, 1, 1, 0, 9); +  while (buffer_pool_status->status == 0) +    ; +  */ + +  while(timer_regs -> time < 0xa000) +    {} + +  putstr("Done\n"); + +  while(1) +    {} +  hal_finish(); +   +  // Send a bunch, let them pile up in FIFO +  bp_send_from_buf(0, 2, 1, 21, 80);    wait_until_status_nonzero(); +  bp_clear_buf(0); +  putstr("First add'l TX done\n"); +  bp_send_from_buf(0, 2, 1, 81, 288);   wait_until_status_nonzero(); +  bp_clear_buf(0); +  bp_send_from_buf(0, 2, 1, 289, 292);  wait_until_status_nonzero(); +  bp_clear_buf(0); +  bp_send_from_buf(0, 2, 1, 293, 326);  wait_until_status_nonzero(); +  bp_clear_buf(0); +  bp_send_from_buf(0, 2, 1, 327, 399);  wait_until_status_nonzero(); +  bp_clear_buf(0); +  bp_send_from_buf(0, 2, 1, 400, 511);  wait_until_status_nonzero(); +  bp_clear_buf(0); +  putstr("All add'l TX done\n"); +   +  bp_receive_to_buf(1, 2, 1, 21, 80);   wait_until_status_nonzero(); +  bp_clear_buf(1); +  putstr("First add'l RX done\n"); +  bp_receive_to_buf(1, 2, 1, 81, 288);  wait_until_status_nonzero(); +  bp_clear_buf(1); +  bp_receive_to_buf(1, 2, 1, 289, 292); wait_until_status_nonzero(); +  bp_clear_buf(1); +  bp_receive_to_buf(1, 2, 1, 293, 326); wait_until_status_nonzero(); +  bp_clear_buf(1); +  bp_receive_to_buf(1, 2, 1, 327, 399); wait_until_status_nonzero(); +  bp_clear_buf(1); +  bp_receive_to_buf(1, 2, 1, 400, 511); wait_until_status_nonzero(); +  bp_clear_buf(1); +  putstr("All add'l RX done\n"); + +  for(i=0;i<512;i++) +    if(buffer0[i] != buffer1[i]) { +      putstr("ERROR at location: "); +      puthex_nl(i); +      putstr("Value sent: "); +      puthex_nl(buffer0[i]); +      putstr("Value rcvd: "); +      puthex_nl(buffer1[i]); +      //break; +    } +   +  putstr("Done Testing\n"); +   +  hal_finish(); +  return 1; +} diff --git a/firmware/microblaze/apps/cruft/mimo_app_common_v2.c b/firmware/microblaze/apps/cruft/mimo_app_common_v2.c new file mode 100644 index 000000000..5dbecb0d0 --- /dev/null +++ b/firmware/microblaze/apps/cruft/mimo_app_common_v2.c @@ -0,0 +1,582 @@ +/* -*- 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mimo_app_common_v2.h" +#include "buffer_pool.h" +#include "memcpy_wa.h" +#include "ethernet.h" +#include "nonstdio.h" +#include "print_rmon_regs.h" +#include "db.h" +#include "db_base.h" +#include "clocks.h" +#include "u2_init.h" +#include <string.h> + +volatile bool link_is_up = false;	// eth handler sets this +int cpu_tx_buf_dest_port = PORT_ETH; + +// If this is non-zero, this dbsm could be writing to the ethernet +dbsm_t *ac_could_be_sending_to_eth; + +static unsigned char exp_seqno __attribute__((unused)) = 0; + +void abort(void); + +static bool +burn_mac_addr(const op_burn_mac_addr_t *p) +{ +  return ethernet_set_mac_addr(&p->addr); +} + +static bool +sync_to_pps(const op_generic_t *p) +{ +  timesync_regs->sync_on_next_pps = 1; +  putstr("SYNC to PPS\n"); +  return true; +} + +static bool +config_mimo_cmd(const op_config_mimo_t *p) +{ +  clocks_mimo_config(p->flags); +  return true; +} + +void +set_reply_hdr(u2_eth_packet_t *reply_pkt, u2_eth_packet_t const *cmd_pkt) +{ +  reply_pkt->ehdr.dst = cmd_pkt->ehdr.src; +  reply_pkt->ehdr.src = *ethernet_mac_addr(); +  reply_pkt->ehdr.ethertype = U2_ETHERTYPE; +  reply_pkt->thdr.flags = 0; +  reply_pkt->thdr.fifo_status = 0;	// written by protocol engine +  reply_pkt->thdr.seqno = 0;		// written by protocol engine +  reply_pkt->thdr.ack = 0;		// written by protocol engine +  u2p_set_word0(&reply_pkt->fixed, 0, CONTROL_CHAN); +  reply_pkt->fixed.timestamp = timer_regs->time; +} + +static void +send_reply(unsigned char *reply, size_t reply_len) +{ +  if (reply_len < 64) +    reply_len = 64; + +  // wait for buffer to become idle +  hal_set_leds(0x4, 0x4); +  while((buffer_pool_status->status & BPS_IDLE(CPU_TX_BUF)) == 0) +    ; +  hal_set_leds(0x0, 0x4); + +  // copy reply into CPU_TX_BUF +  memcpy_wa(buffer_ram(CPU_TX_BUF), reply, reply_len); + +  // wait until nobody else is sending to the ethernet +  if (ac_could_be_sending_to_eth){ +    hal_set_leds(0x8, 0x8); +    dbsm_wait_for_opening(ac_could_be_sending_to_eth); +    hal_set_leds(0x0, 0x8); +  } + +  if (0){ +    printf("sending_reply to port %d, len = %d\n", cpu_tx_buf_dest_port, (int)reply_len); +    print_buffer(buffer_ram(CPU_TX_BUF), reply_len/4); +  } + +  // fire it off +  bp_send_from_buf(CPU_TX_BUF, cpu_tx_buf_dest_port, 1, 0, reply_len/4); + +  // wait for it to complete (not long, it's a small pkt) +  while((buffer_pool_status->status & (BPS_DONE(CPU_TX_BUF) | BPS_ERROR(CPU_TX_BUF))) == 0) +    ; + +  bp_clear_buf(CPU_TX_BUF); +} + + +static size_t +op_id_cmd(const op_generic_t *p, +	  void *reply_payload, size_t reply_payload_space) +{ +  op_id_reply_t *r = (op_id_reply_t *) reply_payload; +  if (reply_payload_space < sizeof(*r))	// no room +    return 0; + +  // Build reply subpacket + +  r->opcode = OP_ID_REPLY; +  r->len = sizeof(op_id_reply_t); +  r->rid = p->rid; +  r->addr = *ethernet_mac_addr(); +  r->hw_rev = (u2_hw_rev_major << 8) | u2_hw_rev_minor; +  // r->fpga_md5sum = ;	// FIXME +  // r->sw_md5sum = ;	// FIXME + +  return r->len; +} + + +static size_t +config_tx_v2_cmd(const op_config_tx_v2_t *p, +		 void *reply_payload, size_t reply_payload_space) +{ +  op_config_tx_reply_v2_t *r = (op_config_tx_reply_v2_t *) reply_payload; +  if (reply_payload_space < sizeof(*r)) +    return 0;					// no room + +  struct tune_result	tune_result; +  memset(&tune_result, 0, sizeof(tune_result)); + +  bool ok = true; +   +#if 0 +  if (p->valid & CFGV_GAIN){ +    ok &= db_set_gain(tx_dboard, p->gain); +  } + +  if (p->valid & CFGV_FREQ){ +    bool was_streaming = is_streaming(); +    if (was_streaming) +      stop_rx_cmd(); +     +    u2_fxpt_freq_t f = u2_fxpt_freq_from_hilo(p->freq_hi, p->freq_lo); +    bool tune_ok = db_tune(tx_dboard, f, &tune_result); +    ok &= tune_ok; +    print_tune_result("Tx", tune_ok, f, &tune_result); + +    if (was_streaming) +      restart_streaming(); +  } + +  if (p->valid & CFGV_INTERP_DECIM){ +    int interp = p->interp; +    int hb1 = 0; +    int hb2 = 0; + +    if (!(interp & 1)){ +      hb2 = 1; +      interp = interp >> 1; +    } + +    if (!(interp & 1)){ +      hb1 = 1; +      interp = interp >> 1; +    } +     +    if (interp < MIN_CIC_INTERP || interp > MAX_CIC_INTERP) +      ok = false; +    else { +      dsp_tx_regs->interp_rate = (hb1<<9) | (hb2<<8) | interp; +      // printf("Interp: %d, register %d\n", p->interp, (hb1<<9) | (hb2<<8) | interp); +    } +  } + +  if (p->valid & CFGV_SCALE_IQ){ +    dsp_tx_regs->scale_iq = p->scale_iq; +  } +#endif + +  // Build reply subpacket + +  r->opcode = OP_CONFIG_TX_REPLY_V2; +  r->len = sizeof(*r); +  r->rid = p->rid; +  r->ok = ok; +  r->inverted = tune_result.inverted; +  r->baseband_freq_hi = u2_fxpt_freq_hi(tune_result.baseband_freq); +  r->baseband_freq_lo = u2_fxpt_freq_lo(tune_result.baseband_freq); +  r->duc_freq_hi = u2_fxpt_freq_hi(tune_result.dxc_freq); +  r->duc_freq_lo = u2_fxpt_freq_lo(tune_result.dxc_freq); +  r->residual_freq_hi = u2_fxpt_freq_hi(tune_result.residual_freq); +  r->residual_freq_lo = u2_fxpt_freq_lo(tune_result.residual_freq); +  return r->len; +} + +static size_t +config_rx_v2_cmd(const op_config_rx_v2_t *p,  +		 void *reply_payload, size_t reply_payload_space) +{ +  op_config_rx_reply_v2_t *r = (op_config_rx_reply_v2_t *) reply_payload; +  if (reply_payload_space < sizeof(*r)) +    return 0;				// no room + +  struct tune_result	tune_result; +  memset(&tune_result, 0, sizeof(tune_result)); + +  bool ok = true; +   +  if (p->valid & CFGV_GAIN){ +    ok &= db_set_gain(rx_dboard, p->gain); +  } + +  if (p->valid & CFGV_FREQ){ +    bool was_streaming = is_streaming(); +    if (was_streaming) +      stop_rx_cmd(); +     +    u2_fxpt_freq_t f = u2_fxpt_freq_from_hilo(p->freq_hi, p->freq_lo); +    bool tune_ok = db_tune(rx_dboard, f, &tune_result); +    ok &= tune_ok; +    print_tune_result("Rx", tune_ok, f, &tune_result); + +    if (was_streaming) +      restart_streaming(); +  } + +  if (p->valid & CFGV_INTERP_DECIM){ +    int decim = p->decim; +    int hb1 = 0; +    int hb2 = 0; +     +    if(!(decim & 1)) { +      hb2 = 1; +      decim = decim >> 1; +    } +     +    if(!(decim & 1)) { +      hb1 = 1; +      decim = decim >> 1; +    } +     +    if (decim < MIN_CIC_DECIM || decim > MAX_CIC_DECIM) +      ok = false; +    else { +      dsp_rx_regs->decim_rate = (hb1<<9) | (hb2<<8) | decim; +      // printf("Decim: %d, register %d\n", p->decim, (hb1<<9) | (hb2<<8) | decim); +    } +  } + +  if (p->valid & CFGV_SCALE_IQ){ +    dsp_rx_regs->scale_iq = p->scale_iq; +  } + +  // Build reply subpacket + +  r->opcode = OP_CONFIG_RX_REPLY_V2; +  r->len = sizeof(*r); +  r->rid = p->rid; +  r->ok = ok; +  r->inverted = tune_result.inverted; +  r->baseband_freq_hi = u2_fxpt_freq_hi(tune_result.baseband_freq); +  r->baseband_freq_lo = u2_fxpt_freq_lo(tune_result.baseband_freq); +  r->ddc_freq_hi = u2_fxpt_freq_hi(tune_result.dxc_freq); +  r->ddc_freq_lo = u2_fxpt_freq_lo(tune_result.dxc_freq); +  r->residual_freq_hi = u2_fxpt_freq_hi(tune_result.residual_freq); +  r->residual_freq_lo = u2_fxpt_freq_lo(tune_result.residual_freq); + +  return r->len; +} + +static size_t +read_time_cmd(const op_generic_t *p, +	      void *reply_payload, size_t reply_payload_space) +{ +  op_read_time_reply_t *r = (op_read_time_reply_t *) reply_payload; +  if (reply_payload_space < sizeof(*r))		 +    return 0;					// no room + +  r->opcode = OP_READ_TIME_REPLY; +  r->len = sizeof(*r); +  r->rid = p->rid; +  r->time = timer_regs->time; + +  return r->len; +} + +static void +fill_db_info(u2_db_info_t *p, const struct db_base *db) +{ +  p->dbid = db->dbid; +  p->freq_min_hi = u2_fxpt_freq_hi(db->freq_min); +  p->freq_min_lo = u2_fxpt_freq_lo(db->freq_min); +  p->freq_max_hi = u2_fxpt_freq_hi(db->freq_max); +  p->freq_max_lo = u2_fxpt_freq_lo(db->freq_max); +  p->gain_min = db->gain_min; +  p->gain_max = db->gain_max; +  p->gain_step_size = db->gain_step_size; +} + +static size_t +dboard_info_cmd(const op_generic_t *p, +		void *reply_payload, size_t reply_payload_space) +{ +  op_dboard_info_reply_t *r = (op_dboard_info_reply_t *) reply_payload; +  if (reply_payload_space < sizeof(*r))		 +    return 0;					// no room + +  r->opcode = OP_DBOARD_INFO_REPLY; +  r->len = sizeof(*r); +  r->rid = p->rid; +  r->ok = true; + +  fill_db_info(&r->tx_db_info, tx_dboard); +  fill_db_info(&r->rx_db_info, rx_dboard); + +  return r->len; +} + +static size_t +peek_cmd(const op_peek_t *p, +	 void *reply_payload, size_t reply_payload_space) +{ +  op_generic_t *r = (op_generic_t *) reply_payload; + +  putstr("peek: addr="); puthex32(p->addr); +  printf(" bytes=%u\n", p->bytes); + +  if ((reply_payload_space < (sizeof(*r) + p->bytes)) || +      p->bytes > MAX_SUBPKT_LEN - sizeof(op_generic_t)) { +    putstr("peek: insufficient reply packet space\n"); +    return 0;			// FIXME do partial read? +  } + +  r->opcode = OP_PEEK_REPLY; +  r->len = sizeof(*r)+p->bytes; +  r->rid = p->rid; +  r->ok = true; + +  memcpy_wa(reply_payload+sizeof(*r), (void *)p->addr, p->bytes); + +  return r->len; +} + +static bool +poke_cmd(const op_poke_t *p) +{ +  int bytes = p->len - sizeof(*p); +  putstr("poke: addr="); puthex32(p->addr); +  printf(" bytes=%u\n", bytes); + +  uint8_t *src = (uint8_t *)p + sizeof(*p); +  memcpy_wa((void *)p->addr, src, bytes); + +  return true; +} + +static size_t +generic_reply(const op_generic_t *p, +	      void *reply_payload, size_t reply_payload_space, +	      bool ok) +{ +  op_generic_t *r = (op_generic_t *) reply_payload; +  if (reply_payload_space < sizeof(*r))		 +    return 0;					// no room + +  r->opcode = p->opcode | OP_REPLY_BIT; +  r->len = sizeof(*r); +  r->rid = p->rid; +  r->ok = ok; + +  return r->len; +} + +static size_t +add_eop(void *reply_payload, size_t reply_payload_space) +{ +  op_generic_t *r = (op_generic_t *) reply_payload; +  if (reply_payload_space < sizeof(*r))		 +    return 0;					// no room + +  r->opcode = OP_EOP; +  r->len = sizeof(*r); +  r->rid = 0; +  r->ok =  0; + +  return r->len; +} + +void +handle_control_chan_frame(u2_eth_packet_t *pkt, size_t len) +{ +  unsigned char reply[sizeof(u2_eth_packet_t) + 4 * sizeof(u2_subpkt_t)] _AL4; +  unsigned char *reply_payload = &reply[sizeof(u2_eth_packet_t)]; +  int reply_payload_space = sizeof(reply) - sizeof(u2_eth_packet_t); + +  // initialize reply +  memset(reply, 0, sizeof(reply)); +  set_reply_hdr((u2_eth_packet_t *) reply, pkt); + +  // point to beginning of payload (subpackets) +  unsigned char *payload = ((unsigned char *) pkt) + sizeof(u2_eth_packet_t); +  int payload_len = len - sizeof(u2_eth_packet_t); + +  size_t subpktlen = 0; + +  while (payload_len >= sizeof(op_generic_t)){ +    const op_generic_t *gp = (const op_generic_t *) payload; +    subpktlen = 0; + +    // printf("\nopcode = %d\n", gp->opcode); + +    switch(gp->opcode){ +    case OP_EOP:		// end of subpackets +      goto end_of_subpackets; + +    case OP_ID: +      subpktlen = op_id_cmd(gp, reply_payload, reply_payload_space); +      break; +     +    case OP_CONFIG_TX_V2: +      subpktlen = config_tx_v2_cmd((op_config_tx_v2_t *) payload, +				   reply_payload, reply_payload_space); +      break; + +    case OP_CONFIG_RX_V2: +      subpktlen = config_rx_v2_cmd((op_config_rx_v2_t *) payload, +				   reply_payload, reply_payload_space); +      break; + +    case OP_START_RX_STREAMING: +      start_rx_streaming_cmd(&pkt->ehdr.src, (op_start_rx_streaming_t *) payload); +      subpktlen = generic_reply(gp, reply_payload, reply_payload_space, true); +      break; +     +    case OP_STOP_RX: +      stop_rx_cmd(); +      subpktlen = generic_reply(gp, reply_payload, reply_payload_space, true); +      break; +     +    case OP_BURN_MAC_ADDR: +      subpktlen = generic_reply(gp, reply_payload, reply_payload_space, +				burn_mac_addr((op_burn_mac_addr_t *) payload)); +      break; + +    case OP_CONFIG_MIMO: +      subpktlen = generic_reply(gp, reply_payload, reply_payload_space, +				config_mimo_cmd((op_config_mimo_t *) payload)); +      break; + +    case OP_READ_TIME: +      subpktlen = read_time_cmd(gp, reply_payload, reply_payload_space); +      break; + +    case OP_DBOARD_INFO: +      subpktlen = dboard_info_cmd(gp, reply_payload, reply_payload_space); +      break; + +    case OP_SYNC_TO_PPS: +      subpktlen = generic_reply(gp, reply_payload, reply_payload_space, +				sync_to_pps((op_generic_t *) payload)); +      break; + +    case OP_PEEK: +      subpktlen = peek_cmd((op_peek_t *)payload, reply_payload, reply_payload_space); +      break; + +    case OP_POKE: +      subpktlen = generic_reply(gp, reply_payload, reply_payload_space, +				poke_cmd((op_poke_t *)payload)); +      break; + +    default: +      printf("app_common_v2: unhandled opcode = %d\n", gp->opcode); +      break; +    } + +    int t = (gp->len + 3) & ~3;		// bump to a multiple of 4 +    payload += t; +    payload_len -= t; + +    subpktlen = (subpktlen + 3) & ~3;	// bump to a multiple of 4 +    reply_payload += subpktlen; +    reply_payload_space -= subpktlen; +  } + + end_of_subpackets: + +  // add the EOP marker +  subpktlen = add_eop(reply_payload, reply_payload_space); +  subpktlen = (subpktlen + 3) & ~3;	// bump to a multiple of 4 +  reply_payload += subpktlen; +  reply_payload_space -= subpktlen; + +  send_reply(reply, reply_payload - reply); +} + + +/* + * Called when an ethernet packet is received. + * Return true if we handled it here, otherwise + * it'll be passed on to the DSP Tx pipe + */ +int +eth_pkt_inspector(bsm12_t *sm, int bufno) +{ +  u2_eth_packet_t *pkt = (u2_eth_packet_t *) buffer_ram(bufno); +  size_t byte_len = (buffer_pool_status->last_line[bufno] - 3) * 4; + +  //static size_t last_len = 0; + +  // hal_toggle_leds(0x1); + +  // inspect rcvd frame and figure out what do do. + +  if (pkt->ehdr.ethertype != U2_ETHERTYPE) +    return true;	// ignore, probably bogus PAUSE frame from MAC + +  int chan = u2p_chan(&pkt->fixed); + +  switch (chan){ +  case CONTROL_CHAN: +    handle_control_chan_frame(pkt, byte_len); +    return -1; +    break; + +  case 0: +    return 0;	// pass it off to DSP TX + +  case 1: +    return 1;	// pass it off to SERDES TX + +  default: +    abort(); +    break; +  } +} + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ +  link_is_up = speed != 0; +  hal_set_leds(link_is_up ? LED_RJ45 : 0x0, LED_RJ45); +  printf("\neth link changed: speed = %d\n", speed); +} + + +void +print_tune_result(char *msg, bool tune_ok, +		  u2_fxpt_freq_t target_freq, struct tune_result *r) +{ +#if 0 +  printf("db_tune %s %s\n", msg, tune_ok ? "true" : "false"); +  putstr("  target_freq   "); print_fxpt_freq(target_freq); newline(); +  putstr("  baseband_freq "); print_fxpt_freq(r->baseband_freq); newline(); +  putstr("  dxc_freq      "); print_fxpt_freq(r->dxc_freq); newline(); +  putstr("  residual_freq "); print_fxpt_freq(r->residual_freq); newline(); +  printf("  inverted      %s\n", r->inverted ? "true" : "false"); +#endif +} diff --git a/firmware/microblaze/apps/cruft/mimo_app_common_v2.h b/firmware/microblaze/apps/cruft/mimo_app_common_v2.h new file mode 100644 index 000000000..1e62ced37 --- /dev/null +++ b/firmware/microblaze/apps/cruft/mimo_app_common_v2.h @@ -0,0 +1,63 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_APP_COMMON_H +#define INCLUDED_APP_COMMON_H + +#include <stdbool.h> +#include "usrp2_eth_packet.h" +#include "bsm12.h" +#include "memory_map.h" +#include "hal_io.h" +#include <stddef.h> +#include <db.h> + +#define CPU_TX_BUF 	7	// cpu -> eth + +#define	_AL4 __attribute__((aligned (4))) + +extern volatile bool link_is_up;	// eth handler sets this + +// If there's a dbsm that sends to the ethernet, put it's address here +extern dbsm_t *ac_could_be_sending_to_eth; + +extern int cpu_tx_buf_dest_port; + +void set_reply_hdr(u2_eth_packet_t *reply_pkt, u2_eth_packet_t const *cmd_pkt); + +/* + * Called when an ethernet packet is received. + */ +int eth_pkt_inspector(bsm12_t *sm, int bufno); + + +void link_changed_callback(int speed); + +void +print_tune_result(char *msg, bool tune_ok, +		  u2_fxpt_freq_t target_freq, struct tune_result *r); + + +void start_rx_streaming_cmd(const eth_mac_addr_t *host, op_start_rx_streaming_t *p); +void stop_rx_cmd(void); +void restart_streaming(void); +bool is_streaming(void); + +void handle_control_chan_frame(u2_eth_packet_t *pkt, size_t len); + +#endif /* INCLUDED_APP_COMMON_H */ diff --git a/firmware/microblaze/apps/cruft/mimo_tx.c b/firmware/microblaze/apps/cruft/mimo_tx.c new file mode 100644 index 000000000..e0f8aa6fa --- /dev/null +++ b/firmware/microblaze/apps/cruft/mimo_tx.c @@ -0,0 +1,363 @@ +/* + * 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/>. + */ + +/* + * This is a down and dirty test program that confirms that the we can + * coherently transmit different signals to two USRP2s connected via a + * mimo cable.  This code runs in the USRP2 connected to the ethernet. + * The other USRP runs mimo_tx_slave.  The host runs test_mimo_tx. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "bsm12.h" +#include "mimo_app_common_v2.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include "clocks.h" + +#define FW_SETS_SEQNO	1	// define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno;	// used when f/w is filling in sequence numbers +#endif + + +/* + * Experimental code to transmit packets to DSP Tx and SERDES + * + * Hard wire the Tx config so we don't have to deal with control stuff yet. + */ + +#define	BUF_BSM12_0	4 +#define	BUF_BSM12_1	5 +#define	BUF_BSM12_2	6 + +//#define CPU_TX_BUF	7	// cpu -> eth + +// 4 lines of ethernet hdr + 1 line transport hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4) + +// Receive from ethernet +buf_cmd_args_t bsm12_recv_args = { +  PORT_ETH, +  0, +  BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t bsm12_send0_args = { +  PORT_DSP, +  DSP_TX_FIRST_LINE,	// starts just past transport header +  0			// filled in from last_line register +}; + +// send to SERDES +buf_cmd_args_t bsm12_send1_args = { +  PORT_SERDES, +  0,			// starts just past transport header +  0			// filled in from last_line register +}; + +bsm12_t bsm12_sm;	// the state machine + +#if 0 +/* + * ================================================================ + *   configure DSP RX double buffering state machine (dsp -> eth) + * ================================================================ + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4 + 1) + +// receive from DSP +buf_cmd_args_t dsp_rx_recv_args = { +  PORT_DSP, +  DSP_RX_FIRST_LINE, +  BP_LAST_LINE +}; + +// send to ETH +buf_cmd_args_t dsp_rx_send_args = { +  PORT_ETH, +  0,		// starts with ethernet header in line 0 +  0,		// filled in from list_line register +}; + +dbsm_t dsp_rx_sm;	// the state machine +#endif + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +// variables for streaming mode + +static bool         streaming_p = false; +static unsigned int streaming_items_per_frame = 0; +static int          streaming_frame_count = 0; +#define FRAMES_PER_CMD	1000 + +bool is_streaming(void){ return streaming_p; } + + +// ---------------------------------------------------------------- + + +void +restart_streaming(void) +{ +#if 0 +  // setup RX DSP regs +  dsp_rx_regs->clear_state = 1;			// reset + +  streaming_p = true; +  streaming_frame_count = FRAMES_PER_CMD; + +  dsp_rx_regs->rx_command = +    MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, +	      streaming_items_per_frame, +	      1, 1);			// set "chain" bit + +  // kick off the state machine +  dbsm_start(&dsp_rx_sm); + +  dsp_rx_regs->rx_time = 0;		// enqueue first of two commands + +  // make sure this one and the rest have the "now" and "chain" bits set. +  dsp_rx_regs->rx_command = +    MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, +	      streaming_items_per_frame, +	      1, 1);				 + +  dsp_rx_regs->rx_time = 0;		// enqueue second command +#endif +} + +void +start_rx_streaming_cmd(const eth_mac_addr_t *host, op_start_rx_streaming_t *p) +{ +#if 0 +  host_mac_addr = *host;	// remember who we're sending to + +  /* +   * Construct  ethernet header and word0 and preload into two buffers +   */ +  u2_eth_packet_t	pkt; +  memset(&pkt, 0, sizeof(pkt)); +  pkt.ehdr.dst = *host; +  pkt.ehdr.src = *ethernet_mac_addr(); +  pkt.ehdr.ethertype = U2_ETHERTYPE; +  u2p_set_word0(&pkt.fixed, 0, 0); +  // DSP RX will fill in timestamp + +  memcpy_wa(buffer_ram(DSP_RX_BUF_0), &pkt, sizeof(pkt)); +  memcpy_wa(buffer_ram(DSP_RX_BUF_1), &pkt, sizeof(pkt)); + + +  if (FW_SETS_SEQNO) +    fw_seqno = 0; + +  streaming_items_per_frame = p->items_per_frame; +  restart_streaming(); +#endif +} + + +void +stop_rx_cmd(void) +{ +#if 0 +  streaming_p = false; +  dsp_rx_regs->clear_state = 1;	// flush cmd queue +  bp_clear_buf(DSP_RX_BUF_0); +  bp_clear_buf(DSP_RX_BUF_1); +#endif +} + + +static void +setup_tx() +{ +  dsp_tx_regs->clear_state = 1; + +  int tx_scale = 2500; +  int interp = 8;		// * 4 + +  // setup some defaults + +  dsp_tx_regs->freq = 429496730;	// 10MHz +  dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; +  dsp_tx_regs->interp_rate = (1 << 9) | (1 << 8) | interp; +} + + +#if 0 +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY.  This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet.  A bit of a kludge + * but it should work. + */ +int +fw_sets_seqno_inspector(bsm12_t *sm, int buf_this) +{ +  uint32_t *p = buffer_ram(buf_this); +  uint32_t seqno = fw_seqno++; + +  // KLUDGE all kinds of nasty magic numbers and embedded knowledge +  uint32_t t = p[4]; +  t = (t & 0xffff00ff) | ((seqno & 0xff) << 8); +  p[4] = t; + +  // queue up another rx command when required +  if (streaming_p && --streaming_frame_count == 0){ +    streaming_frame_count = FRAMES_PER_CMD; +    dsp_rx_regs->rx_time = 0; +  } + +  return false;		// we didn't handle the packet +} +#endif +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ +  uint32_t  status = buffer_pool_status->status; + +  bsm12_process_status(&bsm12_sm, status); +} + +int +main(void) +{ +  u2_init(); + +  putstr("\nMIMO Tx\n"); +  print_mac_addr(ethernet_mac_addr()->addr); +  newline(); + +  ethernet_register_link_changed_callback(link_changed_callback); +  ethernet_init(); + +  clocks_mimo_config(MC_WE_DONT_LOCK | MC_PROVIDE_CLK_TO_MIMO); + +#if 0 +  // make bit 15 of Tx gpio's be a s/w output +  hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); +  hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + +  output_regs->debug_mux_ctrl = 1; +#if 0 +  hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); +  hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); +  hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); +  hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + +  // initialize double buffering state machine for ethernet -> DSP Tx + +  bsm12_init(&bsm12_sm, BUF_BSM12_0, +	     &bsm12_recv_args, &bsm12_send0_args, &bsm12_send1_args, +	     eth_pkt_inspector); + + +#if 0 +  // initialize double buffering state machine for DSP RX -> Ethernet + +  if (FW_SETS_SEQNO){ +    dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	      &dsp_rx_recv_args, &dsp_rx_send_args, +	      fw_sets_seqno_inspector); +  } +  else { +    dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	      &dsp_rx_recv_args, &dsp_rx_send_args, +	      dbsm_nop_inspector); +  } + +  // tell app_common that this dbsm could be sending to the ethernet +  ac_could_be_sending_to_eth = &dsp_rx_sm; +#endif + + +  // program tx registers +  setup_tx(); + +  // kick off the state machine +  bsm12_start(&bsm12_sm); + +  //int which = 0; + +  while(1){ +    // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); +    // which ^= 0x8000; + +    buffer_irq_handler(0); + +    int pending = pic_regs->pending;		// poll for under or overrun + +    if (pending & PIC_UNDERRUN_INT){ +      // dbsm_handle_tx_underrun(&dsp_tx_sm); +      pic_regs->pending = PIC_UNDERRUN_INT;	// clear interrupt +      putchar('U'); +    } + +    if (pending & PIC_OVERRUN_INT){ +      // dbsm_handle_rx_overrun(&dsp_rx_sm); +      pic_regs->pending = PIC_OVERRUN_INT;	// clear pending interrupt + +      // FIXME Figure out how to handle this robustly. +      // Any buffers that are emptying should be allowed to drain... + +      if (streaming_p){ +	// restart_streaming(); +	// FIXME report error +      } +      else { +	// FIXME report error +      } +      putchar('O'); +    } +  } +} diff --git a/firmware/microblaze/apps/cruft/mimo_tx_slave.c b/firmware/microblaze/apps/cruft/mimo_tx_slave.c new file mode 100644 index 000000000..cdf9c03c2 --- /dev/null +++ b/firmware/microblaze/apps/cruft/mimo_tx_slave.c @@ -0,0 +1,376 @@ +/* + * 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/>. + */ + +/* + * This is a down and dirty test program that confirms that the we can + * coherently transmit different signals to two USRP2s connected via a + * mimo cable.  This code runs in the USRP2 NOT connected to the + * ethernet.  The USRP connected to the ethernet runs mimo_tx.  The + * host runs test_mimo_tx. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include "clocks.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +#define FW_SETS_SEQNO	1	// define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno;	// used when f/w is filling in sequence numbers +#endif + + +/* + * Full duplex Tx and Rx between serdes and DSP pipelines + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to serdes flow + * Buffers 4 and 5 are used to double-buffer the serdes to DSP Tx flow + */ +//#define CPU_RX_BUF	0	// eth -> cpu + +#define	DSP_RX_BUF_0	2	// dsp rx -> serdes (double buffer) +#define	DSP_RX_BUF_1	3	// dsp rx -> serdes +#define	DSP_TX_BUF_0	4	// serdes -> dsp tx (double buffer) +#define	DSP_TX_BUF_1	5	// serdes -> dsp tx + +/* + * ================================================================== + *   configure DSP TX double buffering state machine (serdes -> dsp) + * ================================================================== + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4) + +// Receive from serdes +buf_cmd_args_t dsp_tx_recv_args = { +  PORT_SERDES, +  0, +  BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { +  PORT_DSP, +  DSP_TX_FIRST_LINE,	// starts just past transport header +  0			// filled in from last_line register +}; + +dbsm_t dsp_tx_sm;	// the state machine + +/* + * ================================================================= + *   configure DSP RX double buffering state machine (dsp -> serdes) + * ================================================================= + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4 + 1) + +// receive from DSP +buf_cmd_args_t dsp_rx_recv_args = { +  PORT_DSP, +  DSP_RX_FIRST_LINE, +  BP_LAST_LINE +}; + +// send to serdes +buf_cmd_args_t dsp_rx_send_args = { +  PORT_SERDES, +  0,		// starts with ethernet header in line 0 +  0,		// filled in from list_line register +}; + +dbsm_t dsp_rx_sm;	// the state machine + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +// variables for streaming mode + +static bool         streaming_p = false; +static unsigned int streaming_items_per_frame = 0; +static int          streaming_frame_count = 0; +#define FRAMES_PER_CMD	1000 + +bool is_streaming(void){ return streaming_p; } + +// ---------------------------------------------------------------- + + +void +restart_streaming(void) +{ +  // setup RX DSP regs +  dsp_rx_regs->clear_state = 1;			// reset + +  streaming_p = true; +  streaming_frame_count = FRAMES_PER_CMD; + +  dsp_rx_regs->rx_command = +    MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, +	      streaming_items_per_frame, +	      1, 1);			// set "chain" bit + +  // kick off the state machine +  dbsm_start(&dsp_rx_sm); + +  dsp_rx_regs->rx_time = 0;		// enqueue first of two commands + +  // make sure this one and the rest have the "now" and "chain" bits set. +  dsp_rx_regs->rx_command = +    MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, +	      streaming_items_per_frame, +	      1, 1);				 + +  dsp_rx_regs->rx_time = 0;		// enqueue second command +} + +void +start_rx_streaming_cmd(const eth_mac_addr_t *host, op_start_rx_streaming_t *p) +{ +  host_mac_addr = *host;	// remember who we're sending to + +  /* +   * Construct  ethernet header and word0 and preload into two buffers +   */ +  u2_eth_packet_t	pkt; +  memset(&pkt, 0, sizeof(pkt)); +  pkt.ehdr.dst = *host; +  pkt.ehdr.src = *ethernet_mac_addr(); +  pkt.ehdr.ethertype = U2_ETHERTYPE; +  u2p_set_word0(&pkt.fixed, 0, 0); +  // DSP RX will fill in timestamp + +  memcpy_wa(buffer_ram(DSP_RX_BUF_0), &pkt, sizeof(pkt)); +  memcpy_wa(buffer_ram(DSP_RX_BUF_1), &pkt, sizeof(pkt)); + + +  if (FW_SETS_SEQNO) +    fw_seqno = 0; + +  streaming_items_per_frame = p->items_per_frame; +  restart_streaming(); +} + + +void +stop_rx_cmd(void) +{ +  streaming_p = false; +  dsp_rx_regs->clear_state = 1;	// flush cmd queue +  bp_clear_buf(DSP_RX_BUF_0); +  bp_clear_buf(DSP_RX_BUF_1); +} + + +static void +setup_tx() +{ +  dsp_tx_regs->clear_state = 1; +  bp_clear_buf(DSP_TX_BUF_0); +  bp_clear_buf(DSP_TX_BUF_1); + +  int tx_scale = 2500; +  int interp = 8;		// * 4 + +  // setup some defaults + +  dsp_tx_regs->freq = 429496730;	// 10MHz +  dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; +  dsp_tx_regs->interp_rate = (1 << 9) | (1 << 8) | interp; +} + + +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY.  This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet.  A bit of a kludge + * but it should work. + */ +bool  +fw_sets_seqno_inspector(dbsm_t *sm, int buf_this)	// returns false +{ +  uint32_t *p = buffer_ram(buf_this); +  uint32_t seqno = fw_seqno++; + +  // KLUDGE all kinds of nasty magic numbers and embedded knowledge +  uint32_t t = p[4]; +  t = (t & 0xffff00ff) | ((seqno & 0xff) << 8); +  p[4] = t; + +  // queue up another rx command when required +  if (streaming_p && --streaming_frame_count == 0){ +    streaming_frame_count = FRAMES_PER_CMD; +    dsp_rx_regs->rx_time = 0; +  } + +  return false;		// we didn't handle the packet +} +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ +  // hal_toggle_leds(LED_A); + +  uint32_t  status = buffer_pool_status->status; + +  if (0 && (status & ~BPS_IDLE_ALL)){ +    putstr("status = "); +    puthex32_nl(status); +  } + +  dbsm_process_status(&dsp_tx_sm, status); +  dbsm_process_status(&dsp_rx_sm, status); +} + +int +main(void) +{ +  u2_init(); + +  output_regs->led_src = 0x3;		// h/w controls bottom two bits +  clocks_enable_test_clk(true, 1); + +  putstr("\nMIMO Tx Slave\n"); + +  cpu_tx_buf_dest_port = PORT_SERDES; + +  // ethernet_register_link_changed_callback(link_changed_callback); +  // ethernet_init(); + +  clocks_mimo_config(MC_WE_LOCK_TO_MIMO); + +  // puts("post clocks_mimo_config"); + +#if 0 +  // make bit 15 of Tx gpio's be a s/w output +  hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); +  hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + +#if 0 +  output_regs->debug_mux_ctrl = 1; +  hal_gpio_set_sels(GPIO_TX_BANK, "0000000000000000"); +  hal_gpio_set_sels(GPIO_RX_BANK, "0000000000000000"); +  hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); +  hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + +  // initialize double buffering state machine for ethernet -> DSP Tx + +  dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, +	    &dsp_tx_recv_args, &dsp_tx_send_args, +	    eth_pkt_inspector); + + +  //output_regs->flush_icache = 1; +  +  // initialize double buffering state machine for DSP RX -> Ethernet + +  if (FW_SETS_SEQNO){ +    dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	      &dsp_rx_recv_args, &dsp_rx_send_args, +	      fw_sets_seqno_inspector); +  } +  else { +    dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	      &dsp_rx_recv_args, &dsp_rx_send_args, +	      dbsm_nop_inspector); +  } + +  // puts("post dbsm_init's"); + +  // tell app_common that this dbsm could be sending to the ethernet +  ac_could_be_sending_to_eth = &dsp_rx_sm; + + +  // program tx registers +  setup_tx(); + +  // puts("post setup_tx"); + +  // kick off the state machine +  dbsm_start(&dsp_tx_sm); + +  // puts("post dbsm_start"); + +  //int which = 0; + +  while(1){ +    // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); +    // which ^= 0x8000; + +    buffer_irq_handler(0); + +    int pending = pic_regs->pending;		// poll for under or overrun + +    if (pending & PIC_UNDERRUN_INT){ +      dbsm_handle_tx_underrun(&dsp_tx_sm); +      pic_regs->pending = PIC_UNDERRUN_INT;	// clear interrupt +      putchar('U'); +    } + +    if (pending & PIC_OVERRUN_INT){ +      dbsm_handle_rx_overrun(&dsp_rx_sm); +      pic_regs->pending = PIC_OVERRUN_INT;	// clear pending interrupt + +      // FIXME Figure out how to handle this robustly. +      // Any buffers that are emptying should be allowed to drain... + +      if (streaming_p){ +	// restart_streaming(); +	// FIXME report error +      } +      else { +	// FIXME report error +      } +      putchar('O'); +    } +  } +} diff --git a/firmware/microblaze/apps/cruft/rcv_eth_packets.c b/firmware/microblaze/apps/cruft/rcv_eth_packets.c new file mode 100644 index 000000000..03fc94354 --- /dev/null +++ b/firmware/microblaze/apps/cruft/rcv_eth_packets.c @@ -0,0 +1,233 @@ +/* + * 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "memcpy_wa.h" +#include <stddef.h> +#include <stdlib.h> + + +// ---------------------------------------------------------------- + +static eth_mac_addr_t dst_mac_addr = +  {{  0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}; + + +// ---------------------------------------------------------------- + +#define	PACKET_SIZE 1500		// bytes +#define ETH_DATA_RATE 1000000		// 1MB/s +#define	ETH_PACKET_RATE (ETH_DATA_RATE/PACKET_SIZE)	// 13,3333 pkts/s + +#define TIMER_RATE 100000000		// 100 MHz clock + +static int timer_delta = TIMER_RATE/ETH_PACKET_RATE;	// ticks between interrupts + +static volatile bool send_packet_now = false;   // timer handler sets this +static volatile bool link_is_up = false;	// eth handler sets this + +int packet_number = 0; + +// ---------------------------------------------------------------- + +// debugging output on tx pins +#define LS_MASK  0xE0000 +#define LS_1000  0x80000 +#define LS_100   0x40000 +#define LS_10    0x20000 + + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ +  int v = 0; +  switch(speed){ +  case 10: +    v = LS_10; +    link_is_up = true; +    break; +     +  case 100: +    v = LS_100; +    link_is_up = true; +    break; +     +  case 1000: +    v = LS_100; +    link_is_up = true; +    break; + +  default: +    v = 0; +    link_is_up = false; +    break; +  } + +  //hal_gpio_set_tx(v, LS_MASK);	/* set debug bits on d'board */ + +  putstr("\neth link changed: speed = "); +  puthex16_nl(speed); +} + +void +timer_irq_handler(unsigned irq) +{ +  hal_set_timeout(timer_delta);	// schedule next timeout +  send_packet_now = 1; +} + + +void +buffer_irq_handler(unsigned irq) +{ +  // FIXME +} + +static void +init_packet(int *buf, const u2_eth_packet_t *pkt, int bufnum) +{ +  int i = 0; +  int mark = ((bufnum & 0xff) << 24) | 0x005A0000; + +  for (i = 0; i < BP_NLINES; i++){ +    buf[i] = mark | i; +    mark ^= 0x00FF0000; +  } + +  // copy header into buffer +  memcpy_wa(buf, pkt, sizeof(*pkt)); +} + +static void +init_packets(void) +{ +  int	i; +   +  u2_eth_packet_t	pkt __attribute__((aligned (4))); + +  pkt.ehdr.dst = dst_mac_addr; +  pkt.ehdr.src = *ethernet_mac_addr(); +  pkt.ehdr.ethertype = U2_ETHERTYPE; + +  // fill ALL buffers for debugging +  for (i = 0; i < 8; i++) +    init_packet((void *)buffer_ram(i), &pkt, i); +} + +int +main(void) +{ +  u2_init(); + +  int	prev_leds = -1; +  int	new_leds  = 0x00; +  output_regs->leds = 0x00; + +  int peak_hold_count = 0; + +  // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output +  //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); +  //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + +  putstr("\nrcv_eth_packets\n"); +   +  init_packets(); + +  // pic_register_handler(IRQ_BUFFER, buffer_irq_handler);  // poll for now + +  // FIXME turn off timer since I don't think MTS and MFS instructions are implemented +  // pic_register_handler(IRQ_TIMER, timer_irq_handler); +  // hal_set_timeout(timer_delta); + +  ethernet_register_link_changed_callback(link_changed_callback); + +  ethernet_init(); + +  //eth_mac->speed = 4;	// FIXME hardcode mac speed to 1000 + +  // kick off a receive +  bp_receive_to_buf(2, PORT_ETH, 1, 0, 511); + +  while(1){ +    // u2_eth_packet_t	pkt; + +    new_leds = 0; +    if (link_is_up) +      new_leds = 0x2; + +    if ((buffer_pool_status->status & (BPS_DONE_2|BPS_ERROR_2)) != 0){ +      // we've got a packet! + +#if 0 +      // copy to stack buffer so we can byte address it +      memcpy_wa(&pkt, (void *)buffer_ram(2), sizeof(pkt)); +       +      putstr("Rx: src: "); +      print_mac_addr(pkt.ehdr.dst_addr); +      putstr(" dst: "); +      print_mac_addr(pkt.ehdr.src_addr); +      putstr(" ethtype: "); +      puthex16(pkt.ehdr.ethertype); +      putstr(" len: "); +      int len = (buffer_pool_status->last_line[2] + 1) * 4; +      puthex16_nl(len); +#else +      volatile int *bp = buffer_ram(2); +      int	i; +      for (i = 0; i < 16; i++){ +	puthex8(i); +	putchar(':'); +	puthex32_nl(bp[i]); +      } +#endif +       +      // kick off next receive +      bp_clear_buf(2); +      bp_receive_to_buf(2, PORT_ETH, 1, 0, 511); + +      peak_hold_count = 2048 * 10; +    } + +    if (peak_hold_count > 0){ +      peak_hold_count--; +      new_leds |= 0x1; +    } + +    if (new_leds != prev_leds){ +      prev_leds = new_leds; +      output_regs->leds = new_leds; +    } +  } + +  hal_finish(); +  return 1; +} diff --git a/firmware/microblaze/apps/cruft/read_dbids.c b/firmware/microblaze/apps/cruft/read_dbids.c new file mode 100644 index 000000000..24c6d9ab4 --- /dev/null +++ b/firmware/microblaze/apps/cruft/read_dbids.c @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <nonstdio.h> +#include <u2_init.h> +#include <stdbool.h> +#include <usrp2_i2c_addr.h> +#include <i2c.h> + + +int main(void) +{ +  u2_init(); + +  puts("\nread_dbids"); + +  unsigned char dbid_tx[2]; +  unsigned char dbid_rx[2]; +  bool ok; + +  ok = eeprom_read(I2C_ADDR_TX_A, 1, dbid_tx, 2); +  if (!ok){ +    puts("failed to read Tx Daugherboard EEPROM"); +  } +  else { +    putstr("Tx Daugherboard ID: "); +    puthex8(dbid_tx[1]);    // MSB +    puthex8(dbid_tx[0]);    // LSB +    newline(); +  } + +  ok = eeprom_read(I2C_ADDR_RX_A, 1, dbid_rx, 2); +  if (!ok){ +    puts("failed to read Rx Daugherboard EEPROM"); +  } +  else { +    putstr("Rx Daugherboard ID: "); +    puthex8(dbid_rx[1]);    // MSB +    puthex8(dbid_rx[0]);    // LSB +    newline(); +  } + +  return 0; +} diff --git a/firmware/microblaze/apps/cruft/sd_bounce.c b/firmware/microblaze/apps/cruft/sd_bounce.c new file mode 100644 index 000000000..c1b48f170 --- /dev/null +++ b/firmware/microblaze/apps/cruft/sd_bounce.c @@ -0,0 +1,153 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +/*  + * Loopback SERDES to SERDES + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "nonstdio.h" +#include "memset_wa.h" +#include <dbsm.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <clocks.h> + + + +// ---------------------------------------------------------------- + +#define SERDES_RX_BUF_0		0 +#define	SERDES_RX_BUF_1		1 + +/* + * ================================================================ + *      configure SD RX double buffering state machine + * ================================================================ + */ + +// receive from SERDES +buf_cmd_args_t sd_recv_args = { +  PORT_SERDES, +  0, +  BP_LAST_LINE +}; + +// send to SERDES +buf_cmd_args_t sd_send_args = { +  PORT_SERDES, +  0,		// starts with packet in line 0 +  0,		// filled in from list_line register +}; + +dbsm_t sd_sm;	// the state machine + + + + +// ---------------------------------------------------------------- + +#if 0 +static bool +check_packet(int *buf, int nlines) +{ +  bool ok = true; +  int i = 0; +  for (i = 0; i < nlines; i++){ +    int expected = ((2*i + 0) << 16) | (2*i+1); +    if (buf[i] != expected){ +      ok = false; +      printf("buf[%d] = 0x%x  expected = 0x%x\n", i, buf[i], expected); +    } +  } +  return ok; +} + +static void +zero_buffer(int bufno) +{ +  memset_wa(buffer_ram(bufno), 0, BP_NLINES * 4); +} +#endif + + +bool +sd_rx_inspector(dbsm_t *sm, int buf_this) +{ +  hal_toggle_leds(0x2); + +#if 0 +  int last_line = buffer_pool_status->last_line[buf_this]; +  bool ok = check_packet(buffer_ram(buf_this), last_line); +  static int good = 0; +  static int bad = 0; + +  if (ok) +    good++; +  else +    bad++; + +  if(good+bad == 10000) { +    printf("Good %d\tBad %d\n",good,bad); +    good = 0; +    bad = 0; +  } +#endif + +  return false; +} + + +inline static void +buffer_irq_handler(void) +{ +  uint32_t  status = buffer_pool_status->status; +  dbsm_process_status(&sd_sm, status); +} + + +int +main(void) +{ +  u2_init(); + +  putstr("\nsd_bounce\n"); + +  // Get our clock from the mimo interface +  clocks_mimo_config(MC_WE_LOCK_TO_MIMO); + +  dbsm_init(&sd_sm, SERDES_RX_BUF_0, +	    &sd_recv_args, &sd_send_args, +	    sd_rx_inspector); + +  // kick off the state machine +  dbsm_start(&sd_sm); + +  while(1){ +    buffer_irq_handler(); +  } +} diff --git a/firmware/microblaze/apps/cruft/sd_gentest.c b/firmware/microblaze/apps/cruft/sd_gentest.c new file mode 100644 index 000000000..35e912615 --- /dev/null +++ b/firmware/microblaze/apps/cruft/sd_gentest.c @@ -0,0 +1,269 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "nonstdio.h" +#include "memset_wa.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <clocks.h> +#include <mdelay.h> + +// ---------------------------------------------------------------- + +int packet_number = 0; +volatile bool send_packet_now = 0; + +#define SERDES_TX_BUF	0 +#define	SERDES_RX_BUF 	1 + + +#define NLINES_PER_PKT	380 + + +// ---------------------------------------------------------------- + +//static int timer_delta = (int)(MASTER_CLK_RATE * 100e-6); +static int timer_delta = 1000000; // .01 second + +void +timer_irq_handler(unsigned irq) +{ +  hal_set_timeout(timer_delta);	// schedule next timeout +  send_packet_now = true; +} + + +static void +init_packet(int *buf) +{ +  int i = 0; +  for (i = 0; i < BP_NLINES; i++){ +    buf[i] = ((2*i + 0) << 16) | (2*i+1); +  } +} + +static bool +check_packet(int *buf, int nlines) +{ +  bool ok = true; +  int i = 0; +  for (i = 0; i < nlines; i++){ +    int expected = ((2*i + 0) << 16) | (2*i+1); +    if (buf[i] != expected){ +      ok = false; +      printf("buf[%d] = 0x%x  expected = 0x%x\n", i, buf[i], expected); +    } +  } +  return ok; +} + +static void +zero_buffer(int bufno) +{ +  memset_wa(buffer_ram(bufno), 0, BP_NLINES * 4); +} + +static void +init_packets(void) +{ +  // init just the one we're using +  init_packet(buffer_ram(SERDES_TX_BUF)); +} + +int +main(void) +{ +  u2_init(); + +  // We're free running and provide clock to the MIMO interface +  clocks_mimo_config(MC_WE_DONT_LOCK | MC_PROVIDE_CLK_TO_MIMO); + + +  // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output +  // output_regs->debug_mux_ctrl = 1; +  // hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); +  // hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + +  putstr("\nsd_gentest\n"); +   +  // Set up serdes (already enabled) +  //output_regs->serdes_ctrl = (SERDES_ENABLE | SERDES_RXEN | SERDES_LOOPEN); +  //output_regs->serdes_ctrl = (SERDES_ENABLE | SERDES_RXEN); + +  init_packets(); + +  // pic_register_handler(IRQ_TIMER, timer_irq_handler); + +  //if (hwconfig_simulation_p()) +  //  timer_delta = sim_timer_delta; + +  // start a receive from sd +  zero_buffer(SERDES_RX_BUF); +  bp_receive_to_buf(SERDES_RX_BUF, PORT_SERDES, 1, 0, BP_LAST_LINE); + +  // fire off the first packet +  bp_send_from_buf(SERDES_TX_BUF, PORT_SERDES, 1, 0, NLINES_PER_PKT); +  hal_set_timeout(timer_delta); +  int ready_to_send = 0; + +  int counter __attribute__((unused)) = 0; +  int sent = 1; +  int txerr = 0; +  int rxerr = 0; +  int rcvd = 0; +  int rxcrc = 0; +  int sent_acc = 0; +  int txerr_acc = 0; +  int rxerr_acc = 0; +  int rcvd_acc = 0; +  int rxcrc_acc = 0; + +#define EXPECTING_PKT() ((counter & 0x1) == 0) +#define	SEND_PKT()      ((counter & 0x1) != 0) + +  bool got_packet = false; + +  while(1){ +    uint32_t status = buffer_pool_status->status; + +    if (status & (BPS_DONE(SERDES_RX_BUF))){ +      bp_clear_buf(SERDES_RX_BUF); +      got_packet = true; + +      //hal_toggle_leds(0x2); + +      // check packet +      int last_line = buffer_pool_status->last_line[SERDES_RX_BUF]-1; +      bool ok = check_packet(buffer_ram(SERDES_RX_BUF), last_line); +       +      if (ok) { +	rcvd++; +	//putchar('r'); +      } +      else { +	rcvd++; +	rxcrc++; +	//putchar('P'); +      } +      // start a receive from sd +      zero_buffer(SERDES_RX_BUF); +      bp_receive_to_buf(SERDES_RX_BUF, PORT_SERDES, 1, 0, BP_LAST_LINE); +    } + +    if (status & (BPS_ERROR(SERDES_RX_BUF))){ +      bp_clear_buf(SERDES_RX_BUF); +      got_packet = true; +      rcvd++; +      rxerr++; +      //putchar('E'); + +      // start a receive from sd +      zero_buffer(SERDES_RX_BUF); +      bp_receive_to_buf(SERDES_RX_BUF, PORT_SERDES, 1, 0, BP_LAST_LINE); +    } + +    if (status & (BPS_DONE(SERDES_TX_BUF))){ +      bp_clear_buf(SERDES_TX_BUF); +      //putchar('t'); +      bp_send_from_buf(SERDES_TX_BUF, PORT_SERDES, 1, 0, NLINES_PER_PKT); +      //mdelay(1); +      int	i; +      for (i = 0; i < 50; i++){ +	asm volatile ("or  r0, r0, r0\n\ +		   or  r0, r0, r0\n    \ +		   or  r0, r0, r0\n    \ +		   or  r0, r0, r0\n    \ +		   or  r0, r0, r0\n    \ +		   or  r0, r0, r0\n    \ +		   or  r0, r0, r0\n"); +      } +      sent ++; +      ready_to_send = 1; +      //hal_toggle_leds(0x1); +    } + +    if (status & BPS_ERROR(SERDES_TX_BUF)){ +      bp_clear_buf(SERDES_TX_BUF); +      sent++; +      txerr++; +      ready_to_send = 1; +      //putchar('X'); +    } + +    if(sent >=1000) { +      printf("Status\tSENT %d\tTXERR %d\t",sent,txerr); +      printf("RX %d\tERR %d\tCRC %d\tMISSED %d\n",rcvd, rxerr, rxcrc, sent-rcvd); +      sent_acc += sent; sent = 0; +      txerr_acc += txerr; txerr = 0; +      rcvd_acc += rcvd; rcvd = 0; +      rxerr_acc += rxerr; rxerr = 0; +      rxcrc_acc += rxcrc; rxcrc = 0; +    } + +    if(sent_acc >=10000) { +      printf("\nOverall\tSENT %d\tTXERR %d\t",sent_acc,txerr_acc); +      printf("RX %d\tERR %d\tCRC %d\tMISSED %d\n\n",rcvd_acc, rxerr_acc, rxcrc_acc, sent_acc-rcvd_acc); +      sent_acc = 0; +      txerr_acc = 0; +      rcvd_acc = 0; +      rxerr_acc = 0; +      rxcrc_acc = 0; +    } +#if 0 +    int pending = pic_regs->pending; +    if (pending & PIC_TIMER_INT){ +      hal_set_timeout(timer_delta); +       +      /* +	if (EXPECTING_PKT()){ +	if (!got_packet) +	  putchar('T'); +	got_packet = false; +      } + +      if (SEND_PKT()){ +	if (status & BPS_IDLE(SERDES_TX_BUF)) +	  bp_send_from_buf(SERDES_TX_BUF, PORT_SERDES, 1, 0, NLINES_PER_PKT); +      } +      counter++; +      */ + +      putchar('T'); +      if(ready_to_send) { +	bp_send_from_buf(SERDES_TX_BUF, PORT_SERDES, 1, 0, NLINES_PER_PKT); +	counter++; +	ready_to_send = 0; +      } +       +      pic_regs->pending = PIC_TIMER_INT;	// clear pending interrupt +    } +#endif +  } +   +  return 0; +} diff --git a/firmware/microblaze/apps/cruft/serdes_to_dsp.c b/firmware/microblaze/apps/cruft/serdes_to_dsp.c new file mode 100644 index 000000000..4994e0a69 --- /dev/null +++ b/firmware/microblaze/apps/cruft/serdes_to_dsp.c @@ -0,0 +1,179 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +/* + * This program can respond to queries from the host + * and stream rx samples. + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx  eth flow + */ +//#define CPU_RX_BUF	0	// eth -> cpu +//#define CPU_TX_BUF 	1	// cpu -> eth + +#define	DSP_RX_BUF_0	2	// dsp rx -> eth (double buffer) +#define	DSP_RX_BUF_1	3	// dsp rx -> eth +#define	DSP_TX_BUF_0	4	// eth -> dsp tx (double buffer) +#define	DSP_TX_BUF_1	5	// eth -> dsp tx + +/* + * ================================================================ + *      configure DSP TX double buffering state machine + * ================================================================ + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4) + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { +  PORT_SERDES, +  0, +  BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { +  PORT_DSP, +  DSP_TX_FIRST_LINE,	// starts just past transport header +  0			// filled in from last_line register +}; + +dbsm_t dsp_tx_sm;	// the state machine + + +// ---------------------------------------------------------------- + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +void +start_rx_cmd(const eth_mac_addr_t *host, op_start_rx_t *p) +{ +} + +void +stop_rx_cmd(void) +{ +} + +static void +setup_tx() +{ +  dsp_tx_regs->clear_state = 1; +  bp_clear_buf(DSP_TX_BUF_0); +  bp_clear_buf(DSP_TX_BUF_1); + +  int tx_scale = 256; +  int interp = 32; + +  op_config_tx_t def_config; +  memset(&def_config, 0, sizeof(def_config)); +  def_config.phase_inc  = 408021893;			// 9.5 MHz [2**32 * fc/fsample] +  def_config.scale_iq = (tx_scale << 16) | tx_scale; +  def_config.interp = interp; + +  // setup Tx DSP regs +  config_tx_cmd(&def_config); +} + + +inline static void +buffer_irq_handler(unsigned irq) +{ +  //hal_toggle_leds(0x2); + +  uint32_t  status = buffer_pool_status->status; + +  dbsm_process_status(&dsp_tx_sm, status); + +  if (status & BPS_DONE(CPU_TX_BUF)){ +    bp_clear_buf(CPU_TX_BUF); +  } +} + +int +main(void) +{ +  u2_init(); + +  // Get our clock from the mimo interface + +  clocks_enable_test_clk(true,1); +  clocks_mimo_config(MC_WE_LOCK_TO_MIMO); +   +  // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output +  //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); +  //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + +  putstr("\nserdes_to_dsp\n"); + +  ethernet_register_link_changed_callback(link_changed_callback); +  ethernet_init(); + + +  // initialize double buffering state machine for ethernet -> DSP Tx + +  dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, +	    &dsp_tx_recv_args, &dsp_tx_send_args, +	    eth_pkt_inspector); + +  // program tx registers +  setup_tx(); + +  // kick off the state machine +  dbsm_start(&dsp_tx_sm); + +  while(1){ +    buffer_irq_handler(0); + +    int pending = pic_regs->pending;	// poll for under or overrun + +    if (pending & PIC_UNDERRUN_INT){ +      dbsm_handle_tx_underrun(&dsp_tx_sm); +      pic_regs->pending = PIC_UNDERRUN_INT;	// clear interrupt +      putchar('U'); +    } +  } +} + diff --git a/firmware/microblaze/apps/cruft/serdes_txrx.c b/firmware/microblaze/apps/cruft/serdes_txrx.c new file mode 100644 index 000000000..2c47c9628 --- /dev/null +++ b/firmware/microblaze/apps/cruft/serdes_txrx.c @@ -0,0 +1,368 @@ +/* + * 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "dbsm.h" +#include "app_common_v2.h" +#include "memcpy_wa.h" +#include "clocks.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + + +#define FW_SETS_SEQNO	1	// define to 0 or 1 (FIXME must be 1 for now) + +#if (FW_SETS_SEQNO) +static int fw_seqno;	// used when f/w is filling in sequence numbers +#endif + + +/* + * Full duplex Tx and Rx between serdes and DSP pipelines + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to serdes flow + * Buffers 4 and 5 are used to double-buffer the serdes to DSP Tx flow + */ +//#define CPU_RX_BUF	0	// eth -> cpu + +#define	DSP_RX_BUF_0	2	// dsp rx -> serdes (double buffer) +#define	DSP_RX_BUF_1	3	// dsp rx -> serdes +#define	DSP_TX_BUF_0	4	// serdes -> dsp tx (double buffer) +#define	DSP_TX_BUF_1	5	// serdes -> dsp tx + +/* + * ================================================================== + *   configure DSP TX double buffering state machine (serdes -> dsp) + * ================================================================== + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4) + +// Receive from serdes +buf_cmd_args_t dsp_tx_recv_args = { +  PORT_SERDES, +  0, +  BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { +  PORT_DSP, +  DSP_TX_FIRST_LINE,	// starts just past transport header +  0			// filled in from last_line register +}; + +dbsm_t dsp_tx_sm;	// the state machine + +/* + * ================================================================= + *   configure DSP RX double buffering state machine (dsp -> serdes) + * ================================================================= + */ + +// 4 lines of ethernet hdr + 1 line transport hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE ((sizeof(u2_eth_hdr_t) + sizeof(u2_transport_hdr_t))/4 + 1) + +// receive from DSP +buf_cmd_args_t dsp_rx_recv_args = { +  PORT_DSP, +  DSP_RX_FIRST_LINE, +  BP_LAST_LINE +}; + +// send to serdes +buf_cmd_args_t dsp_rx_send_args = { +  PORT_SERDES, +  0,		// starts with ethernet header in line 0 +  0,		// filled in from list_line register +}; + +dbsm_t dsp_rx_sm;	// the state machine + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +// variables for streaming mode + +static bool         streaming_p = false; +static unsigned int streaming_items_per_frame = 0; +static int          streaming_frame_count = 0; +#define FRAMES_PER_CMD	1000 + +bool is_streaming(void){ return streaming_p; } + +// ---------------------------------------------------------------- + + +void +restart_streaming(void) +{ +  // setup RX DSP regs +  dsp_rx_regs->clear_state = 1;			// reset + +  streaming_p = true; +  streaming_frame_count = FRAMES_PER_CMD; + +  dsp_rx_regs->rx_command = +    MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, +	      streaming_items_per_frame, +	      1, 1);			// set "chain" bit + +  // kick off the state machine +  dbsm_start(&dsp_rx_sm); + +  dsp_rx_regs->rx_time = 0;		// enqueue first of two commands + +  // make sure this one and the rest have the "now" and "chain" bits set. +  dsp_rx_regs->rx_command = +    MK_RX_CMD(FRAMES_PER_CMD * streaming_items_per_frame, +	      streaming_items_per_frame, +	      1, 1);				 + +  dsp_rx_regs->rx_time = 0;		// enqueue second command +} + +void +start_rx_streaming_cmd(const eth_mac_addr_t *host, op_start_rx_streaming_t *p) +{ +  host_mac_addr = *host;	// remember who we're sending to + +  /* +   * Construct  ethernet header and word0 and preload into two buffers +   */ +  u2_eth_packet_t	pkt; +  memset(&pkt, 0, sizeof(pkt)); +  pkt.ehdr.dst = *host; +  pkt.ehdr.src = *ethernet_mac_addr(); +  pkt.ehdr.ethertype = U2_ETHERTYPE; +  u2p_set_word0(&pkt.fixed, 0, 0); +  // DSP RX will fill in timestamp + +  memcpy_wa(buffer_ram(DSP_RX_BUF_0), &pkt, sizeof(pkt)); +  memcpy_wa(buffer_ram(DSP_RX_BUF_1), &pkt, sizeof(pkt)); + + +  if (FW_SETS_SEQNO) +    fw_seqno = 0; + +  streaming_items_per_frame = p->items_per_frame; +  restart_streaming(); +} + + +void +stop_rx_cmd(void) +{ +  streaming_p = false; +  dsp_rx_regs->clear_state = 1;	// flush cmd queue +  bp_clear_buf(DSP_RX_BUF_0); +  bp_clear_buf(DSP_RX_BUF_1); +} + + +static void +setup_tx() +{ +  dsp_tx_regs->clear_state = 1; +  bp_clear_buf(DSP_TX_BUF_0); +  bp_clear_buf(DSP_TX_BUF_1); + +  int tx_scale = 256; +  int interp = 32; + +  // setup some defaults + +  dsp_tx_regs->freq = 0; +  dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; +  dsp_tx_regs->interp_rate = interp; +} + + +#if (FW_SETS_SEQNO) +/* + * Debugging ONLY.  This will be handled by the tx_protocol_engine. + * + * This is called when the DSP Rx chain has filled in a packet. + * We set and increment the seqno, then return false, indicating + * that we didn't handle the packet.  A bit of a kludge + * but it should work. + */ +bool  +fw_sets_seqno_inspector(dbsm_t *sm, int buf_this)	// returns false +{ +  uint32_t *p = buffer_ram(buf_this); +  uint32_t seqno = fw_seqno++; + +  // KLUDGE all kinds of nasty magic numbers and embedded knowledge +  uint32_t t = p[4]; +  t = (t & 0xffff00ff) | ((seqno & 0xff) << 8); +  p[4] = t; + +  // queue up another rx command when required +  if (streaming_p && --streaming_frame_count == 0){ +    streaming_frame_count = FRAMES_PER_CMD; +    dsp_rx_regs->rx_time = 0; +  } + +  return false;		// we didn't handle the packet +} +#endif + + +inline static void +buffer_irq_handler(unsigned irq) +{ +  // hal_toggle_leds(LED_A); + +  uint32_t  status = buffer_pool_status->status; + +  if (0 && (status & ~BPS_IDLE_ALL)){ +    putstr("status = "); +    puthex32_nl(status); +  } + +  dbsm_process_status(&dsp_tx_sm, status); +  dbsm_process_status(&dsp_rx_sm, status); +} + +int +main(void) +{ +  u2_init(); + +  output_regs->led_src = 0x3;		// h/w controls bottom two bits +  clocks_enable_test_clk(true, 1); + +  putstr("\nSERDES TxRx\n"); + +  cpu_tx_buf_dest_port = PORT_SERDES; + +  // ethernet_register_link_changed_callback(link_changed_callback); +  // ethernet_init(); + +  clocks_mimo_config(MC_WE_LOCK_TO_MIMO); + +  // puts("post clocks_mimo_config"); + +#if 0 +  // make bit 15 of Tx gpio's be a s/w output +  hal_gpio_set_sel(GPIO_TX_BANK, 15, 's'); +  hal_gpio_set_ddr(GPIO_TX_BANK, 0x8000, 0x8000); +#endif + +#if 0 +  output_regs->debug_mux_ctrl = 1; +  hal_gpio_set_sels(GPIO_TX_BANK, "0000000000000000"); +  hal_gpio_set_sels(GPIO_RX_BANK, "0000000000000000"); +  hal_gpio_set_ddr(GPIO_TX_BANK, 0xffff, 0xffff); +  hal_gpio_set_ddr(GPIO_RX_BANK, 0xffff, 0xffff); +#endif + + +  // initialize double buffering state machine for ethernet -> DSP Tx + +  dbsm_init(&dsp_tx_sm, DSP_TX_BUF_0, +	    &dsp_tx_recv_args, &dsp_tx_send_args, +	    eth_pkt_inspector); + + +  //output_regs->flush_icache = 1; +  +  // initialize double buffering state machine for DSP RX -> Ethernet + +  if (FW_SETS_SEQNO){ +    dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	      &dsp_rx_recv_args, &dsp_rx_send_args, +	      fw_sets_seqno_inspector); +  } +  else { +    dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	      &dsp_rx_recv_args, &dsp_rx_send_args, +	      dbsm_nop_inspector); +  } + +  // puts("post dbsm_init's"); + +  // tell app_common that this dbsm could be sending to the ethernet +  ac_could_be_sending_to_eth = &dsp_rx_sm; + + +  // program tx registers +  setup_tx(); + +  // puts("post setup_tx"); + +  // kick off the state machine +  dbsm_start(&dsp_tx_sm); + +  // puts("post dbsm_start"); + +  //int which = 0; + +  while(1){ +    // hal_gpio_write(GPIO_TX_BANK, which, 0x8000); +    // which ^= 0x8000; + +    buffer_irq_handler(0); + +    int pending = pic_regs->pending;		// poll for under or overrun + +    if (pending & PIC_UNDERRUN_INT){ +      dbsm_handle_tx_underrun(&dsp_tx_sm); +      pic_regs->pending = PIC_UNDERRUN_INT;	// clear interrupt +      putchar('U'); +    } + +    if (pending & PIC_OVERRUN_INT){ +      dbsm_handle_rx_overrun(&dsp_rx_sm); +      pic_regs->pending = PIC_OVERRUN_INT;	// clear pending interrupt + +      // FIXME Figure out how to handle this robustly. +      // Any buffers that are emptying should be allowed to drain... + +      if (streaming_p){ +	// restart_streaming(); +	// FIXME report error +      } +      else { +	// FIXME report error +      } +      putchar('O'); +    } +  } +} diff --git a/firmware/microblaze/apps/cruft/set_hw_rev.c b/firmware/microblaze/apps/cruft/set_hw_rev.c new file mode 100644 index 000000000..d4ac8ff81 --- /dev/null +++ b/firmware/microblaze/apps/cruft/set_hw_rev.c @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#include <u2_init.h> +#include <nonstdio.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> + +#define HW_REV_MAJOR 0 +#define HW_REV_MINOR 3 + +int +main(void) +{ +  u2_init(); + +  putstr("\nset_hw_rev\n"); + +  bool ok = true; +  unsigned char maj = HW_REV_MAJOR; +  unsigned char min = HW_REV_MINOR; +  ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_MSB, &maj, 1); +  ok &= eeprom_write(I2C_ADDR_MBOARD, MBOARD_REV_LSB, &min, 1); + +  if (ok) +    printf("OK: set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); +  else +    printf("FAILED to set h/w rev to %d.%d\n", HW_REV_MAJOR, HW_REV_MINOR); + +  return 0; +} diff --git a/firmware/microblaze/apps/cruft/test1.c b/firmware/microblaze/apps/cruft/test1.c new file mode 100644 index 000000000..c3cc3be56 --- /dev/null +++ b/firmware/microblaze/apps/cruft/test1.c @@ -0,0 +1,282 @@ +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include "nonstdio.h" + +// Globals +#define EMPTY 0 +#define FILLING 1 +#define FULL 2 +#define EMPTYING 3 + +#define PORT 2    // ethernet = 2, serdes = 0 +int dsp_rx_buf, dsp_tx_buf, serdes_rx_buf, serdes_tx_buf; +int dsp_rx_idle, dsp_tx_idle, serdes_rx_idle, serdes_tx_idle; + +int buffer_state[4]; + + +void double_buffering(int port); + +// +// We register this in the secondary interrupt vector. +// It's called on buffer manager interrupts +// +void +buffer_irq_handler(unsigned irq) +{ +  double_buffering(PORT); +} + +int +main(void) +{ +  int i; + +  u2_init(); + +  // Control LEDs +  output_regs->leds = 0x02; + +  // Turn on ADCs +  output_regs->adc_ctrl = 0x0A; + +  // Set up TX Chain +  dsp_tx_regs->freq = 0; +  dsp_tx_regs->scale_iq = (1 << 16) | 1; +  dsp_tx_regs->interp_rate = 8; + +  // Set up RX Chain +  dsp_rx_regs->freq = 0; +  dsp_rx_regs->scale_iq = (1 << 16) | 1; +  dsp_rx_regs->decim_rate = 8; + +  // Set up buffer control, using only 4 for now +  for(i=0;i<4;i++)  +    buffer_state[i] = EMPTY; + +  // Set up DSP RX +  buffer_state[0] = FILLING; +  serdes_tx_idle = 1; +  bp_receive_to_buf(0, 1, 1, 10, 509);  // DSP_RX to buffer 0, use 500 lines + +  //dsp_rx_regs->run_rx = 1;           // Start DSP_RX +  putstr("Done DSP RX setup\n"); + +  // Set up serdes RX +  buffer_state[2] = FILLING; +  dsp_tx_idle = 1; +  bp_receive_to_buf(2, PORT, 1, 5, 504); + +  while (buffer_pool_status->status == 0)  // wait for completion of DSP RX +    ; + +  putstr("Done DSP TX setup\n"); +  //dsp_tx_regs->run_tx = 1; + +  // register interrupt handler +  pic_register_handler(IRQ_BUFFER, buffer_irq_handler); + +  while (1) +    ; + +  hal_finish(); +  return 1; +} + +void  +double_buffering(int port) { +  unsigned int localstatus = buffer_pool_status->status; + +  if(localstatus & BPS_DONE_0) { +    bp_clear_buf(0); +    if(buffer_state[0] == FILLING) { +      buffer_state[0] = FULL; +      if(buffer_state[1] == EMPTY) { +	bp_receive_to_buf(1, 1, 1, 10, 509);  // DSP_RX to buffer 1, use 500 lines +	buffer_state[1] = FILLING; +      } +      else +	dsp_rx_idle = 1; +      if(serdes_tx_idle) { +	serdes_tx_idle = 0; +	bp_send_from_buf(0, port, 1, 10, 509);  // SERDES_TX from buffer 0 +	buffer_state[0] = EMPTYING; +      } +    } +    else {  // buffer was emptying +      buffer_state[0] = EMPTY; +      if(dsp_rx_idle) { +	dsp_rx_idle = 0; +	bp_receive_to_buf(0, 1, 1, 10, 509);  // DSP_RX to buffer 0, use 500 lines +	buffer_state[0] = FILLING; +      } +      if(buffer_state[1] == FULL) { +	bp_send_from_buf(1, port, 1, 10, 509);  // SERDES_TX from buffer 1 +	buffer_state[1] = EMPTYING; +      } +      else +	serdes_tx_idle = 1; +    } +    putstr("Int Proc'ed 0\n"); +  } +  if(localstatus & BPS_DONE_1) { +    bp_clear_buf(1); +    if(buffer_state[1] == FILLING) { +      buffer_state[1] = FULL; +      if(buffer_state[0] == EMPTY) { +	bp_receive_to_buf(0, 1, 1, 10, 509);  // DSP_RX to buffer 1, use 500 lines +	buffer_state[0] = FILLING; +      } +      else +	dsp_rx_idle = 1; +      if(serdes_tx_idle) { +	serdes_tx_idle = 0; +	bp_send_from_buf(1, port, 1, 10, 509);  // SERDES_TX from buffer 1 +	buffer_state[1] = EMPTYING; +      } +    } +    else {  // buffer was emptying +      buffer_state[1] = EMPTY; +      if(dsp_rx_idle) { +	dsp_rx_idle = 0; +	bp_receive_to_buf(1, 1, 1, 10, 509);  // DSP_RX to buffer 1, use 500 lines +	buffer_state[1] = FILLING; +      } +      if(buffer_state[0] == FULL) { +	bp_send_from_buf(0, port, 1, 10, 509);  // SERDES_TX from buffer 0 +	buffer_state[0] = EMPTYING; +      } +      else +	serdes_tx_idle = 1; +    } +  putstr("Int Proc'ed 1\n"); +  } +  if(localstatus & BPS_DONE_2) { +    bp_clear_buf(2); +    if(buffer_state[2] == FILLING) { +      buffer_state[2] = FULL; +      if(buffer_state[3] == EMPTY) { +	bp_receive_to_buf(3, port, 1, 5, 504);  // SERDES_RX to buffer 3, use 500 lines +	buffer_state[3] = FILLING; +      } +      else +	serdes_rx_idle = 1; +      if(dsp_tx_idle) { +	dsp_tx_idle = 0; +	bp_send_from_buf(2, 1, 1, 5, 504);  // DSP_TX from buffer 2 +	buffer_state[2] = EMPTYING; +      } +    } +    else {  // buffer was emptying +      buffer_state[2] = EMPTY; +      if(serdes_rx_idle) { +	serdes_rx_idle = 0; +	bp_receive_to_buf(2, port, 1, 5, 504);  // SERDES_RX to buffer 2 +	buffer_state[2] = FILLING; +      } +      if(buffer_state[3] == FULL) { +	bp_send_from_buf(3, 1, 1, 5, 504);  // DSP_TX from buffer 3 +	buffer_state[3] = EMPTYING; +      } +      else +	dsp_tx_idle = 1; +    } +  putstr("Int Proc'ed 2\n"); +  } +  if(localstatus & BPS_DONE_3) { +    bp_clear_buf(3); +    if(buffer_state[3] == FILLING) { +      buffer_state[3] = FULL; +      if(buffer_state[2] == EMPTY) { +	bp_receive_to_buf(2, port, 1, 5, 504);  // SERDES_RX to buffer 2, use 500 lines +	buffer_state[2] = FILLING; +      } +      else +	serdes_rx_idle = 1; +      if(dsp_tx_idle) { +	dsp_tx_idle = 0; +	bp_send_from_buf(3, 1, 1, 5, 504);  // DSP_TX from buffer 3 +	buffer_state[3] = EMPTYING; +      } +    } +    else {  // buffer was emptying +      buffer_state[3] = EMPTY; +      if(serdes_rx_idle) { +	serdes_rx_idle = 0; +	bp_receive_to_buf(3, port, 1, 5, 504);  // SERDES_RX to buffer 3 +	buffer_state[3] = FILLING; +      } +      if(buffer_state[2] == FULL) { +	bp_send_from_buf(2, 1, 1, 5, 504);  // DSP_TX from buffer 2 +	buffer_state[2] = EMPTYING; +      } +      else +	dsp_tx_idle = 1; +    } +  putstr("Int Proc'ed 3\n"); +  } +} + +// Spare Code + +#if 0   +  // Set up LSDAC +  int i = 0; +  while(1) { +    int command = (3 << 19) | (0 << 16) |  (i & 0xffff); +    spi_transact(SPI_TXONLY, SPI_SS_TX_DAC, command, 24, 1); // negate TX phase +    i++; +  } +#endif + +#if 0   +  // Write to buffer 0 +  int *buf = (int *)(BUFFER_BASE + BUFFER_0); +  puthex_nl((int)buf); + +  for(i=0;i<BUFFER_SIZE;i++) +    buf[i] = i; + +  putstr("Filled buffer 0\n"); + +  // Write to buffer 1 +  buf = (int *)(BUFFER_BASE + BUFFER_1); +  puthex_nl((int)buf); +  for(i=0;i<BUFFER_SIZE;i++) +    buf[i] =  i + ((i^0xFFFF) << 16); + +  putstr("Filled buffer 1\n"); + +#endif + +#if 0 +  // rx SERDES into buffer #2  (buf,port,step,fl,ll) +  bp_receive_to_buf(2, 0, 1, 10, 300); +  putstr("SERDES RX buffer setup\n"); + +  // send SERDES from buffer #0 (buf,port,step,fl,ll) +  bp_send_from_buf(0, 0, 1, 20, 200); +  putstr("SERDES TX buffer setup\n"); + +#endif + +#if 0 +  // send to DACs from buffer #1 +  bp_send_from_buf(1 /*buf#*/, 1 /*port*/, 1 /*step*/, 20 /*fl*/, 250 /*ll*/); +  putstr("DAC Buffer setup\n"); +#endif + +#if 0 +  //putstr("ENTER INT\n"); +  for(i=0;i<8;i++) +    if(*status & (1<<i)) { +      //putstr("Clearing buf "); +      puthex_nl(i); +      bp_clear_buf(i); +    } +  //putstr("EXIT INT\n"); +#endif diff --git a/firmware/microblaze/apps/cruft/test_db_spi.c b/firmware/microblaze/apps/cruft/test_db_spi.c new file mode 100644 index 000000000..f4fa98ef1 --- /dev/null +++ b/firmware/microblaze/apps/cruft/test_db_spi.c @@ -0,0 +1,35 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <u2_init.h> +#include <nonstdio.h> +#include <hal_io.h> +#include <spi.h> + +int +main(void) +{ +  u2_init(); + +  puts("\ntest_db_spi"); + +  while(1){ +    spi_transact(SPI_TXONLY, SPI_SS_RX_DB, 0xCC33, 16, SPIF_PUSH_FALL); +    spi_transact(SPI_TXONLY, SPI_SS_TX_DB, 0x33CC, 16, SPIF_PUSH_FALL); +  } +} diff --git a/firmware/microblaze/apps/cruft/test_i2c.c b/firmware/microblaze/apps/cruft/test_i2c.c new file mode 100644 index 000000000..f349ead88 --- /dev/null +++ b/firmware/microblaze/apps/cruft/test_i2c.c @@ -0,0 +1,108 @@ +/* + * 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/>. + */ + +#include <stdio.h> +#include <stdint.h> +#include <u2_init.h>		/* FIXME */ +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <string.h> +#include <hal_io.h> + + + +#define ASSERT_TRUE(x) \ +  do { \ +    if (!(x)){ \ +      printf("ASSERT_TRUE failed on line %d\n", __LINE__); \ +      nerrors++; \ +    } \ +  } while(0) + +#define ASSERT_FALSE(x) \ +  do { \ +    if (x){ \ +      printf("ASSERT_FALSE failed on line %d\n", __LINE__); \ +      nerrors++; \ +    } \ +  } while(0) + + +#define BUFSIZE 128 + +int +main(void) +{ +  int i; +  bool ok; +  int  nerrors = 0; +  uint8_t buf[BUFSIZE]; +  int not_dev_addr = 0x35;	// no device with this address on the i2c bus. +  int offset; +  int len; +   +  u2_init(); + +  puts("test_i2c\n"); + +  // try writing a non-existent device +  buf[0] = 0xA5; +  ok = i2c_write(not_dev_addr, buf, 1); +  ASSERT_FALSE(ok); + +  // try read from non-existent device +  buf[0] = 0; +  ok = i2c_read(not_dev_addr, buf, 1); +  ASSERT_FALSE(ok); + +  // try writing eeprom +  offset = 31; +  len = 8; +  memset(buf, 0, sizeof(buf)); +  for (i = 0; i < len; i++) +    buf[i] = i; +  ok = eeprom_write(I2C_ADDR_MBOARD, offset, buf, len); +  ASSERT_TRUE(ok); + +  // now try to read it back +  offset = 31; +  len = 8; +  memset(buf, 0, sizeof(buf)); +  ok = eeprom_read(I2C_ADDR_MBOARD, offset, buf, len); +  ASSERT_TRUE(ok); + +  // check result +  for (i = 0; i < len; i++){ +    if (buf[i] != i){ +      printf("buf[%d] = %d, should be %d\n", i, buf[i], i); +      nerrors++; +    } +  } +   +  if (nerrors == 0){ +    output_regs->leds = 0x3; +    puts("PASSED\n"); +  } +  else { +    output_regs->leds = 0x0; +    puts("FAILED\n"); +  } + +  hal_finish(); +  return 0; +} + diff --git a/firmware/microblaze/apps/cruft/test_lsadc.c b/firmware/microblaze/apps/cruft/test_lsadc.c new file mode 100644 index 000000000..5fda29cd7 --- /dev/null +++ b/firmware/microblaze/apps/cruft/test_lsadc.c @@ -0,0 +1,57 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <lsadc.h> +#include <lsdac.h> +#include <u2_init.h> +#include <nonstdio.h> +#include <hal_io.h> + +int +main(void) +{ +  u2_init(); + +  puts("\ntest_lsadc"); + +  uint32_t r; + +  unsigned int up_counter = 0; +   +  while (1){ +    unsigned int v; +    v = up_counter; + +    lsdac_write_rx(0, v << 0); +    lsdac_write_rx(2, v << 1); + +#if 1 +    r = lsadc_read_rx(0); +    lsdac_write_rx(1, r & 0x0fff); +    //puthex32_nl(r); +#endif + +#if 1 +    r = lsadc_read_rx(1); +    lsdac_write_rx(3, r & 0x0fff); +    //puthex32_nl(r); +#endif + +    up_counter++; +  } +} diff --git a/firmware/microblaze/apps/cruft/test_lsdac.c b/firmware/microblaze/apps/cruft/test_lsdac.c new file mode 100644 index 000000000..8c1bf333b --- /dev/null +++ b/firmware/microblaze/apps/cruft/test_lsdac.c @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <lsdac.h> +#include <u2_init.h> +#include <nonstdio.h> +#include <hal_io.h> + +int +main(void) +{ +  u2_init(); + +  puts("\ntest_lsdac"); + +  unsigned int up_counter = 0; +  unsigned int dn_counter = 0; + +  while(1){ +    unsigned int v; +    v = up_counter; +    lsdac_write_rx(0, v << 0); +    lsdac_write_rx(1, v << 1); +    lsdac_write_rx(2, v << 2); +    lsdac_write_rx(3, v << 3); + +    v = up_counter; +    lsdac_write_tx(0, v << 0); +    lsdac_write_tx(1, v << 1); +    lsdac_write_tx(2, v << 2); +    lsdac_write_tx(3, v << 3); + +    up_counter++; +    dn_counter--; +  } +} diff --git a/firmware/microblaze/apps/cruft/test_phy_comm.c b/firmware/microblaze/apps/cruft/test_phy_comm.c new file mode 100644 index 000000000..d312ca4cc --- /dev/null +++ b/firmware/microblaze/apps/cruft/test_phy_comm.c @@ -0,0 +1,113 @@ +/* + * 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/>. + */ + +// check communication with ethernet PHY chip + +#include "u2_init.h" +#include "memory_map.h" +#include "hal_io.h" +#include "ethernet.h" +#include "pic.h" +#include "nonstdio.h" + + +#define DELTA_T     12500000  		// .125s (10ns per tick) +//#define DELTA_T      10000 + +// debugging output on tx pins +#define LS_MASK  0xE0000 +#define LS_1000  0x80000 +#define LS_100   0x40000 +#define LS_10    0x20000 + + + +#define U2_ETHERTYPE 0xBEEF + + +static volatile int led_link_up_flag = 0; + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ +  int v = 0; +  switch(speed){ +  case 10: +    v = LS_10; +    led_link_up_flag = 0x2; +    break; +     +  case 100: +    v = LS_100; +    led_link_up_flag = 0x2; +    break; +     +  case 1000: +    v = LS_100; +    led_link_up_flag = 0x2; +    break; + +  default: +    v = 0; +    led_link_up_flag = 0; +    break; +  } + +  //hal_gpio_set_tx(v, LS_MASK);	/* set debug bits on d'board */ + +  putstr("\neth link changed: speed = "); +  puthex_nl(speed); +} + +void +timer_handler(unsigned irq) +{ +  static int led_counter = 0; + +  hal_set_timeout(DELTA_T);	// schedule next timeout +  output_regs->leds = (led_counter++ & 0x1) | led_link_up_flag; +} + +int +main(void) +{ +  u2_init(); + +  putstr("\n test_phy_comm\n"); + +  pic_register_handler(IRQ_ONETIME, timer_handler); +  hal_set_timeout(DELTA_T);	// schedule timeout + +  // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output +  //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); +  //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + +  ethernet_register_link_changed_callback(link_changed_callback); + +  output_regs->phy_ctrl = 1;	/* reset the eth PHY */ +  output_regs->phy_ctrl = 0; + +  ethernet_init(); + +  while(1) +    ; + +  return 0; +} diff --git a/firmware/microblaze/apps/cruft/test_ram.c b/firmware/microblaze/apps/cruft/test_ram.c new file mode 100644 index 000000000..77ee693f6 --- /dev/null +++ b/firmware/microblaze/apps/cruft/test_ram.c @@ -0,0 +1,105 @@ +/* + * 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/>. + */ + +#include <stdio.h> +#include <stdint.h> +#include <u2_init.h>		/* FIXME */ +#include <sd.h> +#include <string.h> +#include <hal_io.h> +#include <nonstdio.h> +#include <hal_uart.h> + +#define ASSERT_TRUE(x) \ +  do { \ +    if (!(x)){ \ +      printf("ASSERT_TRUE failed on line %d\n", __LINE__); \ +      nerrors++; \ +    } \ +  } while(0) + +#define ASSERT_FALSE(x) \ +  do { \ +    if (x){ \ +      printf("ASSERT_FALSE failed on line %d\n", __LINE__); \ +      nerrors++; \ +    } \ +  } while(0) + + +#define BUFSIZE 128 + +int test_ram() +{ +  int i,j,k; +  output_regs->ram_page = 1<<10; +   +  extram[0] = 0xDEADBEEF; +  extram[1] = 0xF00D1234; +  extram[7] = 0x76543210; +   +  output_regs->ram_page = 2<<10; +  extram[7] = 0x55555555; +  extram[1] = 0xaaaaaaaa; +  extram[0] = 0xeeeeeeee; +   +  output_regs->ram_page = 1<<10; +   +  i = extram[0]; +  k = extram[1]; +  j = extram[7]; +   +  if((i != 0xDEADBEEF)||(j!=0x76543210)||(k!=0xF00D1234)) { +    puts("RAM FAIL1!\n"); +    puthex32_nl(i); +    puthex32_nl(j); +    puthex32_nl(k); +    return 0; +  } +   +  output_regs->ram_page = 2<<10; + +  j = extram[7]; +  k = extram[1]; +  i = extram[0]; + +  if((i != 0xeeeeeeee)||(j!=0x55555555)||(k!=0xaaaaaaaa)) { +    puts("RAM FAIL2!\n"); +    puthex32_nl(i); +    puthex32_nl(j); +    puthex32_nl(k); +    return 0; +  } +  return 1; +} + +int +main(void) +{ + +  u2_init(); +  puts("\ntest_ram\n"); +  int success = test_ram(); +  if(success) +    puts("RAM Passed Tests\n"); +  else +    puts("RAM Failed\n"); + +  hal_finish(); +  return 0; +} + diff --git a/firmware/microblaze/apps/cruft/test_sd.c b/firmware/microblaze/apps/cruft/test_sd.c new file mode 100644 index 000000000..494432d7f --- /dev/null +++ b/firmware/microblaze/apps/cruft/test_sd.c @@ -0,0 +1,81 @@ +/* + * 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/>. + */ + +#include <stdio.h> +#include <stdint.h> +#include <u2_init.h>		/* FIXME */ +#include <sd.h> +#include <string.h> +#include <hal_io.h> +#include <nonstdio.h> + + +#define ASSERT_TRUE(x) \ +  do { \ +    if (!(x)){ \ +      printf("ASSERT_TRUE failed on line %d\n", __LINE__); \ +      nerrors++; \ +    } \ +  } while(0) + +#define ASSERT_FALSE(x) \ +  do { \ +    if (x){ \ +      printf("ASSERT_FALSE failed on line %d\n", __LINE__); \ +      nerrors++; \ +    } \ +  } while(0) + + +#define BUFSIZE 128 + +int +main(void) +{ +  int i; +  unsigned char buf[512]; + +  u2_init(); + +  puts("\ntest_sd\n"); +   + +  i = sd_init(); +  if(i) +    puts("Successfully Init'ed Card\n"); +  else +    puts("FAILED INIT of Card\n"); + +  i = sd_read_block(2048,buf); +  if(i) { +    puts("READ Command accepted\n"); +    for(i=0;i<512;i++) +      if((i&15) == 15) +	puthex8_nl(buf[i]); +      else { +	puthex8(buf[i]); +	putchar(' '); +      } +  } +  else +    puts("READ Command Rejected\n"); +   +  puts("Done"); +  hal_finish(); +  return 0; +} + diff --git a/firmware/microblaze/apps/cruft/timer_test.c b/firmware/microblaze/apps/cruft/timer_test.c new file mode 100644 index 000000000..44e80b5f1 --- /dev/null +++ b/firmware/microblaze/apps/cruft/timer_test.c @@ -0,0 +1,57 @@ +/* + * 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/>. + */ + +#include "u2_init.h" +#include "memory_map.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include "nonstdio.h" +#include "hal_io.h" + +#define DELTA_T  500  		// 5 us (10ns per tick) + + +void  +timer_handler(unsigned irq) +{ +  hal_set_timeout(DELTA_T); + +  putstr("Tick: "); +  puthex_nl(0); +} + +int +main(void) +{ +  u2_init(); + +  // setup timer + +  putstr("Setting up timer\n"); +  pic_register_handler(IRQ_ONETIME, timer_handler); + +  hal_set_timeout(DELTA_T); + +  while (1) +    ; + +  putstr("Done Testing\n"); +   +  hal_finish(); +  return 1; +} diff --git a/firmware/microblaze/apps/cruft/tx_standalone.c b/firmware/microblaze/apps/cruft/tx_standalone.c new file mode 100644 index 000000000..1645fa8ba --- /dev/null +++ b/firmware/microblaze/apps/cruft/tx_standalone.c @@ -0,0 +1,339 @@ +/* + * 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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "hal_io.h" +#include "buffer_pool.h" +#include "pic.h" +#include <stdbool.h> +#include "ethernet.h" +#include "nonstdio.h" +#include "usrp2_eth_packet.h" +#include "memcpy_wa.h" +#include "dbsm.h" +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#define	_AL4 __attribute__((aligned (4))) + +#define USE_BUFFER_INTERRUPT	0	// 0 or 1 + + +static int timer_delta = MASTER_CLK_RATE/1000;	// tick at 1kHz + +/* + * This program can respond to queries from the host + * and stream rx samples. + * + * Buffer 1 is used by the cpu to send frames to the host. + * Buffers 2 and 3 are used to double-buffer the DSP Rx to eth flow + * Buffers 4 and 5 are used to double-buffer the eth to DSP Tx  eth flow + */ +//#define CPU_RX_BUF	0	// eth -> cpu +#define CPU_TX_BUF 	1	// cpu -> eth + +#define	DSP_RX_BUF_0	2	// dsp rx -> eth (double buffer) +#define	DSP_RX_BUF_1	3	// dsp rx -> eth +#define	DSP_TX_BUF_0	4	// eth -> dsp tx (double buffer) +#define	DSP_TX_BUF_1	5	// eth -> dsp tx + + +/* + * ================================================================ + *      configure DSP RX double buffering state machine + * ================================================================ + */ + + +// 4 lines of ethernet hdr + 1 line (word0) +// DSP Rx writes timestamp followed by nlines_per_frame of samples +#define DSP_RX_FIRST_LINE		  5 +#define DSP_RX_SAMPLES_PER_FRAME	128 +#define	DSP_RX_EXTRA_LINES		  1	// writes timestamp + +// Receive from DSP Rx +buf_cmd_args_t dsp_rx_recv_args = { +  PORT_DSP, +  DSP_RX_FIRST_LINE, +  BP_LAST_LINE +}; + +// send to ethernet +buf_cmd_args_t dsp_rx_send_args = { +  PORT_ETH, +  0,		// starts with ethernet header in line 0 +  0,		// filled in from last_line register +}; + +dbsm_t dsp_rx_sm;	// the state machine + +/* + * ================================================================ + *      configure DSP TX double buffering state machine + * ================================================================ + */ + +// 4 lines of ethernet hdr + 2 lines (word0 + timestamp) +// DSP Tx reads word0 (flags) + timestamp followed by samples + +#define DSP_TX_FIRST_LINE		  4 +#define DSP_TX_SAMPLES_PER_FRAME	250	// not used except w/ debugging +#define	DSP_TX_EXTRA_LINES		  2	// reads word0 + timestamp + +// Receive from ethernet +buf_cmd_args_t dsp_tx_recv_args = { +  PORT_ETH, +  0, +  BP_LAST_LINE +}; + +// send to DSP Tx +buf_cmd_args_t dsp_tx_send_args = { +  PORT_DSP, +  DSP_TX_FIRST_LINE,	// starts just past ethernet header +  0			// filled in from last_line register +}; + +dbsm_t dsp_tx_sm;	// the state machine + +/* + * send constant buffer to DSP TX + */ +static inline void  +SEND_CONST_TO_DSP_TX(void) +{ +  bp_send_from_buf(DSP_TX_BUF_0, PORT_DSP, 1, +		   DSP_TX_FIRST_LINE, +		   DSP_TX_FIRST_LINE + DSP_TX_EXTRA_LINES + DSP_TX_SAMPLES_PER_FRAME - 1); +} + +// ---------------------------------------------------------------- + + + +// The mac address of the host we're sending to. +eth_mac_addr_t host_mac_addr; + + +void link_changed_callback(int speed); +static volatile bool link_is_up = false;	// eth handler sets this + + +void +timer_irq_handler(unsigned irq) +{ +  hal_set_timeout(timer_delta);	// schedule next timeout +} + +// Tx DSP underrun +void +underrun_irq_handler(unsigned irq) +{ +  dsp_tx_regs->clear_state = 1; +  bp_clear_buf(DSP_TX_BUF_0); +  bp_clear_buf(DSP_TX_BUF_1); +  dbsm_stop(&dsp_tx_sm); + +  // FIXME anything else? + +  putstr("\nirq: underrun\n"); +} + +// Rx DSP overrun +void +overrun_irq_handler(unsigned irq) +{ +  dsp_rx_regs->clear_state = 1; +  bp_clear_buf(DSP_RX_BUF_0); +  bp_clear_buf(DSP_RX_BUF_1); +  dbsm_stop(&dsp_rx_sm); + +  // FIXME anything else? + +  putstr("\nirq: overrun\n"); +} + +static void +start_tx_transfers(void) +{ +  bp_clear_buf(DSP_TX_BUF_0);	// FIXME, really goes in state machine +  bp_clear_buf(DSP_TX_BUF_1); + +  // fill everything with a constant 32k + 0j + +  uint32_t const_sample = (32000 << 16) | 0; +  int i; +  for (i = 0; i < BP_NLINES; i++){ +    buffer_ram(DSP_TX_BUF_0)[i] = const_sample; +    buffer_ram(DSP_TX_BUF_1)[i] = const_sample; +  } + +  /* +   * Construct  ethernet header and word0 and preload into two buffers +   */ +  u2_eth_packet_t	pkt; +  memset(&pkt, 0, sizeof(pkt)); +  //pkt.ehdr.dst = *host; +  pkt.ehdr.src = *ethernet_mac_addr(); +  pkt.ehdr.ethertype = U2_ETHERTYPE; +  u2p_set_word0(&pkt.fixed, +		U2P_TX_IMMEDIATE | U2P_TX_START_OF_BURST, 0); +  u2p_set_timestamp(&pkt.fixed, T_NOW); + +  memcpy_wa(buffer_ram(DSP_TX_BUF_0), &pkt, sizeof(pkt)); +  memcpy_wa(buffer_ram(DSP_TX_BUF_1), &pkt, sizeof(pkt)); + + +  int tx_scale = 256; + +  // setup Tx DSP regs +  dsp_tx_regs->clear_state = 1;			// reset +  dsp_tx_regs->freq = 408021893;		// 9.5 MHz [2**32 * fc/fsample] +  dsp_tx_regs->scale_iq = (tx_scale << 16) | tx_scale; +  dsp_tx_regs->interp_rate = 32; + +  // kick off the state machine +  // dbsm_start(&dsp_rx_sm); + +  SEND_CONST_TO_DSP_TX();	// send constant buffer to DSP TX +} + + +void +buffer_irq_handler(unsigned irq) +{ +  uint32_t  status = buffer_pool_status->status; + +  if (0){ +    putstr("irq: "); +    puthex32(status); +    putchar('\n'); +  } + +  if (status & BPS_ERROR_ALL){ +    // FIXME rare path, handle error conditions +  } + +  if (status & BPS_DONE(DSP_TX_BUF_0)){ +    bp_clear_buf(DSP_TX_BUF_0); +    SEND_CONST_TO_DSP_TX(); +    hal_toggle_leds(0x1); +  } + +} + +int +main(void) +{ +  u2_init(); + +  // setup tx gpio bits for GPIOM_FPGA_1 -- fpga debug output +  //hal_gpio_set_sels(GPIO_TX_BANK, "1111111111111111"); +  //hal_gpio_set_sels(GPIO_RX_BANK, "1111111111111111"); + +  putstr("\ntx_only\n"); +   +  // Control LEDs +  hal_set_leds(0x0, 0x3); + +  if (USE_BUFFER_INTERRUPT) +    pic_register_handler(IRQ_BUFFER,   buffer_irq_handler); + +  pic_register_handler(IRQ_OVERRUN,  overrun_irq_handler); +  pic_register_handler(IRQ_UNDERRUN, underrun_irq_handler); + +  //pic_register_handler(IRQ_TIMER, timer_irq_handler); +  //hal_set_timeout(timer_delta); + +  ethernet_register_link_changed_callback(link_changed_callback); + +  ethernet_init(); + +  // initialize double buffering state machine for DSP RX -> Ethernet +  dbsm_init(&dsp_rx_sm, DSP_RX_BUF_0, +	    &dsp_rx_recv_args, &dsp_rx_send_args, +	    dbsm_nop_inspector); + +  // setup receive from ETH +  // bp_receive_to_buf(CPU_RX_BUF, PORT_ETH, 1, 0, BP_LAST_LINE); + +#if 0 +  if (hwconfig_simulation_p()){ +    // If we're simulating, pretend that we got a start command from the host +    eth_mac_addr_t host = {{ 0x00, 0x0A, 0xE4, 0x3E, 0xD2, 0xD5 }}; +    start_rx_cmd(&host); +  } +#endif + +  start_tx_transfers();		// send constant buffers to DSP TX + +  while(1){ +    if (!USE_BUFFER_INTERRUPT) +      buffer_irq_handler(0); +  } +} + +// ---------------------------------------------------------------- + +// debugging output on tx pins +#define LS_MASK  0xE0000 +#define LS_1000  0x80000 +#define LS_100   0x40000 +#define LS_10    0x20000 + +/* + * Called when eth phy state changes (w/ interrupts disabled) + */ +void +link_changed_callback(int speed) +{ +  int v = 0; +  switch(speed){ +  case 10: +    v = LS_10; +    link_is_up = true; +    break; +     +  case 100: +    v = LS_100; +    link_is_up = true; +    break; +     +  case 1000: +    v = LS_100; +    link_is_up = true; +    break; + +  default: +    v = 0; +    link_is_up = false; +    break; +  } + +  //hal_gpio_set_tx(v, LS_MASK);	/* set debug bits on d'board */ + +  // hal_set_leds(link_is_up ? 0x2 : 0x0, 0x2); + +  printf("\neth link changed: speed = %d\n", speed); +} diff --git a/firmware/microblaze/apps/flash_test.c b/firmware/microblaze/apps/flash_test.c new file mode 100644 index 000000000..5b4569030 --- /dev/null +++ b/firmware/microblaze/apps/flash_test.c @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <memory_map.h> +#include <hal_io.h> +#include <hal_uart.h> +#include <xilinx_s3_icap.h> +#include <nonstdio.h> +#include <spi_flash.h> +#include <spi.h> +#include <clocks.h> +#include <string.h> + +//just a test to write to SPI flash and retrieve the same values. +//uses the MOBFLEET SPI flash library + +void delay(uint32_t t) { +	while(t-- != 0) asm("NOP"); +} + +int main(int argc, char *argv[]) { +	uint16_t i, t; +	uint8_t buf[260]; +	const uint8_t testdata[] = {0xDE, 0xAD, 0xBE, 0xEF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}; + +  hal_disable_ints();	// In case we got here via jmp 0x0 +//	spi_init(); +  hal_uart_init(); +//	clocks_init(); //set up AD9510, enable FPGA clock @ 1x divisor + +	puts("SPI Flash test\n"); +	puts("Initializing SPI\n"); + +	spif_init(); +	delay(800000); +	puts("Erasing sector 1\n"); +	spi_flash_erase(0x00010000, 256); +	delay(800000); +	puts("Reading back data\n"); +	spi_flash_read(0x00010000, 256, buf); +	delay(800000); + +	t=1; +	for(i=4; i<250; i++) { +		if(buf[i] != 0xFF) t=0; +	} + +	if(!t) puts("Data was not initialized to 0xFF. Unsuccessful erase or read\n"); +	else puts("Data initialized to 0xFF, erase confirmed\n"); + +	puts("Writing test buffer\n"); +	spi_flash_program(0x00010000, 16, testdata); +	//memset(buf, 0, 256); + +	delay(800000); +	puts("Wrote data, reading back\n"); + +	spi_flash_read(0x00010000, 16, buf); + +	if(memcmp(testdata, buf, 16)) puts("Data is not the same between read and write. Unsuccessful write or read\n"); +	else puts("Successful write! Flash write correct\n"); + +	return 0; +} diff --git a/firmware/microblaze/apps/hardware_testbed.c b/firmware/microblaze/apps/hardware_testbed.c new file mode 100644 index 000000000..e68e68ff7 --- /dev/null +++ b/firmware/microblaze/apps/hardware_testbed.c @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <memory_map.h> +#include <nonstdio.h> +#include <hal_io.h> +#include <xilinx_s3_icap.h> +#include <spi_flash.h> +//#include <spi_flash_private.h> +#include <clocks.h> +#include <ihex.h> +#include <bootloader_utils.h> +#include <string.h> +#include <hal_uart.h> +#include <spi.h> + +//so this is just an evolving file used to set up and test different bits of hardware (EEPROM, clock chip, A/D, D/A, PHY) +void delay(uint32_t t) { +	while(t-- != 0) asm("NOP"); +} + +int main(int argc, char *argv[]) { + +	hal_disable_ints(); +  hal_uart_init(); +	spi_init(); + +	puts("Hardware testbed. Init clocks..."); + +	clocks_init(); + +	//now, hopefully, we should be running at 100MHz instead of 50MHz, meaning our UART is twice as fast and we're talking at 230400. + +	while(1) { +		delay(500000); +		puts("Eat at Joe's."); +	} + + + + + +	return 0; +} diff --git a/firmware/microblaze/apps/txrx_uhd.c b/firmware/microblaze/apps/txrx_uhd.c index f7f140121..4e03bc869 100644 --- a/firmware/microblaze/apps/txrx_uhd.c +++ b/firmware/microblaze/apps/txrx_uhd.c @@ -41,7 +41,6 @@  #include <stdlib.h>  #include <string.h>  #include "clocks.h" -#include <vrt/bits.h>  #include "usrp2/fw_common.h"  #include <i2c.h>  #include <ethertype.h> @@ -266,7 +265,7 @@ void handle_udp_ctrl_packet(       * Peek and Poke Register       ******************************************************************/      case USRP2_CTRL_ID_POKE_THIS_REGISTER_FOR_ME_BRO: -        if (ctrl_data_in->data.poke_args.addr < 0xC000){ +        if (0){//ctrl_data_in->data.poke_args.addr < 0xC000){              printf("error! tried to poke into 0x%x\n", ctrl_data_in->data.poke_args.addr);          }          else switch(ctrl_data_in->data.poke_args.num_bytes){ 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 2343025cc..26987b0ec 100755 --- a/firmware/microblaze/bootstrap +++ b/firmware/microblaze/bootstrap @@ -17,6 +17,8 @@  #  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 46968b7fb..f6986f2dd 100644 --- a/firmware/microblaze/configure.ac +++ b/firmware/microblaze/configure.ac @@ -32,6 +32,7 @@ m4_if(m4_defn([m4_PACKAGE_VERSION]), [2.64],       [m4_define([_AC_LANG_IO_PROGRAM(]_GCC_LANG[)], m4_defn([AC_LANG_PROGRAM(]_GCC_LANG[)]))])])   AC_PROG_CC([mb-gcc]) +AM_PROG_CC_C_O  LT_INIT  ################################################## @@ -46,9 +47,8 @@ AC_PATH_PROG([HEXDUMP],    [hexdump])  ##################################################  AC_CONFIG_FILES([ \      Makefile \ -    apps/Makefile \ -    include/Makefile \ -    include/net/Makefile \ -    lib/Makefile \ +    usrp2p/bootloader/Makefile \ +    usrp2/Makefile \ +    usrp2p/Makefile \  ])  AC_OUTPUT diff --git a/firmware/microblaze/lib/Makefile.inc b/firmware/microblaze/lib/Makefile.inc new file mode 100644 index 000000000..0e88298b8 --- /dev/null +++ b/firmware/microblaze/lib/Makefile.inc @@ -0,0 +1,48 @@ +# +# Copyright 2010 Ettus Research LLC +# +# 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/>. +# + +COMMON_SRCS = \ +	$(top_srcdir)/lib/u2_init.c \ +	$(top_srcdir)/lib/abort.c \ +	$(top_srcdir)/lib/ad9510.c \ +	$(top_srcdir)/lib/bsm12.c \ +	$(top_srcdir)/lib/buffer_pool.c \ +	$(top_srcdir)/lib/dbsm.c \ +	$(top_srcdir)/lib/eeprom.c \ +	$(top_srcdir)/lib/ethernet.c \ +	$(top_srcdir)/lib/eth_mac.c \ +	$(top_srcdir)/lib/_exit.c \ +	$(top_srcdir)/lib/exit.c \ +	$(top_srcdir)/lib/hal_io.c \ +	$(top_srcdir)/lib/hal_uart.c \ +	$(top_srcdir)/lib/i2c.c \ +	$(top_srcdir)/lib/mdelay.c \ +	$(top_srcdir)/lib/memcpy_wa.c \ +	$(top_srcdir)/lib/memset_wa.c \ +	$(top_srcdir)/lib/nonstdio.c \ +	$(top_srcdir)/lib/pic.c \ +	$(top_srcdir)/lib/print_mac_addr.c \ +	$(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 \ +	$(top_srcdir)/lib/banal.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/clock_bits.h b/firmware/microblaze/lib/clock_bits.h new file mode 100644 index 000000000..d2052e941 --- /dev/null +++ b/firmware/microblaze/lib/clock_bits.h @@ -0,0 +1,55 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* + * Copyright 2008 Free Software Foundation, Inc. + *  + * 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_USRP2_CLOCK_BITS_H +#define INCLUDED_USRP2_CLOCK_BITS_H + +#define	_MC_WE_LOCK			0x0001 +#define	_MC_MIMO_CLK_INPUT		0x0002		// else SMA input + +/* + * Derived masks (use these): + * + * We get our input from 1 of three places: + *  Our free running oscilator, our SMA connector, or from the MIMO connector + */ +#define	MC_WE_DONT_LOCK			0x0000 +#define	MC_WE_LOCK_TO_SMA		(_MC_WE_LOCK | 0) +#define	MC_WE_LOCK_TO_MIMO		(_MC_WE_LOCK | _MC_MIMO_CLK_INPUT) + +/* + * Independent of the source of the clock, we may or may not drive our + * clock onto the mimo connector.  Note that there are dedicated clock + * signals in each direction, so disaster doesn't occurs if we're + * unnecessarily providing clock. + */ +#define	MC_PROVIDE_CLK_TO_MIMO		0x0004 + +#define MC_REF_CLK_MASK          0x0f + +#define MC_PPS_SOURCE_SMA        (0x00 << 4) +#define MC_PPS_SOURCE_MIMO       (0x01 << 4) + +#define MC_PPS_POLARITY_NEG      (0x00 << 5) +#define MC_PPS_POLARITY_POS      (0x01 << 5) + +#endif /* INCLUDED_USRP2_CLOCK_BITS_H */ diff --git a/firmware/microblaze/lib/clocks.h b/firmware/microblaze/lib/clocks.h index 43d5a05c2..a285e03dd 100644 --- a/firmware/microblaze/lib/clocks.h +++ b/firmware/microblaze/lib/clocks.h @@ -26,7 +26,7 @@   */  #include <stdbool.h> -#include <usrp2_clock_bits.h> +#include "clock_bits.h"  /*! diff --git a/firmware/microblaze/lib/compiler.h b/firmware/microblaze/lib/compiler.h new file mode 100644 index 000000000..4fa9b49f8 --- /dev/null +++ b/firmware/microblaze/lib/compiler.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_COMPILER_H +#define INCLUDED_COMPILER_H + +// FIXME gcc specific. +#define _AL4   __attribute__((aligned (4))) + + +#endif /* INCLUDED_COMPILER_H */ diff --git a/firmware/microblaze/lib/ethernet.c b/firmware/microblaze/lib/ethernet.c index 34a3ad7c1..2d7c4d7bd 100644 --- a/firmware/microblaze/lib/ethernet.c +++ b/firmware/microblaze/lib/ethernet.c @@ -25,8 +25,7 @@  #include "nonstdio.h"  #include <stdbool.h>  #include "i2c.h" -#include "usrp2_i2c_addr.h" - +#include "usrp2/fw_common.h"  #define VERBOSE 0 @@ -302,7 +301,7 @@ ethernet_mac_addr(void)        return &src_mac_addr;      eth_mac_addr_t tmp; -    bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &tmp, sizeof(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      } @@ -316,7 +315,7 @@ ethernet_mac_addr(void)  bool  ethernet_set_mac_addr(const eth_mac_addr_t *t)  { -  bool ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, t, sizeof(eth_mac_addr_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; @@ -344,7 +343,7 @@ const struct ip_addr *get_ip_addr(void)        return &src_ip_addr;      struct ip_addr tmp; -    bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_IP_ADDR, &tmp, sizeof(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      } @@ -356,7 +355,7 @@ const struct ip_addr *get_ip_addr(void)  }  bool set_ip_addr(const struct ip_addr *t){ -  bool ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_IP_ADDR, t, sizeof(struct ip_addr)); +  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; diff --git a/firmware/microblaze/lib/gdbstub2.c b/firmware/microblaze/lib/gdbstub2.c new file mode 100644 index 000000000..4c63dfce2 --- /dev/null +++ b/firmware/microblaze/lib/gdbstub2.c @@ -0,0 +1,506 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Implement a eensy weensy part of the GDB Remote Serial Protocol + * + * See Appendix D of the GDB manual + * + *   m<addr>,<length> 		-- read <length> bytes of memory starting at <addr> + *     Reply: + *     XX...		XX... is memory contents in hex + *     ENN		ENN   NN is a hex error number + * + *   M<addr>,<length>:XX...     -- write memory, data in hex + *     Reply: + *     OK		for success + *     ENN		for an error.  NN is a hex error number + * + *   X<addr>,<length>:XX...     -- write memory, data in binary + *     Reply: + *     OK		for success + *     ENN		for an error.  NN is a hex error number + * + *   c<addr>			-- continue.  <addr> is the address to resume (goto). + *     Reply: <none> + * + *   \x80 New Format... + */ + +#include "gdbstub2.h" +#include "loader_parser.h" +#include "hal_uart.h" +#include <stdbool.h> +#include <stddef.h> + +#define MAX_PACKET	1024 + +/* + * Get raw character from serial port, no echo. + */ +static inline int  +gdb_getc(void) +{ +  return hal_uart_getc(); +} + +/* + * Put character to serial port.  Raw output. + */ +static inline void +gdb_putc(int ch) +{ +  hal_uart_putc(ch); +} + +// ------------------------------------------------------------------------ + +#define	GDB_ESCAPE 0x7d + +static unsigned char hex_table[16] = {  +  '0', '1', '2', '3', '4', '5', '6', '7', +  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +}; + +static int +put_hex8_checksum(int ch, int checksum) +{ +  unsigned char t = hex_table[(ch >> 4) & 0xf]; +  checksum += t; +  gdb_putc(t); + +  t = hex_table[ch & 0xf]; +  checksum += t; +  gdb_putc(t); +  return checksum; +} + +static void +put_hex8(int ch) +{ +  put_hex8_checksum(ch, 0); +} + +static bool +hex4_to_bin(int ch, int *value) +{ +  if ('0' <= ch && ch <= '9'){ +    *value = ch - '0'; +    return true; +  } +  if ('a' <= ch && ch <= 'f'){ +    *value = ch - 'a' + 10; +    return true; +  } +  if ('A' <= ch && ch <= 'F'){ +    *value = ch - 'A' + 10; +    return true; +  } +  *value = 0; +  return false; +} + +static bool +hex8_to_bin(const unsigned char *s, int *value) +{ +  int v0, v1; +  if (hex4_to_bin(s[0], &v0) && hex4_to_bin(s[1], &v1)){ +    *value = (v0 << 4) | v1; +    return true; +  } +  return false; +} + +static bool +hex_to_bin_array(unsigned char *binary_data, const unsigned char *hex_data, size_t nbytes) +{ +  for (size_t i = 0; i < nbytes; i++){ +    int t; +    if (!hex8_to_bin(&hex_data[2*i], &t)) +      return false; +    binary_data[i] = t; +  } +  return true; +} + +static bool +needs_escaping(int ch) +{ +  return ch == '$' || ch == '#' || ch == GDB_ESCAPE; +} + +/* + * \brief Wait for a packet.   + * \param[out] pkt_buf gets the received packet payload. + * \param[in]  max_size is the maximum number of bytes to write into \p pkt_buf. + * \param[out] actual_size is the number of bytes written to \p pkt_buf. + * + * \returns true iff the payload fits and the checksum is OK. + * + * Packets have this format: + * + *  $<packet-data>#<checksum> + * + * Where <packet-data> is anything and <checksum> is a two byte hex + * checksum.  In <packet-data> '$', '#' and 0x7d are escaped with 0x7d. + * The checksum is computed as the modulo 256 sum of all characters + * btween the leading '$' and the trailing '#' (an 8-bit unsigned + * checksum). + */ +static bool +get_packet(unsigned char *pkt_buf, size_t max_size, size_t *actual_size) +{ +  typedef enum states { +    LOOKING_FOR_DOLLAR, +    LOOKING_FOR_HASH, +    CSUM1, +    CSUM2, +  } state_t; + +  *actual_size = 0; +  unsigned char csum[2] = {0, 0}; +  state_t state = LOOKING_FOR_DOLLAR; +  size_t  pi = 0; + +  while (1){ +    int ch = gdb_getc(); + +    switch (state){ +    case LOOKING_FOR_DOLLAR: +      if (ch == '$'){ +	pi = 0; +	state = LOOKING_FOR_HASH; +      } +      else if (ch == '#'){	// most likely missed the $ +	return false; +      } +      break; +	 +    case LOOKING_FOR_HASH: +      if (ch == '$'){ +	return false; +      } +      else if (ch == '#'){ +	state = CSUM1; +      } +      else { +	if (pi >= max_size)	// payload too big +	  return false; + +	if (ch == GDB_ESCAPE) +	  ch = gdb_getc(); + +	pkt_buf[pi++] = ch; +      } +      break; +       +    case CSUM1: +      csum[0] = ch; +      state = CSUM2; +      break; + +    case CSUM2: +      csum[1] = ch; +      *actual_size = pi; + +      // accept .. as a correct checksum +      if (csum[0] == '.' && csum[1] == '.') +	return true; + +      int expected_checksum; +      if (!hex8_to_bin(csum, &expected_checksum)) +	return false; + +      int checksum = 0; +      for (size_t i = 0; i < pi; i++) +	checksum += pkt_buf[i]; + +      checksum &= 0xff; +      return checksum == expected_checksum; +    } +  } +} + +static void +put_packet_trailer(int checksum) +{ +  gdb_putc('#'); +  put_hex8(checksum & 0xff); +  gdb_putc('\r'); +  gdb_putc('\n'); +} + +static void +put_packet(const unsigned char *pkt_buf, size_t size) +{ +  gdb_putc('$'); + +  int checksum = 0; +  for (size_t i = 0; i < size; i++){ +    int ch = pkt_buf[i]; +    if (needs_escaping(ch)) +      gdb_putc(GDB_ESCAPE); +    gdb_putc(ch); +    checksum += ch; +  } +  put_packet_trailer(checksum); +} + +/*! + * Read a hex number + * + * \param[inout] bufptr - pointer to pointer to buffer (updated on return) + * \param[in] end - one past end of valid data in buf + * \param[out] value - the parsed value + * + * \returns true iff a valid hex number was read from bufptr + */ +static bool +parse_number(const unsigned char **bufptr, const unsigned char *end, unsigned int *value) +{ +  const unsigned char *buf = *bufptr; +  unsigned int v = 0; +  bool valid = false; +  int nibble; + +  while (buf < end && hex4_to_bin(*buf, &nibble)){ +    valid = true; +    v = (v << 4) | nibble; +    buf++; +  } +   +  *value = v; +  *bufptr = buf; +  return valid; +} + +static bool +parse_char(const unsigned char **bufptr, const unsigned char *end, unsigned char *ch) +{ +  const unsigned char *buf = *bufptr; +  if (buf < end){ +    *ch = *buf++; +    *bufptr = buf; +    return true; +  } +  return false; +} + +static bool +expect_char(const unsigned char **bufptr, const unsigned char *end, unsigned char expected) +{ +  unsigned char ch; +  return parse_char(bufptr, end, &ch) && ch == expected; +} + +static bool +expect_end(const unsigned char **bufptr, const unsigned char *end) +{ +  return *bufptr == end; +} + +static bool +parse_addr_length(const unsigned char **bufptr, const unsigned char *end, +		  unsigned int *addr, unsigned int *length) +{ +  return (parse_number(bufptr, end, addr) +	  && expect_char(bufptr, end, ',') +	  && parse_number(bufptr, end, length)); +} + +static void +put_error(int error) +{ +  unsigned char buf[3]; +  buf[0] = 'E'; +  buf[1] = hex_table[(error >> 4) & 0xf]; +  buf[2] = hex_table[error & 0xf]; +   +  put_packet(buf, sizeof(buf)); +} + +static void +put_ok(void) +{ +  const unsigned char buf[2] = "OK"; +  put_packet(buf, sizeof(buf)); +} + +/* + * Read memory and send the reply. + * We do it on the fly so that our packet size is effectively unlimited + */ +static void +read_memory(unsigned int addr, unsigned int nbytes) +{ +  int checksum = 0; +  gdb_putc('$'); + +  if ((addr & 0x3) == 0 && (nbytes & 0x3) == 0){	// word aligned +    union { +      unsigned int	i; +      unsigned char	c[4]; +    } u; + +    unsigned int *p = (unsigned int *) addr; +    unsigned int length = nbytes / 4; + +    for (unsigned int i = 0; i < length; i++){ +      u.i = p[i];	// do a word read +      checksum = put_hex8_checksum(u.c[0], checksum); +      checksum = put_hex8_checksum(u.c[1], checksum); +      checksum = put_hex8_checksum(u.c[2], checksum); +      checksum = put_hex8_checksum(u.c[3], checksum); +    } +  } +  else {						// byte aligned +    unsigned char *p = (unsigned char *) addr; +    for (unsigned int i = 0; i < nbytes; i++) +      checksum = put_hex8_checksum(p[i], checksum); +  } + +  put_packet_trailer(checksum); +} + +static unsigned int +get_unaligned_int(const unsigned char *p) +{ +  // we're bigendian +  return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]); +} + +static bool +write_memory(unsigned int addr, size_t nbytes, +	     const unsigned char *data) +{ +  if ((addr & 0x3) == 0 && (nbytes & 0x3) == 0){	// word-aligned dst +    unsigned int *dst = (unsigned int *) addr; +    size_t length = nbytes / 4; +    for (size_t i = 0; i < length; i++){ +      unsigned int t = get_unaligned_int(&data[4*i]); +      dst[i] = t;					// word writes +    } +  } +  else {						// non-word-aligned dst +    unsigned char *dst = (unsigned char *) addr; +    for (size_t i = 0; i < nbytes; i++){ +      dst[i] = data[i]; +    } +  } +  return true; +} + +void +gdbstub2_main_loop(void) +{ +  unsigned char inpkt[MAX_PACKET + 24]; +  unsigned char binary_data[MAX_PACKET/2] __attribute__((aligned (4))); + +  hal_uart_set_mode(UART_MODE_RAW); //tell UART HAL not to map \n to \r\n + +  while (1){ +    size_t	inpkt_len; +    bool ok = get_packet(inpkt, sizeof(inpkt), &inpkt_len); +    if (!ok){ +      gdb_putc('-'); +      continue; +    } +    gdb_putc('+'); + +    const unsigned char *buf = inpkt; +    const unsigned char *end = inpkt + inpkt_len; +    unsigned char ch; + +    if (!parse_char(&buf, end, &ch)){	// empty packet +      put_packet(0, 0); +      continue; +    } + +    unsigned int addr; +    unsigned int length; + +    switch(ch){ +    case 'm':		// m<addr>,<length>  -- read <length> bytes starting at <addr> +      if (!(parse_addr_length(&buf, end, &addr, &length) && expect_end(&buf, end))){ +	put_error(1); +      } +      else { +	read_memory(addr, length); +      } +      break; + +    case 'M':		// M<addr>,<length>:XX...  -- write <length> bytes starting at <addr> +			//   XX... is the data in hex +      if (!(parse_addr_length(&buf, end, &addr, &length) +	    && expect_char(&buf, end, ':') +	    && (end - buf) == 2 * length)){ +	put_error(1); +      } +      else { +	if (!hex_to_bin_array(binary_data, buf, length)) +	  put_error(2); +	else if (!write_memory(addr, length, binary_data)) +	  put_error(3); +	else +	  put_ok(); +      } +      break; + +    case 'X':		// X<addr>,<length>:XX...  -- write <length> bytes starting at <addr> +			//   XX... is the data in binary +      if (!(parse_addr_length(&buf, end, &addr, &length) +	    && expect_char(&buf, end, ':') +	    && (end - buf) == length)){ +	put_error(1); +      } +      else { +	if (!write_memory(addr, length, buf)) +	  put_error(3); +	else +	  put_ok(); +      } +      break; + +    case 'c':		// c<addr>	-- continue.  <addr> is the address to resume (goto). +      if (!(parse_number(&buf, end, &addr) +	    && expect_end(&buf, end))){ +	put_error(1); +      } +      else { +	typedef void (*fptr_t)(void); +	(*(fptr_t) addr)();	// most likely no return +      } +      break; +/* +    case 0x80: +      { +	unsigned char *output = binary_data;  // reuse +	size_t sizeof_output = sizeof(binary_data); +	size_t actual_olen; +	loader_parser(buf, end-buf, +		      output, sizeof_output, &actual_olen); +	put_packet(output, actual_olen); +      } +      break; +*/ +    default:		// unknown packet type +      put_packet(0, 0); +      break; +    } +  } +} diff --git a/firmware/microblaze/lib/gdbstub2.h b/firmware/microblaze/lib/gdbstub2.h new file mode 100644 index 000000000..15cdde939 --- /dev/null +++ b/firmware/microblaze/lib/gdbstub2.h @@ -0,0 +1,25 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_GDBSTUB_H +#define INCLUDED_GDBSTUB_H + +void gdbstub2_main_loop(void); + +#endif /* INCLUDED_GDBSTUB_H */ + diff --git a/firmware/microblaze/lib/hal_io.c b/firmware/microblaze/lib/hal_io.c index 0afd6a2cc..58b1e681e 100644 --- a/firmware/microblaze/lib/hal_io.c +++ b/firmware/microblaze/lib/hal_io.c @@ -193,3 +193,13 @@ puts(const char *s)    putchar('\n');    return 0;  } + +char * +gets(char * const s) +{ +	char *x = s; +	while((*x=(char)hal_uart_getc()) != '\n') x++; +	*x = 0; +	return s; +} + diff --git a/firmware/microblaze/lib/hal_io.h b/firmware/microblaze/lib/hal_io.h index d8967f063..c67d96c62 100644 --- a/firmware/microblaze/lib/hal_io.h +++ b/firmware/microblaze/lib/hal_io.h @@ -23,6 +23,7 @@  void hal_io_init(void);  void hal_finish(); +char *gets(char * const s);  /*   * ------------------------------------------------------------------------ diff --git a/firmware/microblaze/lib/hal_uart.c b/firmware/microblaze/lib/hal_uart.c index 75b12b432..fe3b7515a 100644 --- a/firmware/microblaze/lib/hal_uart.c +++ b/firmware/microblaze/lib/hal_uart.c @@ -39,16 +39,25 @@ divisor_table[MAX_WB_DIV+1][NSPEEDS] = {  #define u uart_regs +static char uart_mode = UART_MODE_ONLCR; + +void +hal_uart_set_mode(int mode) +{ +  uart_mode = mode; +} +  void  hal_uart_init(void)  { +	hal_uart_set_mode(UART_MODE_ONLCR);    u->clkdiv = 217;  // 230400 bps  }  void  hal_uart_putc(int ch)  { -  if (ch == '\n')		// FIXME for now map \n -> \r\n +  if (ch == '\n')// && (uart_mode == UART_MODE_ONLCR))		//map \n->\r\n if necessary      hal_uart_putc('\r');    while (u->txlevel == 0)	 // wait for fifo to have space @@ -60,7 +69,7 @@ hal_uart_putc(int ch)  void  hal_uart_putc_nowait(int ch)  { -  if (ch == '\n')		// FIXME for now map \n -> \r\n +  if (ch == '\n')// && (uart_mode == UART_MODE_ONLCR))		//map \n->\r\n if necessary      hal_uart_putc('\r');    if(u->txlevel)   // If fifo has space diff --git a/firmware/microblaze/lib/hal_uart.h b/firmware/microblaze/lib/hal_uart.h index 2ddfa6259..dfd73c323 100644 --- a/firmware/microblaze/lib/hal_uart.h +++ b/firmware/microblaze/lib/hal_uart.h @@ -19,6 +19,16 @@  #ifndef INCLUDED_HAL_UART_H  #define INCLUDED_HAL_UART_H +/*! + * \brief uart mode flags + */ +#define	UART_MODE_RAW		0x0000	// no mapping on input or output +#define	UART_MODE_ONLCR	0x0001	// map \n to \r\n on output (default) + +/* + * \brief Set uart mode + */ +void hal_uart_set_mode(int flags);  /*!   * \brief one-time call to init 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/net/.gitignore b/firmware/microblaze/lib/net/.gitignore new file mode 100644 index 000000000..282522db0 --- /dev/null +++ b/firmware/microblaze/lib/net/.gitignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/firmware/microblaze/lib/net/eth_mac_addr.h b/firmware/microblaze/lib/net/eth_mac_addr.h new file mode 100644 index 000000000..b44fb68f7 --- /dev/null +++ b/firmware/microblaze/lib/net/eth_mac_addr.h @@ -0,0 +1,29 @@ +/* + * 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_ETH_MAC_ADDR_H +#define INCLUDED_ETH_MAC_ADDR_H + +#include <stdint.h> + +// Ethernet MAC address + +typedef struct { +  uint8_t	addr[6]; +} eth_mac_addr_t; + +#endif /* INCLUDED_ETH_MAC_ADDR_H */ diff --git a/firmware/microblaze/lib/net/padded_eth_hdr.h b/firmware/microblaze/lib/net/padded_eth_hdr.h new file mode 100644 index 000000000..df816734f --- /dev/null +++ b/firmware/microblaze/lib/net/padded_eth_hdr.h @@ -0,0 +1,37 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2010 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_PADDED_ETH_HDR_H +#define INCLUDED_PADDED_ETH_HDR_H + +#include <compiler.h> +#include <net/eth_mac_addr.h> + +/*! + * \brief Standard 14-byte ethernet header plus two leading bytes of padding. + * + * This is what a buffer contains in line 1 when using the "slow mode" + */ +typedef struct { +  uint16_t	 pad; +  eth_mac_addr_t dst; +  eth_mac_addr_t src; +  uint16_t 	 ethertype; +} _AL4 padded_eth_hdr_t; + + +#endif /* INCLUDED_PADDED_ETH_HDR_H */ diff --git a/firmware/microblaze/lib/net/socket_address.h b/firmware/microblaze/lib/net/socket_address.h new file mode 100644 index 000000000..336f30a0c --- /dev/null +++ b/firmware/microblaze/lib/net/socket_address.h @@ -0,0 +1,41 @@ +/* -*- c -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_SOCKET_ADDRESS_H +#define INCLUDED_SOCKET_ADDRESS_H + +#include <lwip/ip_addr.h> + +// port and address are in network byte order + +typedef struct socket_address { +  unsigned short   port; +  struct ip_addr   addr; +} socket_address_t; + +static inline struct socket_address  +make_socket_address(struct ip_addr addr, int port) +{ +  struct socket_address r; +  r.port = port; +  r.addr = addr; +  return r; +} + + + +#endif /* INCLUDED_SOCKET_ADDRESS_H */ diff --git a/firmware/microblaze/lib/nonstdio.c b/firmware/microblaze/lib/nonstdio.c index 1c991afee..4b5fa4123 100644 --- a/firmware/microblaze/lib/nonstdio.c +++ b/firmware/microblaze/lib/nonstdio.c @@ -78,3 +78,46 @@ puthex32_nl(unsigned long x)    puthex32(x);    newline();  } +/* +void reverse(char s[]) +{ +    int c, i, j; + +    for (i = 0, j = strlen(s)-1; i<j; i++, j--) { +        c = s[i]; +        s[i] = s[j]; +        s[j] = c; +    } +} + +int abs(signed long value) { +	return (value >= 0) ? value : 0-value; +} + +//we'll keep the puthex functions above because they're way more lightweight. but sometimes you just want to print in decimal, you know? +char *itoa(signed long value, char *result, int base) +{ +	// check that the base if valid +	if (base < 2 || base > 16) { *result = 0; return result; } + +	char* out = result; +	signed long quotient = value; + +	do { +		*out = hex[ abs(quotient % base) ]; +		++out; +		quotient /= base; +	} while ( quotient ); + +	// Only apply negative sign for base 10 +	if ( value < 0 && base == 10) *out++ = '-'; + +	*out = 0; +	reverse( result ); + +	return result; + +} +*/ + + diff --git a/firmware/microblaze/lib/nonstdio.h b/firmware/microblaze/lib/nonstdio.h index 3fd9e39bb..62ebfa46d 100644 --- a/firmware/microblaze/lib/nonstdio.h +++ b/firmware/microblaze/lib/nonstdio.h @@ -1,4 +1,6 @@ -/* -*- c -*- */ +// +// Copyright 2010 Ettus Research LLC +//  /*   * Copyright 2007 Free Software Foundation, Inc.   * @@ -20,7 +22,7 @@  #define INCLUDED_NONSTDIO_H  #include <stdio.h> -#include <usrp2_types.h> +#include <stdint.h>  #include <stddef.h>  void putstr(const char *s);		// cf puts, no added newline @@ -37,10 +39,10 @@ void puthex32_nl(unsigned long x);  void newline();				// putchar('\n')  void print_mac_addr(const unsigned char addr[6]); -void print_fxpt_freq(u2_fxpt_freq_t v); -void print_fxpt_gain(u2_fxpt_gain_t v);  void print_uint64(uint64_t v);  void print_buffer(uint32_t *buf, size_t n); +//char *itoa(signed long value, char *result, int base); +//void reverse(char s[]);  #endif /* INCLUDED_NONSTDIO_H */ diff --git a/firmware/microblaze/lib/spi.h b/firmware/microblaze/lib/spi.h index f5b69b270..01e4d26fd 100644 --- a/firmware/microblaze/lib/spi.h +++ b/firmware/microblaze/lib/spi.h @@ -48,5 +48,23 @@ void spi_wait(void);  uint32_t  spi_transact(bool readback, int slave, uint32_t data, int length, uint32_t flags); +// ---------------------------------------------------------------- +// Routines that manipulate the FLASH SPI BUS +// ---------------------------------------------------------------- + +/*! + * \brief One time call to initialize SPI + */ +void spif_init(void); + +/*!  + * \brief Wait for last SPI transaction to complete. + * Unless you need to know it completed, it's not necessary to call this. + */ +void spif_wait(void); + +uint32_t +spif_transact(bool readback_, int slave, uint32_t data, int length, uint32_t flags); +  #endif /* INCLUDED_SPI_H */ diff --git a/firmware/microblaze/lib/u2_init.c b/firmware/microblaze/lib/u2_init.c index 6809101c0..ff558d673 100644 --- a/firmware/microblaze/lib/u2_init.c +++ b/firmware/microblaze/lib/u2_init.c @@ -25,9 +25,7 @@  #include "i2c.h"  #include "mdelay.h"  #include "clocks.h" -#include "usrp2_i2c_addr.h" - -//#include "nonstdio.h" +#include "usrp2/fw_common.h"  unsigned char u2_hw_rev_major;  unsigned char u2_hw_rev_minor; @@ -35,8 +33,8 @@ unsigned char u2_hw_rev_minor;  static inline void  get_hw_rev(void)  { -  bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_REV_LSB, &u2_hw_rev_minor, 1); -  ok &= eeprom_read(I2C_ADDR_MBOARD, MBOARD_REV_MSB, &u2_hw_rev_major, 1); +  bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV_LSB, &u2_hw_rev_minor, 1); +  ok &= eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV_MSB, &u2_hw_rev_major, 1);  }  /* 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/usrp2/.gitignore b/firmware/microblaze/usrp2/.gitignore new file mode 100644 index 000000000..18f715618 --- /dev/null +++ b/firmware/microblaze/usrp2/.gitignore @@ -0,0 +1,9 @@ +/Makefile +/Makefile.in +/*.a +/*.bin +/*.dump +/*.ihx +/*.elf +/*.rom +/*.map diff --git a/firmware/microblaze/usrp2/Makefile.am b/firmware/microblaze/usrp2/Makefile.am new file mode 100644 index 000000000..859ded9e5 --- /dev/null +++ b/firmware/microblaze/usrp2/Makefile.am @@ -0,0 +1,43 @@ +# +# 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) \ +	libusrp2.a \ +	-Wl,-defsym -Wl,_TEXT_START_ADDR=0x0050 \ +	-Wl,-defsym -Wl,_STACK_SIZE=3072 + +######################################################################## +# USRP2 specific library and programs +######################################################################## +noinst_LIBRARIES = libusrp2.a + +libusrp2_a_SOURCES = \ +	$(COMMON_SRCS) \ +	clocks.c \ +	sd.c + +noinst_PROGRAMS = \ +	usrp2_txrx_uhd.elf + +usrp2_txrx_uhd_elf_SOURCES = \ +	$(top_srcdir)/apps/txrx_uhd.c diff --git a/firmware/microblaze/usrp2/clocks.c b/firmware/microblaze/usrp2/clocks.c new file mode 100644 index 000000000..ccc4a7cc7 --- /dev/null +++ b/firmware/microblaze/usrp2/clocks.c @@ -0,0 +1,239 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <clocks.h> + +#include "memory_map.h" +#include "ad9510.h" +#include "spi.h" +#include "u2_init.h" +#include "nonstdio.h" + +void  +clocks_init(void) +{ +  // Set up basic clocking functions in AD9510 +  ad9510_write_reg(0x45, 0x00); // CLK2 drives distribution + +  clocks_enable_fpga_clk(true, 1); + +  spi_wait(); + +  // Set up PLL for 10 MHz reference +  // Reg 4, A counter, Don't Care +  ad9510_write_reg(0x05, 0x00);  // Reg 5, B counter MSBs, 0 +  ad9510_write_reg(0x06, 0x05);  // Reg 6, B counter LSBs, 5 +  // Reg 7, Loss of reference detect, doesn't work yet, 0 +  ad9510_write_reg(0x5A, 0x01); // Update Regs + +  // Primary clock configuration +  clocks_mimo_config(MC_WE_DONT_LOCK); + +  // Set up other clocks +  //clocks_enable_test_clk(false, 0); +  //clocks_enable_tx_dboard(false, 0); +  //clocks_enable_rx_dboard(false, 0); +  clocks_enable_eth_phyclk(false, 0); + +  // Enable clock to ADCs and DACs +  //clocks_enable_dac_clk(true, 1); +  //clocks_enable_adc_clk(true, 1); +} + + +void +clocks_mimo_config(int flags) +{ +  if (flags & _MC_WE_LOCK){ +    // Reg 8, Charge pump on, dig lock det, positive PFD, 47 +    ad9510_write_reg(0x08, 0x47); +  } +  else { +    // Reg 8, Charge pump off, dig lock det, positive PFD +    ad9510_write_reg(0x08, 0x00); +  } +   +  // Reg 9, Charge pump current, 0x40=3mA, 0x00=650uA +  ad9510_write_reg(0x09, 0x00); +  // Reg A, Prescaler of 2, everything normal 04 +  ad9510_write_reg(0x0A, 0x04); +  // Reg B, R Div MSBs, 0 +  ad9510_write_reg(0x0B, 0x00); +  // Reg C, R Div LSBs, 1 +  ad9510_write_reg(0x0C, 0x01); +  // Reg D, Antibacklash, Digital lock det, 0 + +  ad9510_write_reg(0x5A, 0x01); // Update Regs + +  spi_wait(); +   +  // Allow for clock switchover +   +  if (flags & _MC_WE_LOCK){		// WE LOCK +    if (flags & _MC_MIMO_CLK_INPUT) { +      // Turn on ref output and choose the MIMO connector +      output_regs->clk_ctrl = 0x15;   +    } +    else { +      // turn on ref output and choose the SMA +      output_regs->clk_ctrl = 0x1C;  +    } +  } +  else {				// WE DONT LOCK +    // Disable both ext clk inputs +    output_regs->clk_ctrl = 0x10; +  } + +  // Do we drive a clock onto the MIMO connector? +  if (flags & MC_PROVIDE_CLK_TO_MIMO) +    clocks_enable_clkexp_out(true,10); +  else +    clocks_enable_clkexp_out(false,0);  +} + +bool  +clocks_lock_detect() +{ +  if(pic_regs->pending & PIC_CLKSTATUS) +    return true; +  return false; +} + +int inline +clocks_gen_div(int divisor) +{ +  int L,H; +  L = (divisor>>1)-1; +  H = divisor-L-2; +  return (L<<4)|H; +} + +#define CLOCK_OUT_EN 0x08 +#define CLOCK_OUT_DIS_CMOS 0x01 +#define CLOCK_OUT_DIS_PECL 0x02 +#define CLOCK_DIV_DIS 0x80 +#define CLOCK_DIV_EN 0x00 + +#define CLOCK_MODE_PECL 1 +#define CLOCK_MODE_LVDS 2 +#define CLOCK_MODE_CMOS 3 + +void  +clocks_enable_XXX_clk(bool enable, int divisor, int reg_en, int reg_div, int mode) +{ +  int enable_word, div_word, div_en_word; + +  switch(mode) { +  case CLOCK_MODE_PECL : +    enable_word = enable ? 0x08 : 0x0A; +    break; +  case CLOCK_MODE_LVDS : +    enable_word = enable ? 0x02 : 0x03; +    break; +  case CLOCK_MODE_CMOS : +    enable_word = enable ? 0x08 : 0x09; +    break; +  } +  if(enable && (divisor>1)) { +    div_word = clocks_gen_div(divisor); +    div_en_word = CLOCK_DIV_EN; +  } +  else { +    div_word = 0; +    div_en_word = CLOCK_DIV_DIS; +  } + +  ad9510_write_reg(reg_en,enable_word); // Output en/dis +  ad9510_write_reg(reg_div,div_word); // Set divisor +  ad9510_write_reg(reg_div+1,div_en_word); // Enable or Bypass Divider +  ad9510_write_reg(0x5A, 0x01);  // Update Regs +} + +// Clock 0 +/*void +clocks_enable_test_clk(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x3C,0x48,CLOCK_MODE_PECL); +}*/ + +// Clock 1 +void +clocks_enable_fpga_clk(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x3D,0x4A,CLOCK_MODE_PECL); +} + +// Clock 2 on Rev 3, Clock 5 on Rev 4 +void +clocks_enable_clkexp_out(bool enable, int divisor) +{ +  if(u2_hw_rev_major == 3) +    clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); +  else if(u2_hw_rev_major == 4) { +    ad9510_write_reg(0x34,0x00);  // Turn on fine delay +    ad9510_write_reg(0x35,0x00);  // Set Full Scale to nearly 10ns +    ad9510_write_reg(0x36,0x1c);  // Set fine delay.  0x20 is midscale +    clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); +     +  } +  else +    putstr("ERR: Invalid Rev\n"); +} + +// Clock 5 on Rev 3, none (was 2) on Rev 4 +void +clocks_enable_eth_phyclk(bool enable, int divisor) +{ +  if(u2_hw_rev_major == 3) +    clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); +  else if(u2_hw_rev_major == 4) +    clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); +  else +    putstr("ERR: Invalid Rev\n"); +} + +// Clock 3 +/*void +clocks_enable_dac_clk(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x3F,0x4E,CLOCK_MODE_PECL); +}*/ + +// Clock 4 +/*void +clocks_enable_adc_clk(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x40,0x50,CLOCK_MODE_LVDS); +}*/ + +// Clock 6 +/*void +clocks_enable_tx_dboard(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x42,0x54,CLOCK_MODE_CMOS); +}*/ + +// Clock 7 +/*void +clocks_enable_rx_dboard(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x43,0x56,CLOCK_MODE_CMOS); +}*/ diff --git a/firmware/microblaze/usrp2/memory_map.h b/firmware/microblaze/usrp2/memory_map.h new file mode 100644 index 000000000..4c9ead615 --- /dev/null +++ b/firmware/microblaze/usrp2/memory_map.h @@ -0,0 +1,793 @@ +/* -*- 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-7FFF  32K   RAM space (16K on 1500, 24K on 2000, 32K on DSP) + *   8000-BFFF  16K   Buffer Pool + *   C000-FFFF  16K   Peripherals + */ + + +#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 +// +//////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////// +// Main RAM, Slave 0 + +#define RAM_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 0x8000 + +#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 0xC000   // Base address (16-bit) + +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_ADS64P44 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 0xC400 + +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 0xC800 + +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 0xCC00 + +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) + +/* + * 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 0xD000 + +#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        0xD400 +#define	TX_PROTOCOL_ENGINE_BASE 0xD480 +#define	RX_PROTOCOL_ENGINE_BASE 0xD4C0 +#define BUFFER_POOL_CTRL_BASE   0xD500 +#define LAST_SETTING_REG        0xD7FC	// last valid setting register + +#define SR_MISC 0 +#define SR_TX_PROT_ENG 32 +#define SR_RX_PROT_ENG 48 +#define SR_BUFFER_POOL_CTRL 64 +#define SR_UDP_SM 96 +#define SR_TX_DSP 208 +#define SR_TX_CTRL 224 +#define SR_RX_DSP 160 +#define SR_RX_CTRL 176 +#define SR_TIME64 192 +#define SR_SIMTIMER 198 +#define SR_LAST 255 + +#define	_SR_ADDR(sr)	(MISC_OUTPUT_BASE + (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 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 << 4) +#define	LED_B		(1 << 1) +#define	LED_C		(1 << 3) +#define	LED_D		(1 << 0) +#define	LED_E		(1 << 2) +//      LED_F		// controlled by CPLD +#define	LED_RJ45	(1 << 5) + +#define output_regs ((output_regs_t *) MISC_OUTPUT_BASE) + +// --- udp tx regs --- + +typedef struct { +  // Bits 19:16 are control info; bits 15:0 are data (see below) +  // First two words are unused. +  volatile uint32_t _nope[2]; +  //--- ethernet header - 14 bytes--- +  volatile struct{ +    uint32_t mac_dst_0_1; //word 2 +    uint32_t mac_dst_2_3; +    uint32_t mac_dst_4_5; +    uint32_t mac_src_0_1; +    uint32_t mac_src_2_3; +    uint32_t mac_src_4_5; +    uint32_t ether_type; //word 8 +  } eth_hdr; +  //--- ip header - 20 bytes --- +  volatile struct{ +    uint32_t ver_ihl_tos; //word 9 +    uint32_t total_length; +    uint32_t identification; +    uint32_t flags_frag_off; +    uint32_t ttl_proto; +    uint32_t checksum; +    uint32_t src_addr_high; +    uint32_t src_addr_low; +    uint32_t dst_addr_high; +    uint32_t dst_addr_low; //word 18 +  } ip_hdr; +  //--- udp header - 8 bytes --- +  volatile struct{ +    uint32_t src_port; //word 19 +    uint32_t dst_port; +    uint32_t length; +    uint32_t checksum; //word 22 +  } udp_hdr; +  volatile uint32_t _pad[32-23]; +} sr_udp_sm_t; + +// control bits (all expect UDP_SM_LAST_WORD are mutually exclusive) + +// This is the last word of the header +#define	UDP_SM_LAST_WORD		(1 << 19) + +// Insert IP header checksum here.  Data is the xor of 16'hFFFF and +// the values written into regs 9-13 and 15-18. +#define	UDP_SM_INS_IP_HDR_CHKSUM	(1 << 18) + +// Insert IP Length here (data ignored) +#define	UDP_SM_INS_IP_LEN		(1 << 17) + +// Insert UDP Length here (data ignore) +#define	UDP_SM_INS_UDP_LEN		(1 << 16) + +#define sr_udp_sm ((sr_udp_sm_t *) _SR_ADDR(SR_UDP_SM)) + +// --- dsp tx regs --- + +#define MIN_CIC_INTERP	1 +#define	MAX_CIC_INTERP  128 + +typedef struct { +  volatile uint32_t     num_chan; +  volatile uint32_t     clear_state;	// clears out state machine, fifos, +} sr_tx_ctrl_t; + +#define sr_tx_ctrl ((sr_tx_ctrl_t *) _SR_ADDR(SR_TX_CTRL)) + +typedef struct { +  volatile int32_t	freq; +  volatile uint32_t	scale_iq;	// {scale_i,scale_q} +  volatile uint32_t     interp_rate; +  volatile uint32_t     _padding0;      // padding for the tx_mux +                                        //   NOT freq, scale, interp +  /*! +   * \brief output mux configuration. +   * +   * <pre> +   *     3                   2                   1                        +   *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   *  +-------------------------------+-------+-------+-------+-------+ +   *  |                                               | DAC1  |  DAC0 | +   *  +-------------------------------+-------+-------+-------+-------+ +   *  +   *  There are N DUCs (1 now) with complex inputs and outputs. +   *  There are two DACs. +   *  +   *  Each 4-bit DACx field specifies the source for the DAC +   *  Each subfield is coded like this:  +   *  +   *     3 2 1 0 +   *    +-------+ +   *    |   N   | +   *    +-------+ +   *  +   *  N specifies which DUC output is connected to this DAC. +   *  +   *   N   which interp output +   *  ---  ------------------- +   *   0   DUC 0 I +   *   1   DUC 0 Q +   *   2   DUC 1 I +   *   3   DUC 1 Q +   *   F   All Zeros +   *    +   * The default value is 0x10 +   * </pre> +   */ +  volatile uint32_t	tx_mux; + +} dsp_tx_regs_t; +   +#define dsp_tx_regs ((dsp_tx_regs_t *) _SR_ADDR(SR_TX_DSP)) + +// --- VITA RX CTRL regs --- +typedef struct { +  // The following 3 are logically a single command register. +  // They are clocked into the underlying fifo when time_ticks is written. +  volatile uint32_t	cmd;		// {now, chain, num_samples(30) +  volatile uint32_t	time_secs; +  volatile uint32_t	time_ticks; + +  volatile uint32_t	clear_overrun;	// write anything to clear overrun +  volatile uint32_t	vrt_header;	// word 0 of packet.  FPGA fills in packet counter +  volatile uint32_t	vrt_stream_id;	// word 1 of packet.  +  volatile uint32_t	vrt_trailer; +  volatile uint32_t	nsamples_per_pkt; +  volatile uint32_t     nchannels;      // 1 in basic case, up to 4 for vector sources +  volatile uint32_t     pad[7];         // Make each structure 16 elements long +} sr_rx_ctrl_t; + +#define sr_rx_ctrl ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL)) + +// --- dsp rx regs --- +#define	MIN_CIC_DECIM	1 +#define	MAX_CIC_DECIM   128 + +typedef struct { +  volatile int32_t	freq; +  volatile uint32_t	scale_iq;	// {scale_i,scale_q} +  volatile uint32_t     decim_rate; +  volatile uint32_t     dcoffset_i;     // Bit 31 high sets fixed offset mode, using lower 14 bits, +                                        // otherwise it is automatic  +  volatile uint32_t     dcoffset_q;     // Bit 31 high sets fixed offset mode, using lower 14 bits + +  /*! +   * \brief input mux configuration. +   * +   * This determines which ADC (or constant zero) is connected to  +   * each DDC input.  There are N DDCs (1 now).  Each has two inputs. +   * +   * <pre> +   * Mux value: +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * |                                                       |Q0 |I0 | +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * +   * Each 2-bit I field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) +   * Each 2-bit Q field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) +   * +   * The default value is 0x4 +   * </pre> +   */ +  volatile uint32_t     rx_mux;        // called adc_mux in dsp_core_rx.v + +  /*! +   * \brief Streaming GPIO configuration +   * +   * This determines whether the LSBs of I and Q samples come from the DSP +   * pipeline or from the io_rx GPIO pins.  To stream GPIO, one must first +   * set the GPIO data direction register to have io_rx[15] and/or io_rx[14] +   * configured as inputs.  The GPIO pins will be sampled at the time the +   * remainder of the DSP sample is strobed into the RX sample FIFO.  There +   * will be a decimation-dependent fixed time offset between the GPIO +   * sample stream and the associated RF samples. +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * |                           MBZ                             |Q|I| +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * +   * I         0=LSB comes from DSP pipeline (default) +   *           1=LSB comes from io_rx[15] +   *  +   * Q         0=LSB comes from DSP pipeline (default) +   *           1=LSB comes from io_rx[14] +   */ +  volatile uint32_t gpio_stream_enable; + +} dsp_rx_regs_t; +   +#define dsp_rx_regs ((dsp_rx_regs_t *) _SR_ADDR(SR_RX_DSP)) + +// ---------------------------------------------------------------- +// VITA49 64 bit time (write only) +  /*! +   * \brief Time 64 flags +   * +   * <pre> +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-----------------------------------------------------------+-+-+ +   * |                                                           |S|P| +   * +-----------------------------------------------------------+-+-+ +   * +   * P - PPS edge selection (0=negedge, 1=posedge, default=0) +   * S - Source (0=sma, 1=mimo, 0=default) +   * +   * </pre> +   */ +typedef struct { +  volatile uint32_t	secs;	// value to set absolute secs to on next PPS +  volatile uint32_t	ticks;	// value to set absolute ticks to on next PPS +  volatile uint32_t flags;  // flags - see chart above +  volatile uint32_t imm;    // set immediate (0=latch on next pps, 1=latch immediate, default=0) +} sr_time64_t; + +#define sr_time64 ((sr_time64_t *) _SR_ADDR(SR_TIME64)) + + +/*  + * --- ethernet tx protocol engine regs (write only) --- + * + * These registers control the transmit portion of the ethernet + * protocol engine (out of USRP2).  The protocol engine handles fifo + * status and sequence number insertion in outgoing packets, and + * automagically generates status packets when required to inform the + * host of changes in fifo availability. + * + * All outgoing packets have their fifo_status field set to the number + * of 32-bit lines of fifo available in the ethernet Rx fifo (see + * usrp2_eth_packet.h).  Seqno's are set if FIXME, else 0. + * + * FIXME clean this up once we know how it's supposed to behave. + */ + +typedef struct { +  volatile uint32_t  flags;	     // not yet fully defined (channel?) +  volatile uint32_t  mac_dst0123;    // 4 bytes of destination mac addr +  volatile uint32_t  mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr +  volatile uint32_t  mac_src2345;    // 4 bytes of destination mac addr +  volatile uint32_t  seqno;	     // Write to init seqno.  It autoincs on match +} tx_proto_engine_regs_t; + +#define tx_proto_engine ((tx_proto_engine_regs_t *) TX_PROTOCOL_ENGINE_BASE) + +/* + * --- ethernet rx protocol engine regs (write only) --- + * + * These registers control the receive portion of the ethernet + * protocol engine (into USRP2).  The protocol engine offloads common + * packet inspection operations so that firmware has less to do on + * "fast path" packets. + * + * The registers define conditions which must be matched for a packet + * to be considered a "fast path" packet.  If a received packet + * matches the src and dst mac address, ethertype, flags field, and + * expected seqno number it is considered a "fast path" packet, and + * the expected seqno is updated.  If the packet fails to satisfy any + * of the above conditions it's a "slow path" packet, and the + * corresponding SLOWPATH flag will be set buffer_status register. + */ + +typedef struct { +  volatile uint32_t  flags;	     // not yet fully defined (channel?) +  volatile uint32_t  mac_dst0123;    // 4 bytes of destination mac addr +  volatile uint32_t  mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr +  volatile uint32_t  mac_src2345;    // 4 bytes of destination mac addr +  volatile uint32_t  ethertype_pad;  // ethertype in high 16-bits +} rx_proto_engine_regs_t; + +#define rx_proto_engine ((rx_proto_engine_regs_t *) RX_PROTOCOL_ENGINE_BASE) + + + +/////////////////////////////////////////////////// +// Simple Programmable Interrupt Controller, Slave 8 + +#define PIC_BASE  0xD800 + +// 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_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) + +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)) + +/////////////////////////////////////////////////// +// UART, Slave 10 + +#define UART_BASE  0xE000 + +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  0xE400 + +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) + +/////////////////////////////////////////////////// +// SD Card SPI interface, Slave 13 +//   All regs are 8 bits wide, but are accessed as if they are 32 bits + +#define SDSPI_BASE  0xEC00 + +typedef struct { +  volatile uint32_t status;  // Write a 1 or 0 for controlling CS +  volatile uint32_t clkdiv; +  volatile uint32_t send_dat; +  volatile uint32_t receive_dat; +} sdspi_regs_t; + +#define sdspi_regs ((sdspi_regs_t *) SDSPI_BASE) + +/////////////////////////////////////////////////// +// External RAM interface, Slave 14 +//   Pages are 1K.  Page is 10 bits, set by a control register +//    output_regs->ram_page + +#define EXTRAM_BASE 0xF000 +#define extram ((volatile uint32_t *) EXTRAM_BASE) + + +/////////////////////////////////////////////////// + +#endif + diff --git a/firmware/microblaze/usrp2/sd.c b/firmware/microblaze/usrp2/sd.c new file mode 100644 index 000000000..d000b28ae --- /dev/null +++ b/firmware/microblaze/usrp2/sd.c @@ -0,0 +1,197 @@ +/* -*- c -*- */ +/* + * Copyright 2008 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 "sd.h" +#include "memory_map.h" +#include "stdint.h" +#include "stdio.h" + +static inline void +sd_packarg(unsigned char *argument,unsigned int value) +{ +  argument[3] = (unsigned char)(value >> 24); +  argument[2] = (unsigned char)(value >> 16); +  argument[1] = (unsigned char)(value >> 8); +  argument[0] = (unsigned char)(value); +} + +int +sd_init(void) +{ +  unsigned char response[5]; +  unsigned char argument[4]; +  int i,j; + +  for(i=0;i<4;i++) +    argument[i] = 0; + +  // Set clock to less than 400 kHz to start out +  sdspi_regs->clkdiv = 128; + +  // Delay at least 74 cycles +  sd_assert_cs(); +  for(i = 0; i < 100; i++) +    sd_send_byte(SD_IDLE); +  sd_deassert_cs(); +   +  // Initialization Sequence -- CMD0 CMD55 ACMD41 CMD58 +  // Put card in idle state +  if(sd_send_command(SD_CMD0,SD_CMD0_R,response,argument)==0) +    return 0;  // Something went wrong in command + +  j = 0; +  do { +    j++; +    if(sd_send_command(SD_CMD55,SD_CMD55_R,response,argument)==1) +      sd_send_command(SD_ACMD41,SD_ACMD41_R,response,argument); +    else +      j = SD_IDLE_WAIT_MAX; +  } +  while(((response[0] & SD_MSK_IDLE) == SD_MSK_IDLE) && (j < SD_IDLE_WAIT_MAX)); + +  if(j>= SD_IDLE_WAIT_MAX)  // IDLE timeout exceeded, card asleep +    return 0; +   +  // CMD58 reads the SD card capabilities +  if(sd_send_command(SD_CMD58,SD_CMD58_R,response,argument)==0) +    return 0;  // CMD58 FAIL + +  if((response[2] & SD_MSK_OCR_33) != SD_MSK_OCR_33) +    return 0;  // Card doesn't do 3.3V + +  //printf("OCR = %x %x %x %x\n",response[0],response[1],response[2],response[3]); + +  // Set blocklen here +  sd_packarg(argument,SD_BLOCKLEN); +  if(sd_send_command(SD_CMD16,SD_CMD16_R,response,argument)==0) +    return 0;    // Set Blocklen failed +   +  // Reset back to high speed +  sdspi_regs->clkdiv = 4; +  //puts("finished init\n"); +  return 1; +} + +int sd_send_command(unsigned char cmd,unsigned char response_type, +		    unsigned char *response,unsigned char *argument) +{ +  int i; +  char response_length; +  unsigned char tmp; + +  sd_assert_cs(); +  sd_send_byte((cmd & 0x3F) | 0x40); +  for(i=3;i>=0;i--) +    sd_send_byte(argument[i]); +  sd_send_byte(SD_CRC);   // Always the same + +  response_length = 0; +  switch(response_type) +    { +    case SD_R1: +    case SD_R1B: +      response_length = 1; +      break; +    case SD_R2: +      response_length = 2; +      break; +    case SD_R3: +      response_length = 5; +      break; +    default: +      break; +    } + +  // Wait for a response, which will have a 0 start bit +  i = 0; +  do +    { +      tmp = sd_rcv_byte(); +      i++; +    } +  while(((tmp & 0x80) != 0) && i < SD_CMD_TIMEOUT); + +  if(i>= SD_CMD_TIMEOUT) +    { +      sd_deassert_cs(); +      //puts("cmd send timeout\n"); +      return 0; +    } + +  for(i=response_length-1; i>=0; i--) +    { +      response[i] = tmp; +      tmp = sd_rcv_byte(); +    } +  i = 0; +  if(response_type == SD_R1B) +    { +      do +	{ +	  i++; +	  tmp = sd_rcv_byte(); +	} +      while(tmp != SD_IDLE); +      sd_send_byte(SD_IDLE); +    } +   +  //puts("send cmd success\n"); +  sd_deassert_cs(); +  return 1; +} + +int +sd_read_block (unsigned int blockaddr, unsigned char *buf) +{ +  unsigned char response[5]; +  unsigned char argument[4]; +  unsigned int i = 0; +  unsigned char tmp; + +  blockaddr <<= SD_BLOCKLEN_NBITS; +  sd_packarg(argument,blockaddr); +  if(sd_send_command(SD_CMD17,SD_CMD17_R,response,argument)==0) +    return 0;    //Failed READ; +  if(response[0] != 0) +    return 0;    //Misaligned READ + +  sd_assert_cs(); +  i = 0; +  do +    { +      tmp = sd_rcv_byte(); +      i++; +    } +  while((tmp == 0xFF) && (i < SD_RD_TIMEOUT)); +  if((i>= SD_RD_TIMEOUT) ||((tmp & SD_MSK_TOK_DATAERROR) == 0)) +    { +      sd_send_byte(SD_IDLE);  // Send a dummy before quitting +      return 0;   // Data ERROR +    } +  for(i=0;i<SD_BLOCKLEN;i++) +    buf[i] = sd_rcv_byte(); +  return 1; + +} + +int +sd_write_block(unsigned int blockaddr, const unsigned char *buf) +{ +  // FIXME not implemented yet +  return 0; +} diff --git a/firmware/microblaze/usrp2/sd.h b/firmware/microblaze/usrp2/sd.h new file mode 100644 index 000000000..e2d0ae38e --- /dev/null +++ b/firmware/microblaze/usrp2/sd.h @@ -0,0 +1,122 @@ +/* -*- c -*- */ +/* + * Copyright 2008 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_SD_H +#define INCLUDED_SD_H + +#include "memory_map.h" + +#define SD_READY 1 +#define SD_IDLE_WAIT_MAX 100 +#define SD_CMD_TIMEOUT 100 +#define SD_RD_TIMEOUT 1000 + +#define SD_CMD0 0 +#define SD_CMD1 1 +#define SD_CMD9 9 +#define SD_CMD10 10 +#define SD_CMD12 12 +#define SD_CMD13 13 +#define SD_CMD16 16 +#define SD_CMD17 17  +#define SD_CMD18 18 +#define SD_CMD24 24 +#define SD_CMD25 25 +#define SD_CMD27 27 +#define SD_CMD28 28 +#define SD_CMD29 29 +#define SD_CMD30 30 +#define SD_CMD32 32 +#define SD_CMD33 33 +#define SD_CMD38 38 +#define SD_CMD55 55 +#define SD_CMD58 58 +#define SD_CMD59 59 +#define SD_ACMD41 41 +#define SD_IDLE 0xFF +#define SD_CRC 0x95 + +#define SD_R1 1 +#define SD_R1B 2 +#define SD_R2 3 +#define SD_R3 4 + +#define SD_CMD0_R SD_R1 +#define SD_CMD16_R SD_R1 +#define SD_CMD17_R SD_R1 +#define SD_CMD55_R SD_R1 +#define SD_ACMD41_R SD_R1 +#define SD_CMD58_R SD_R3 + +#define SD_BLOCKLEN 512 +#define SD_BLOCKLEN_NBITS 9 + +#define SD_MSK_IDLE 0x01 +#define SD_MSK_OCR_33 0xC0 +#define SD_MSK_TOK_DATAERROR 0xE0 + + +int sd_init(void); + +static inline void +sd_assert_cs(void) +{ +  // Wait for idle before doing anything +  while(sdspi_regs->status != SD_READY) +    ; +  sdspi_regs->status = 1; +} + +static inline void +sd_deassert_cs(void) +{ +  // Wait for idle before doing anything +  while(sdspi_regs->status != SD_READY) +    ; +  sdspi_regs->status = 0; +} + +static inline char +sd_rcv_byte(void) +{ +  // Wait for idle before doing anything +  while(sdspi_regs->status != SD_READY) +    ; +  sdspi_regs->send_dat = SD_IDLE; +  while(sdspi_regs->status != SD_READY) +    ; +  return sdspi_regs-> receive_dat; +} + +static inline void +sd_send_byte(char dat) +{ +  // Wait for idle before doing anything +  while(sdspi_regs->status != SD_READY) +    ;      // Wait for status = 1 (ready) +  sdspi_regs->send_dat = dat; +} + + +int sd_send_command(unsigned char cmd,unsigned char response_type, +		    unsigned char *response,unsigned char *argument); + +int sd_read_block (unsigned int blockaddr, unsigned char *buf); +int sd_write_block(unsigned int blockaddr, const unsigned char *buf); + +#endif /* INCLUDED_SD_H */ 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..eff544294 --- /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) \ +	clocks.c \ +	spif.c \ +	spi_flash.c \ +	spi_flash_read.c \ +	bootloader_utils.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/clocks.c b/firmware/microblaze/usrp2p/clocks.c new file mode 100644 index 000000000..3488ec468 --- /dev/null +++ b/firmware/microblaze/usrp2p/clocks.c @@ -0,0 +1,259 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <clocks.h> + +#include "memory_map.h" +#include "ad9510.h" +#include "spi.h" +#include "u2_init.h" +#include "nonstdio.h" + +//USRP2PLUS clocks: +//Clock 0: testclk +//Clock 1: FPGA clk +//Clock 2: ADC clk +//Clock 3: DAC clk +//Clock 4: SER clk +//Clock 5: TX dboard clk +//Clock 6: EXP clk +//Clock 7: RX dboard clk + +//TODO: should have enough brains to init the FPGA clock for USRP2+. all others are suspect. +//note that without EEPROM support u2_hw_rev_major is going to be incorrect. + +void  +clocks_init(void) +{ +  // Set up basic clocking functions in AD9510 +  ad9510_write_reg(0x45, 0x01); // CLK2 drives distribution + +  clocks_enable_fpga_clk(true, 1); + +  spi_wait(); + +  // Set up PLL for 10 MHz reference +  // Reg 4, A counter, Don't Care +  ad9510_write_reg(0x05, 0x00);  // Reg 5, B counter MSBs, 0 +  ad9510_write_reg(0x06, 0x05);  // Reg 6, B counter LSBs, 5 +  // Reg 7, Loss of reference detect, doesn't work yet, 0 +  ad9510_write_reg(0x5A, 0x01); // Update Regs + +  // Primary clock configuration +  clocks_mimo_config(MC_WE_DONT_LOCK); + +  // Set up other clocks +  //clocks_enable_test_clk(false, 0); +  //clocks_enable_tx_dboard(false, 0); +  //clocks_enable_rx_dboard(false, 0); +  clocks_enable_eth_phyclk(false, 0); //PHY clk is separate now (u2r4, u2p) + +  // Enable clock to ADCs and DACs +  //clocks_enable_dac_clk(true, 1); +  //clocks_enable_adc_clk(true, 1); +} + + +void +clocks_mimo_config(int flags) +{ +  if (flags & _MC_WE_LOCK){ +    // Reg 8, Charge pump on, dig lock det, positive PFD, 47 +    ad9510_write_reg(0x08, 0x47); +  } +  else { +    // Reg 8, Charge pump off, dig lock det, positive PFD +    ad9510_write_reg(0x08, 0x00); +  } +   +  // Reg 9, Charge pump current, 0x40=3mA, 0x00=650uA +  ad9510_write_reg(0x09, 0x00); +  // Reg A, Prescaler of 2, everything normal 04 +  ad9510_write_reg(0x0A, 0x04); +  // Reg B, R Div MSBs, 0 +  ad9510_write_reg(0x0B, 0x00); +  // Reg C, R Div LSBs, 1 +  ad9510_write_reg(0x0C, 0x01); +  // Reg D, Antibacklash, Digital lock det, 0 + +  ad9510_write_reg(0x5A, 0x01); // Update Regs + +  spi_wait(); +   +  // Allow for clock switchover +   +  if (flags & _MC_WE_LOCK){		// WE LOCK +    if (flags & _MC_MIMO_CLK_INPUT) { +      // Turn on ref output and choose the MIMO connector +      output_regs->clk_ctrl = 0x15;   +    } +    else { +      // turn on ref output and choose the SMA +      output_regs->clk_ctrl = 0x1C;  +    } +  } +  else {				// WE DONT LOCK +    // Disable both ext clk inputs +    output_regs->clk_ctrl = 0x10; +  } + +  // Do we drive a clock onto the MIMO connector? +  if (flags & MC_PROVIDE_CLK_TO_MIMO) +    clocks_enable_clkexp_out(true,10); +  else +    clocks_enable_clkexp_out(false,0);  +} + +bool  +clocks_lock_detect() +{ +  if(pic_regs->pending & PIC_CLKSTATUS) +    return true; +  return false; +} + +int inline +clocks_gen_div(int divisor) +{ +  int L,H; +  L = (divisor>>1)-1; +  H = divisor-L-2; +  return (L<<4)|H; +} + +#define CLOCK_OUT_EN 0x08 +#define CLOCK_OUT_DIS_CMOS 0x01 +#define CLOCK_OUT_DIS_PECL 0x02 +#define CLOCK_DIV_DIS 0x80 +#define CLOCK_DIV_EN 0x00 + +#define CLOCK_MODE_PECL 1 +#define CLOCK_MODE_LVDS 2 +#define CLOCK_MODE_CMOS 3 + +//CHANGED: set to PECL for default behavior +void  +clocks_enable_XXX_clk(bool enable, int divisor, int reg_en, int reg_div, int mode) +{ +  int enable_word, div_word, div_en_word; + +  switch(mode) { +  case CLOCK_MODE_LVDS : +    enable_word = enable ? 0x02 : 0x03; +    break; +  case CLOCK_MODE_CMOS : +    enable_word = enable ? 0x08 : 0x09; +    break; +  case CLOCK_MODE_PECL : +	default: +    enable_word = enable ? 0x08 : 0x0A; +    break; +  } +  if(enable && (divisor>1)) { +    div_word = clocks_gen_div(divisor); +    div_en_word = CLOCK_DIV_EN; +  } +  else { +    div_word = 0; +    div_en_word = CLOCK_DIV_DIS; +  } + +  ad9510_write_reg(reg_en,enable_word); // Output en/dis +  ad9510_write_reg(reg_div,div_word); // Set divisor +  ad9510_write_reg(reg_div+1,div_en_word); // Enable or Bypass Divider +  ad9510_write_reg(0x5A, 0x01);  // Update Regs +} + +// Clock 0 +/*void +clocks_enable_test_clk(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x3C,0x48,CLOCK_MODE_PECL); +}*/ + +// Clock 1 +void +clocks_enable_fpga_clk(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x3D,0x4A,CLOCK_MODE_PECL); +} + +// Clock 2 on Rev 3, Clock 5 on Rev 4, Clock 6 on USRP2+ +void +clocks_enable_clkexp_out(bool enable, int divisor) +{ +  if(u2_hw_rev_major == 3) +    clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); +  else if(u2_hw_rev_major == 4) { +    ad9510_write_reg(0x34,0x00);  // Turn on fine delay +    ad9510_write_reg(0x35,0x00);  // Set Full Scale to nearly 10ns +    ad9510_write_reg(0x36,0x1c);  // Set fine delay.  0x20 is midscale +    clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); +  } +	else if(u2_hw_rev_major == 10) { +		ad9510_write_reg(0x34, 0x00); +		ad9510_write_reg(0x35, 0x00); +		ad9510_write_reg(0x36, 0x1C); +		clocks_enable_XXX_clk(enable, divisor, 0x42, 0x52, CLOCK_MODE_LVDS); +	} +  else +    putstr("ERR (clocks_enable_clkexp_out): Invalid hw rev, don't know what to do!\n"); +} + +// Clock 5 on Rev 3, none (was 2) on Rev 4, none on USRP2+ +void +clocks_enable_eth_phyclk(bool enable, int divisor) +{ +  if(u2_hw_rev_major == 3) +    clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); +  else if(u2_hw_rev_major == 4) +    clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); +  else +    putstr("(clocks_enable_eth_phyclk): no eth PHY clock or invalid hw rev\n"); //not really an error +} + +// Clock 3 +/*void +clocks_enable_dac_clk(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x3F,0x4E,CLOCK_MODE_PECL); +}*/ + +// Clock 4 +/*void +clocks_enable_adc_clk(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x40,0x50,CLOCK_MODE_LVDS); +}*/ + +// Clock 6 +/*void +clocks_enable_tx_dboard(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x42,0x54,CLOCK_MODE_CMOS); +}*/ + +// Clock 7 +/*void +clocks_enable_rx_dboard(bool enable, int divisor) +{ +  clocks_enable_XXX_clk(enable,divisor,0x43,0x56,CLOCK_MODE_CMOS); +}*/ diff --git a/firmware/microblaze/usrp2p/memory_map.h b/firmware/microblaze/usrp2p/memory_map.h new file mode 100644 index 000000000..fc0094e67 --- /dev/null +++ b/firmware/microblaze/usrp2p/memory_map.h @@ -0,0 +1,838 @@ +/* -*- 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 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 << 4) +#define	LED_B		(1 << 1) +#define	LED_C		(1 << 3) +#define	LED_D		(1 << 0) +#define	LED_E		(1 << 2) +//      LED_F		// controlled by CPLD +#define	LED_RJ45	(1 << 5) + +#define output_regs ((output_regs_t *) MISC_OUTPUT_BASE) + +// --- udp tx regs --- + +typedef struct { +  // Bits 19:16 are control info; bits 15:0 are data (see below) +  // First two words are unused. +  volatile uint32_t _nope[2]; +  //--- ethernet header - 14 bytes--- +  volatile struct{ +    uint32_t mac_dst_0_1; //word 2 +    uint32_t mac_dst_2_3; +    uint32_t mac_dst_4_5; +    uint32_t mac_src_0_1; +    uint32_t mac_src_2_3; +    uint32_t mac_src_4_5; +    uint32_t ether_type; //word 8 +  } eth_hdr; +  //--- ip header - 20 bytes --- +  volatile struct{ +    uint32_t ver_ihl_tos; //word 9 +    uint32_t total_length; +    uint32_t identification; +    uint32_t flags_frag_off; +    uint32_t ttl_proto; +    uint32_t checksum; +    uint32_t src_addr_high; +    uint32_t src_addr_low; +    uint32_t dst_addr_high; +    uint32_t dst_addr_low; //word 18 +  } ip_hdr; +  //--- udp header - 8 bytes --- +  volatile struct{ +    uint32_t src_port; //word 19 +    uint32_t dst_port; +    uint32_t length; +    uint32_t checksum; //word 22 +  } udp_hdr; +  volatile uint32_t _pad[32-23]; +} sr_udp_sm_t; + +// control bits (all expect UDP_SM_LAST_WORD are mutually exclusive) + +// This is the last word of the header +#define	UDP_SM_LAST_WORD		(1 << 19) + +// Insert IP header checksum here.  Data is the xor of 16'hFFFF and +// the values written into regs 9-13 and 15-18. +#define	UDP_SM_INS_IP_HDR_CHKSUM	(1 << 18) + +// Insert IP Length here (data ignored) +#define	UDP_SM_INS_IP_LEN		(1 << 17) + +// Insert UDP Length here (data ignore) +#define	UDP_SM_INS_UDP_LEN		(1 << 16) + +#define sr_udp_sm ((sr_udp_sm_t *) _SR_ADDR(SR_UDP_SM)) + +// --- dsp tx regs --- + +#define MIN_CIC_INTERP	1 +#define	MAX_CIC_INTERP  128 + +typedef struct { +  volatile uint32_t     num_chan; +  volatile uint32_t     clear_state;	// clears out state machine, fifos, +} sr_tx_ctrl_t; + +#define sr_tx_ctrl ((sr_tx_ctrl_t *) _SR_ADDR(SR_TX_CTRL)) + +typedef struct { +  volatile int32_t	freq; +  volatile uint32_t	scale_iq;	// {scale_i,scale_q} +  volatile uint32_t     interp_rate; +  volatile uint32_t     _padding0;      // padding for the tx_mux +                                        //   NOT freq, scale, interp +  /*! +   * \brief output mux configuration. +   * +   * <pre> +   *     3                   2                   1                        +   *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   *  +-------------------------------+-------+-------+-------+-------+ +   *  |                                               | DAC1  |  DAC0 | +   *  +-------------------------------+-------+-------+-------+-------+ +   *  +   *  There are N DUCs (1 now) with complex inputs and outputs. +   *  There are two DACs. +   *  +   *  Each 4-bit DACx field specifies the source for the DAC +   *  Each subfield is coded like this:  +   *  +   *     3 2 1 0 +   *    +-------+ +   *    |   N   | +   *    +-------+ +   *  +   *  N specifies which DUC output is connected to this DAC. +   *  +   *   N   which interp output +   *  ---  ------------------- +   *   0   DUC 0 I +   *   1   DUC 0 Q +   *   2   DUC 1 I +   *   3   DUC 1 Q +   *   F   All Zeros +   *    +   * The default value is 0x10 +   * </pre> +   */ +  volatile uint32_t	tx_mux; + +} dsp_tx_regs_t; +   +#define dsp_tx_regs ((dsp_tx_regs_t *) _SR_ADDR(SR_TX_DSP)) + +// --- VITA RX CTRL regs --- +typedef struct { +  // The following 3 are logically a single command register. +  // They are clocked into the underlying fifo when time_ticks is written. +  volatile uint32_t	cmd;		// {now, chain, num_samples(30) +  volatile uint32_t	time_secs; +  volatile uint32_t	time_ticks; + +  volatile uint32_t	clear_overrun;	// write anything to clear overrun +  volatile uint32_t	vrt_header;	// word 0 of packet.  FPGA fills in packet counter +  volatile uint32_t	vrt_stream_id;	// word 1 of packet.  +  volatile uint32_t	vrt_trailer; +  volatile uint32_t	nsamples_per_pkt; +  volatile uint32_t     nchannels;      // 1 in basic case, up to 4 for vector sources +  volatile uint32_t     pad[7];         // Make each structure 16 elements long +} sr_rx_ctrl_t; + +#define sr_rx_ctrl ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL)) + +// --- dsp rx regs --- +#define	MIN_CIC_DECIM	1 +#define	MAX_CIC_DECIM   128 + +typedef struct { +  volatile int32_t	freq; +  volatile uint32_t	scale_iq;	// {scale_i,scale_q} +  volatile uint32_t     decim_rate; +  volatile uint32_t     dcoffset_i;     // Bit 31 high sets fixed offset mode, using lower 14 bits, +                                        // otherwise it is automatic  +  volatile uint32_t     dcoffset_q;     // Bit 31 high sets fixed offset mode, using lower 14 bits + +  /*! +   * \brief input mux configuration. +   * +   * This determines which ADC (or constant zero) is connected to  +   * each DDC input.  There are N DDCs (1 now).  Each has two inputs. +   * +   * <pre> +   * Mux value: +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * |                                                       |Q0 |I0 | +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * +   * Each 2-bit I field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) +   * Each 2-bit Q field is either 00 (A/D A), 01 (A/D B) or 1X (const zero) +   * +   * The default value is 0x4 +   * </pre> +   */ +  volatile uint32_t     rx_mux;        // called adc_mux in dsp_core_rx.v + +  /*! +   * \brief Streaming GPIO configuration +   * +   * This determines whether the LSBs of I and Q samples come from the DSP +   * pipeline or from the io_rx GPIO pins.  To stream GPIO, one must first +   * set the GPIO data direction register to have io_rx[15] and/or io_rx[14] +   * configured as inputs.  The GPIO pins will be sampled at the time the +   * remainder of the DSP sample is strobed into the RX sample FIFO.  There +   * will be a decimation-dependent fixed time offset between the GPIO +   * sample stream and the associated RF samples. +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * |                           MBZ                             |Q|I| +   * +-------+-------+-------+-------+-------+-------+-------+-------+ +   * +   * I         0=LSB comes from DSP pipeline (default) +   *           1=LSB comes from io_rx[15] +   *  +   * Q         0=LSB comes from DSP pipeline (default) +   *           1=LSB comes from io_rx[14] +   */ +  volatile uint32_t gpio_stream_enable; + +} dsp_rx_regs_t; +   +#define dsp_rx_regs ((dsp_rx_regs_t *) _SR_ADDR(SR_RX_DSP)) + +// ---------------------------------------------------------------- +// VITA49 64 bit time (write only) +  /*! +   * \brief Time 64 flags +   * +   * <pre> +   * +   *    3                   2                   1                        +   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +   * +-----------------------------------------------------------+-+-+ +   * |                                                           |S|P| +   * +-----------------------------------------------------------+-+-+ +   * +   * P - PPS edge selection (0=negedge, 1=posedge, default=0) +   * S - Source (0=sma, 1=mimo, 0=default) +   * +   * </pre> +   */ +typedef struct { +  volatile uint32_t	secs;	// value to set absolute secs to on next PPS +  volatile uint32_t	ticks;	// value to set absolute ticks to on next PPS +  volatile uint32_t flags;  // flags - see chart above +  volatile uint32_t imm;    // set immediate (0=latch on next pps, 1=latch immediate, default=0) +} sr_time64_t; + +#define sr_time64 ((sr_time64_t *) _SR_ADDR(SR_TIME64)) + + +/*  + * --- ethernet tx protocol engine regs (write only) --- + * + * These registers control the transmit portion of the ethernet + * protocol engine (out of USRP2).  The protocol engine handles fifo + * status and sequence number insertion in outgoing packets, and + * automagically generates status packets when required to inform the + * host of changes in fifo availability. + * + * All outgoing packets have their fifo_status field set to the number + * of 32-bit lines of fifo available in the ethernet Rx fifo (see + * usrp2_eth_packet.h).  Seqno's are set if FIXME, else 0. + * + * FIXME clean this up once we know how it's supposed to behave. + */ + +typedef struct { +  volatile uint32_t  flags;	     // not yet fully defined (channel?) +  volatile uint32_t  mac_dst0123;    // 4 bytes of destination mac addr +  volatile uint32_t  mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr +  volatile uint32_t  mac_src2345;    // 4 bytes of destination mac addr +  volatile uint32_t  seqno;	     // Write to init seqno.  It autoincs on match +} tx_proto_engine_regs_t; + +#define tx_proto_engine ((tx_proto_engine_regs_t *) TX_PROTOCOL_ENGINE_BASE) + +/* + * --- ethernet rx protocol engine regs (write only) --- + * + * These registers control the receive portion of the ethernet + * protocol engine (into USRP2).  The protocol engine offloads common + * packet inspection operations so that firmware has less to do on + * "fast path" packets. + * + * The registers define conditions which must be matched for a packet + * to be considered a "fast path" packet.  If a received packet + * matches the src and dst mac address, ethertype, flags field, and + * expected seqno number it is considered a "fast path" packet, and + * the expected seqno is updated.  If the packet fails to satisfy any + * of the above conditions it's a "slow path" packet, and the + * corresponding SLOWPATH flag will be set buffer_status register. + */ + +typedef struct { +  volatile uint32_t  flags;	     // not yet fully defined (channel?) +  volatile uint32_t  mac_dst0123;    // 4 bytes of destination mac addr +  volatile uint32_t  mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr +  volatile uint32_t  mac_src2345;    // 4 bytes of destination mac addr +  volatile uint32_t  ethertype_pad;  // ethertype in high 16-bits +} rx_proto_engine_regs_t; + +#define rx_proto_engine ((rx_proto_engine_regs_t *) RX_PROTOCOL_ENGINE_BASE) + + + +/////////////////////////////////////////////////// +// Simple Programmable Interrupt Controller, Slave 8 + +#define PIC_BASE  0x3500 + +// Interrupt request lines +// Bit numbers (LSB == 0) that correpond to interrupts into PIC + +#define	IRQ_BUFFER	0	// buffer manager +#define	IRQ_ONETIME	1 +#define	IRQ_SPI		2 +#define	IRQ_I2C		3 +#define	IRQ_PHY		4	// ethernet PHY +#define	IRQ_UNDERRUN	5 +#define	IRQ_OVERRUN	6 +#define	IRQ_PPS		7	// pulse per second +#define	IRQ_UART_RX	8 +#define	IRQ_UART_TX	9 +#define	IRQ_SERDES	10 +#define	IRQ_CLKSTATUS	11 +#define IRQ_PERIODIC    12 +#define IRQ_BUTTON	13 + +#define IRQ_TO_MASK(x) (1 << (x)) + +#define PIC_BUFFER_INT    IRQ_TO_MASK(IRQ_BUFFER) +#define PIC_ONETIME_INT   IRQ_TO_MASK(IRQ_ONETIME) +#define PIC_SPI_INT       IRQ_TO_MASK(IRQ_SPI) +#define PIC_I2C_INT       IRQ_TO_MASK(IRQ_I2C) +#define PIC_PHY_INT       IRQ_TO_MASK(IRQ_PHY) +#define PIC_UNDERRUN_INT  IRQ_TO_MASK(IRQ_UNDERRUN) +#define PIC_OVERRUN_INT   IRQ_TO_MASK(IRQ_OVERRUN) +#define PIC_PPS_INT   	  IRQ_TO_MASK(IRQ_PPS) +#define PIC_UART_RX_INT   IRQ_TO_MASK(IRQ_UART_RX) +#define PIC_UART_TX_INT   IRQ_TO_MASK(IRQ_UART_TX) +#define PIC_SERDES        IRQ_TO_MASK(IRQ_SERDES) +#define PIC_CLKSTATUS     IRQ_TO_MASK(IRQ_CLKSTATUS) +#define PIC_BUTTON				IRQ_TO_MASK(IRQ_BUTTON) + +typedef struct { +  volatile uint32_t edge_enable; // mask: 1 -> edge triggered, 0 -> level +  volatile uint32_t polarity;	 // mask: 1 -> rising edge +  volatile uint32_t mask;	 // mask: 1 -> disabled +  volatile uint32_t pending;	 // mask: 1 -> pending; write 1's to clear pending ints +} pic_regs_t; + +#define pic_regs ((pic_regs_t *) PIC_BASE) + +// ---------------------------------------------------------------- +// WB_CLK_RATE is the time base for this +typedef struct { +  volatile uint32_t	onetime;   // Number of wb clk cycles till the onetime interrupt +  volatile uint32_t	periodic;  // Repeat rate of periodic interrupt +} sr_simple_timer_t; + +#define sr_simple_timer ((sr_simple_timer_t *) _SR_ADDR(SR_SIMTIMER)) + +/////////////////////////////////////////////////// +// UNUSED, Slave 9 + +/////////////////////////////////////////////////// +// UART, Slave 10 + +#define UART_BASE  0x3700 + +typedef struct { +  //  All elements are 8 bits except for clkdiv (16), but we use uint32 to make  +  //    the hardware for decoding easier +  volatile uint32_t clkdiv;  // Set to 50e6 divided by baud rate (no x16 factor) +  volatile uint32_t txlevel; // Number of spaces in the FIFO for writes +  volatile uint32_t rxlevel; // Number of available elements in the FIFO for reads +  volatile uint32_t txchar;  // Write characters to be sent here +  volatile uint32_t rxchar;  // Read received characters here +} 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 */  | 
