diff options
Diffstat (limited to 'firmware/microblaze/lib')
| -rw-r--r-- | firmware/microblaze/lib/Makefile.inc | 2 | ||||
| -rw-r--r-- | firmware/microblaze/lib/bootconfig.c | 101 | ||||
| -rw-r--r-- | firmware/microblaze/lib/gdbstub2.c | 506 | ||||
| -rw-r--r-- | firmware/microblaze/lib/gdbstub2.h | 25 | ||||
| -rw-r--r-- | firmware/microblaze/lib/hal_io.c | 92 | ||||
| -rw-r--r-- | firmware/microblaze/lib/hal_io.h | 5 | ||||
| -rw-r--r-- | firmware/microblaze/lib/hal_uart.c | 94 | ||||
| -rw-r--r-- | firmware/microblaze/lib/hal_uart.h | 50 | ||||
| -rw-r--r-- | firmware/microblaze/lib/i2c.c | 177 | ||||
| -rw-r--r-- | firmware/microblaze/lib/i2c.h | 21 | ||||
| -rw-r--r-- | firmware/microblaze/lib/i2c_async.c | 206 | ||||
| -rw-r--r-- | firmware/microblaze/lib/i2c_async.h | 50 | ||||
| -rw-r--r-- | firmware/microblaze/lib/ihex.c | 57 | ||||
| -rw-r--r-- | firmware/microblaze/lib/ihex.h | 18 | ||||
| -rw-r--r-- | firmware/microblaze/lib/u2_init.c | 2 | ||||
| -rw-r--r-- | firmware/microblaze/lib/udp_fw_update.h | 71 | 
16 files changed, 1221 insertions, 256 deletions
| diff --git a/firmware/microblaze/lib/Makefile.inc b/firmware/microblaze/lib/Makefile.inc index a576d1ccb..349ff2cd0 100644 --- a/firmware/microblaze/lib/Makefile.inc +++ b/firmware/microblaze/lib/Makefile.inc @@ -32,6 +32,7 @@ COMMON_SRCS = \  	$(top_srcdir)/lib/hal_io.c \  	$(top_srcdir)/lib/hal_uart.c \  	$(top_srcdir)/lib/i2c.c \ +	$(top_srcdir)/lib/i2c_async.c \  	$(top_srcdir)/lib/mdelay.c \  	$(top_srcdir)/lib/memcpy_wa.c \  	$(top_srcdir)/lib/memset_wa.c \ @@ -41,6 +42,7 @@ COMMON_SRCS = \  	$(top_srcdir)/lib/print_rmon_regs.c \  	$(top_srcdir)/lib/print_buffer.c \  	$(top_srcdir)/lib/printf.c \ +	$(top_srcdir)/lib/ihex.c \  	$(top_srcdir)/lib/spi.c \  	$(top_srcdir)/lib/net_common.c \  	$(top_srcdir)/lib/arp_cache.c \ diff --git a/firmware/microblaze/lib/bootconfig.c b/firmware/microblaze/lib/bootconfig.c new file mode 100644 index 000000000..93adc05c2 --- /dev/null +++ b/firmware/microblaze/lib/bootconfig.c @@ -0,0 +1,101 @@ +/* -*- c -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include "bootconfig.h" +#include "bootconfig_private.h" +#include <stdint.h> +#include <stddef.h> +#include <i2c.h> +#include <quadradio/i2c_addr.h> +#include <mdelay.h> +#include <xilinx_v5_icap.h> +#include <nonstdio.h> + +eeprom_boot_info_t eeprom_shadow; + +static eeprom_boot_info_t eeprom_default = { +  .magic = EEPROM_BOOT_INFO_MAGIC, +  .nattempts = 1, +  .next_boot.fpga_image_number = 0, +  .next_boot.firmware_image_number = 0, +  .default_boot.fpga_image_number = 0, +  .default_boot.firmware_image_number = 0 +}; + +eeprom_boot_info_t * +_bc_get_eeprom_shadow(void) +{ +  return &eeprom_shadow; +} + + +bool +_bc_write_eeprom_shadow(void) +{ +  return eeprom_write(I2C_ADDR_MBOARD, BOOT_INFO_OFFSET, &eeprom_shadow, sizeof(eeprom_shadow)); +} + +void +bootconfig_init(void) +{ +  if (!eeprom_read(I2C_ADDR_MBOARD, BOOT_INFO_OFFSET, &eeprom_shadow, sizeof(eeprom_shadow)) +      || eeprom_shadow.magic != EEPROM_BOOT_INFO_MAGIC){ +    eeprom_shadow = eeprom_default; +    _bc_write_eeprom_shadow(); +  } +} + +bootconfig_t  +bootconfig_get_default(void) +{ +  return eeprom_shadow.default_boot; +} + +bool +bootconfig_set_default(bootconfig_t bc) +{ +  if (!validate_bootconfig(bc)) +    return false; + +  eeprom_shadow.default_boot  = bc; +  eeprom_shadow.next_boot  = bc; +  return _bc_write_eeprom_shadow(); +} + +void +bootconfig_boot(bootconfig_t bc) +{ +  if (!validate_bootconfig(bc)) +    return; + +  eeprom_shadow.next_boot = bc; +  eeprom_shadow.nattempts = 1; +  _bc_write_eeprom_shadow(); + +  if (1){ +    puts("\nbootconfig: chaining to FPGA slot 0 bootloader"); +    mdelay(100); +  } + +  while (1){ +    // Reload fpga with code from SPI flash address 0x0. +    icap_reload_fpga(0x00000000); +  } +}   diff --git a/firmware/microblaze/lib/gdbstub2.c b/firmware/microblaze/lib/gdbstub2.c new file mode 100644 index 000000000..4c63dfce2 --- /dev/null +++ b/firmware/microblaze/lib/gdbstub2.c @@ -0,0 +1,506 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Implement a eensy weensy part of the GDB Remote Serial Protocol + * + * See Appendix D of the GDB manual + * + *   m<addr>,<length> 		-- read <length> bytes of memory starting at <addr> + *     Reply: + *     XX...		XX... is memory contents in hex + *     ENN		ENN   NN is a hex error number + * + *   M<addr>,<length>:XX...     -- write memory, data in hex + *     Reply: + *     OK		for success + *     ENN		for an error.  NN is a hex error number + * + *   X<addr>,<length>:XX...     -- write memory, data in binary + *     Reply: + *     OK		for success + *     ENN		for an error.  NN is a hex error number + * + *   c<addr>			-- continue.  <addr> is the address to resume (goto). + *     Reply: <none> + * + *   \x80 New Format... + */ + +#include "gdbstub2.h" +#include "loader_parser.h" +#include "hal_uart.h" +#include <stdbool.h> +#include <stddef.h> + +#define MAX_PACKET	1024 + +/* + * Get raw character from serial port, no echo. + */ +static inline int  +gdb_getc(void) +{ +  return hal_uart_getc(); +} + +/* + * Put character to serial port.  Raw output. + */ +static inline void +gdb_putc(int ch) +{ +  hal_uart_putc(ch); +} + +// ------------------------------------------------------------------------ + +#define	GDB_ESCAPE 0x7d + +static unsigned char hex_table[16] = {  +  '0', '1', '2', '3', '4', '5', '6', '7', +  '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +}; + +static int +put_hex8_checksum(int ch, int checksum) +{ +  unsigned char t = hex_table[(ch >> 4) & 0xf]; +  checksum += t; +  gdb_putc(t); + +  t = hex_table[ch & 0xf]; +  checksum += t; +  gdb_putc(t); +  return checksum; +} + +static void +put_hex8(int ch) +{ +  put_hex8_checksum(ch, 0); +} + +static bool +hex4_to_bin(int ch, int *value) +{ +  if ('0' <= ch && ch <= '9'){ +    *value = ch - '0'; +    return true; +  } +  if ('a' <= ch && ch <= 'f'){ +    *value = ch - 'a' + 10; +    return true; +  } +  if ('A' <= ch && ch <= 'F'){ +    *value = ch - 'A' + 10; +    return true; +  } +  *value = 0; +  return false; +} + +static bool +hex8_to_bin(const unsigned char *s, int *value) +{ +  int v0, v1; +  if (hex4_to_bin(s[0], &v0) && hex4_to_bin(s[1], &v1)){ +    *value = (v0 << 4) | v1; +    return true; +  } +  return false; +} + +static bool +hex_to_bin_array(unsigned char *binary_data, const unsigned char *hex_data, size_t nbytes) +{ +  for (size_t i = 0; i < nbytes; i++){ +    int t; +    if (!hex8_to_bin(&hex_data[2*i], &t)) +      return false; +    binary_data[i] = t; +  } +  return true; +} + +static bool +needs_escaping(int ch) +{ +  return ch == '$' || ch == '#' || ch == GDB_ESCAPE; +} + +/* + * \brief Wait for a packet.   + * \param[out] pkt_buf gets the received packet payload. + * \param[in]  max_size is the maximum number of bytes to write into \p pkt_buf. + * \param[out] actual_size is the number of bytes written to \p pkt_buf. + * + * \returns true iff the payload fits and the checksum is OK. + * + * Packets have this format: + * + *  $<packet-data>#<checksum> + * + * Where <packet-data> is anything and <checksum> is a two byte hex + * checksum.  In <packet-data> '$', '#' and 0x7d are escaped with 0x7d. + * The checksum is computed as the modulo 256 sum of all characters + * btween the leading '$' and the trailing '#' (an 8-bit unsigned + * checksum). + */ +static bool +get_packet(unsigned char *pkt_buf, size_t max_size, size_t *actual_size) +{ +  typedef enum states { +    LOOKING_FOR_DOLLAR, +    LOOKING_FOR_HASH, +    CSUM1, +    CSUM2, +  } state_t; + +  *actual_size = 0; +  unsigned char csum[2] = {0, 0}; +  state_t state = LOOKING_FOR_DOLLAR; +  size_t  pi = 0; + +  while (1){ +    int ch = gdb_getc(); + +    switch (state){ +    case LOOKING_FOR_DOLLAR: +      if (ch == '$'){ +	pi = 0; +	state = LOOKING_FOR_HASH; +      } +      else if (ch == '#'){	// most likely missed the $ +	return false; +      } +      break; +	 +    case LOOKING_FOR_HASH: +      if (ch == '$'){ +	return false; +      } +      else if (ch == '#'){ +	state = CSUM1; +      } +      else { +	if (pi >= max_size)	// payload too big +	  return false; + +	if (ch == GDB_ESCAPE) +	  ch = gdb_getc(); + +	pkt_buf[pi++] = ch; +      } +      break; +       +    case CSUM1: +      csum[0] = ch; +      state = CSUM2; +      break; + +    case CSUM2: +      csum[1] = ch; +      *actual_size = pi; + +      // accept .. as a correct checksum +      if (csum[0] == '.' && csum[1] == '.') +	return true; + +      int expected_checksum; +      if (!hex8_to_bin(csum, &expected_checksum)) +	return false; + +      int checksum = 0; +      for (size_t i = 0; i < pi; i++) +	checksum += pkt_buf[i]; + +      checksum &= 0xff; +      return checksum == expected_checksum; +    } +  } +} + +static void +put_packet_trailer(int checksum) +{ +  gdb_putc('#'); +  put_hex8(checksum & 0xff); +  gdb_putc('\r'); +  gdb_putc('\n'); +} + +static void +put_packet(const unsigned char *pkt_buf, size_t size) +{ +  gdb_putc('$'); + +  int checksum = 0; +  for (size_t i = 0; i < size; i++){ +    int ch = pkt_buf[i]; +    if (needs_escaping(ch)) +      gdb_putc(GDB_ESCAPE); +    gdb_putc(ch); +    checksum += ch; +  } +  put_packet_trailer(checksum); +} + +/*! + * Read a hex number + * + * \param[inout] bufptr - pointer to pointer to buffer (updated on return) + * \param[in] end - one past end of valid data in buf + * \param[out] value - the parsed value + * + * \returns true iff a valid hex number was read from bufptr + */ +static bool +parse_number(const unsigned char **bufptr, const unsigned char *end, unsigned int *value) +{ +  const unsigned char *buf = *bufptr; +  unsigned int v = 0; +  bool valid = false; +  int nibble; + +  while (buf < end && hex4_to_bin(*buf, &nibble)){ +    valid = true; +    v = (v << 4) | nibble; +    buf++; +  } +   +  *value = v; +  *bufptr = buf; +  return valid; +} + +static bool +parse_char(const unsigned char **bufptr, const unsigned char *end, unsigned char *ch) +{ +  const unsigned char *buf = *bufptr; +  if (buf < end){ +    *ch = *buf++; +    *bufptr = buf; +    return true; +  } +  return false; +} + +static bool +expect_char(const unsigned char **bufptr, const unsigned char *end, unsigned char expected) +{ +  unsigned char ch; +  return parse_char(bufptr, end, &ch) && ch == expected; +} + +static bool +expect_end(const unsigned char **bufptr, const unsigned char *end) +{ +  return *bufptr == end; +} + +static bool +parse_addr_length(const unsigned char **bufptr, const unsigned char *end, +		  unsigned int *addr, unsigned int *length) +{ +  return (parse_number(bufptr, end, addr) +	  && expect_char(bufptr, end, ',') +	  && parse_number(bufptr, end, length)); +} + +static void +put_error(int error) +{ +  unsigned char buf[3]; +  buf[0] = 'E'; +  buf[1] = hex_table[(error >> 4) & 0xf]; +  buf[2] = hex_table[error & 0xf]; +   +  put_packet(buf, sizeof(buf)); +} + +static void +put_ok(void) +{ +  const unsigned char buf[2] = "OK"; +  put_packet(buf, sizeof(buf)); +} + +/* + * Read memory and send the reply. + * We do it on the fly so that our packet size is effectively unlimited + */ +static void +read_memory(unsigned int addr, unsigned int nbytes) +{ +  int checksum = 0; +  gdb_putc('$'); + +  if ((addr & 0x3) == 0 && (nbytes & 0x3) == 0){	// word aligned +    union { +      unsigned int	i; +      unsigned char	c[4]; +    } u; + +    unsigned int *p = (unsigned int *) addr; +    unsigned int length = nbytes / 4; + +    for (unsigned int i = 0; i < length; i++){ +      u.i = p[i];	// do a word read +      checksum = put_hex8_checksum(u.c[0], checksum); +      checksum = put_hex8_checksum(u.c[1], checksum); +      checksum = put_hex8_checksum(u.c[2], checksum); +      checksum = put_hex8_checksum(u.c[3], checksum); +    } +  } +  else {						// byte aligned +    unsigned char *p = (unsigned char *) addr; +    for (unsigned int i = 0; i < nbytes; i++) +      checksum = put_hex8_checksum(p[i], checksum); +  } + +  put_packet_trailer(checksum); +} + +static unsigned int +get_unaligned_int(const unsigned char *p) +{ +  // we're bigendian +  return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]); +} + +static bool +write_memory(unsigned int addr, size_t nbytes, +	     const unsigned char *data) +{ +  if ((addr & 0x3) == 0 && (nbytes & 0x3) == 0){	// word-aligned dst +    unsigned int *dst = (unsigned int *) addr; +    size_t length = nbytes / 4; +    for (size_t i = 0; i < length; i++){ +      unsigned int t = get_unaligned_int(&data[4*i]); +      dst[i] = t;					// word writes +    } +  } +  else {						// non-word-aligned dst +    unsigned char *dst = (unsigned char *) addr; +    for (size_t i = 0; i < nbytes; i++){ +      dst[i] = data[i]; +    } +  } +  return true; +} + +void +gdbstub2_main_loop(void) +{ +  unsigned char inpkt[MAX_PACKET + 24]; +  unsigned char binary_data[MAX_PACKET/2] __attribute__((aligned (4))); + +  hal_uart_set_mode(UART_MODE_RAW); //tell UART HAL not to map \n to \r\n + +  while (1){ +    size_t	inpkt_len; +    bool ok = get_packet(inpkt, sizeof(inpkt), &inpkt_len); +    if (!ok){ +      gdb_putc('-'); +      continue; +    } +    gdb_putc('+'); + +    const unsigned char *buf = inpkt; +    const unsigned char *end = inpkt + inpkt_len; +    unsigned char ch; + +    if (!parse_char(&buf, end, &ch)){	// empty packet +      put_packet(0, 0); +      continue; +    } + +    unsigned int addr; +    unsigned int length; + +    switch(ch){ +    case 'm':		// m<addr>,<length>  -- read <length> bytes starting at <addr> +      if (!(parse_addr_length(&buf, end, &addr, &length) && expect_end(&buf, end))){ +	put_error(1); +      } +      else { +	read_memory(addr, length); +      } +      break; + +    case 'M':		// M<addr>,<length>:XX...  -- write <length> bytes starting at <addr> +			//   XX... is the data in hex +      if (!(parse_addr_length(&buf, end, &addr, &length) +	    && expect_char(&buf, end, ':') +	    && (end - buf) == 2 * length)){ +	put_error(1); +      } +      else { +	if (!hex_to_bin_array(binary_data, buf, length)) +	  put_error(2); +	else if (!write_memory(addr, length, binary_data)) +	  put_error(3); +	else +	  put_ok(); +      } +      break; + +    case 'X':		// X<addr>,<length>:XX...  -- write <length> bytes starting at <addr> +			//   XX... is the data in binary +      if (!(parse_addr_length(&buf, end, &addr, &length) +	    && expect_char(&buf, end, ':') +	    && (end - buf) == length)){ +	put_error(1); +      } +      else { +	if (!write_memory(addr, length, buf)) +	  put_error(3); +	else +	  put_ok(); +      } +      break; + +    case 'c':		// c<addr>	-- continue.  <addr> is the address to resume (goto). +      if (!(parse_number(&buf, end, &addr) +	    && expect_end(&buf, end))){ +	put_error(1); +      } +      else { +	typedef void (*fptr_t)(void); +	(*(fptr_t) addr)();	// most likely no return +      } +      break; +/* +    case 0x80: +      { +	unsigned char *output = binary_data;  // reuse +	size_t sizeof_output = sizeof(binary_data); +	size_t actual_olen; +	loader_parser(buf, end-buf, +		      output, sizeof_output, &actual_olen); +	put_packet(output, actual_olen); +      } +      break; +*/ +    default:		// unknown packet type +      put_packet(0, 0); +      break; +    } +  } +} diff --git a/firmware/microblaze/lib/gdbstub2.h b/firmware/microblaze/lib/gdbstub2.h new file mode 100644 index 000000000..15cdde939 --- /dev/null +++ b/firmware/microblaze/lib/gdbstub2.h @@ -0,0 +1,25 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_GDBSTUB_H +#define INCLUDED_GDBSTUB_H + +void gdbstub2_main_loop(void); + +#endif /* INCLUDED_GDBSTUB_H */ + diff --git a/firmware/microblaze/lib/hal_io.c b/firmware/microblaze/lib/hal_io.c index 58b1e681e..be4c570c7 100644 --- a/firmware/microblaze/lib/hal_io.c +++ b/firmware/microblaze/lib/hal_io.c @@ -18,9 +18,9 @@  // conditionalized on HAL_IO_USES_DBOARD_PINS && HAL_IO_USES_UART -#include "hal_io.h"  #include "memory_map.h"  #include "hal_uart.h" +#include "hal_io.h"  #include <stdbool.h>  #include <stdio.h>  #include <string.h> @@ -124,16 +124,29 @@ hal_finish(void)  // %c  inline int +fputchar(hal_uart_name_t u, int ch) +{ +  hal_uart_putc(u, ch); +  return ch; +} + +inline int  putchar(int ch)  { -  hal_uart_putc(ch); +  hal_uart_putc(DEFAULT_UART, ch);    return ch;  }  int +fgetchar(hal_uart_name_t u) +{ +  return hal_uart_getc(u); +} + +int  getchar(void)  { -  return hal_uart_getc(); +  return fgetchar(DEFAULT_UART);  }  #else	// nop all i/o @@ -172,34 +185,87 @@ getchar(void)  // \n  inline void  +fnewline(hal_uart_name_t u) +{ +  fputchar(u, '\n'); +} + +inline void  newline(void)  { -  putchar('\n'); +  fnewline(DEFAULT_UART);  }  int -putstr(const char *s) +fputstr(hal_uart_name_t u, const char *s)  {    while (*s) -    putchar(*s++); +    fputchar(u, *s++);    return 0;  }  int -puts(const char *s) +fnputstr(hal_uart_name_t u, const char *s, int len) +{ +  int x = 0; +  while (*s && (len > x++)) +    fputchar(u, *s++); + +  return x; +} + +int +putstr(const char *s)  { -  putstr(s); -  putchar('\n'); +  return fputstr(DEFAULT_UART, s); +} + +int +fputs(hal_uart_name_t u, const char *s) +{ +  fputstr(u, s); +  fputchar(u, '\n');    return 0;  } +int puts(const char *s) +{ +  return fputs(DEFAULT_UART, s); +} + +char * +fgets(hal_uart_name_t u, char * const s) +{ +  char *x = s; +  while((*x=(char)hal_uart_getc(u)) != '\n') x++; +  *x = 0; +  return s; +} + +int +fngets(hal_uart_name_t u, char * const s, int len) +{ +  char *x = s; +  while(((*x=(char)hal_uart_getc(u)) != '\n') && ((x-s) < len)) x++; +  *x = 0; +  return (x-s); +} + +int +fngets_timeout(hal_uart_name_t u, char * const s, int len) +{ +  char *x = s; + +  while(((*x=(char)hal_uart_getc_timeout(u)) != '\n') && (*x != -1) && ((x-s) < len)) x++; +  *x = 0; +  //printf("Returning from fngets() with string %d of length %d\n", s[0], x-s); +  return (x-s); +} +  char *  gets(char * const s)  { -	char *x = s; -	while((*x=(char)hal_uart_getc()) != '\n') x++; -	*x = 0; -	return s; +  return fgets(DEFAULT_UART, s);  } diff --git a/firmware/microblaze/lib/hal_io.h b/firmware/microblaze/lib/hal_io.h index c67d96c62..950f8d591 100644 --- a/firmware/microblaze/lib/hal_io.h +++ b/firmware/microblaze/lib/hal_io.h @@ -20,10 +20,15 @@  #define INCLUDED_HAL_IO_H  #include "memory_map.h" +#include "hal_uart.h"  void hal_io_init(void);  void hal_finish();  char *gets(char * const s); +int fputstr(hal_uart_name_t u, const char *s); +int fnputstr(hal_uart_name_t u, const char *s, int len); +int fngets(hal_uart_name_t u, char * const s, int len); +int fngets_timeout(hal_uart_name_t u, char * const s, int len);  /*   * ------------------------------------------------------------------------ diff --git a/firmware/microblaze/lib/hal_uart.c b/firmware/microblaze/lib/hal_uart.c index fe3b7515a..7836240fe 100644 --- a/firmware/microblaze/lib/hal_uart.c +++ b/firmware/microblaze/lib/hal_uart.c @@ -16,71 +16,105 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ +#include "memory_map.h"  #include "hal_uart.h"  #include "hal_io.h" -#include "memory_map.h" +#include "mdelay.h" -// First pass, no interrupts - -// Replaced with divisors.py which generates best divisor -//#define CALC_DIVISOR(rate) (WISHBONE_CLK_RATE / ((rate) * 16)) +//just to save you from going insane, note that firmware/FPGA UARTs [0-2] correspond to serial ports [1-3]. +//so in software, we refer to UART_DEBUG as UART0, but it transmits on pin TXD<1>. see the UART assignments in hal_uart.h.  #define NSPEEDS 6  #define	MAX_WB_DIV 4 +//if you're going to recalculate the divisors, it's just uart_clock_rate / baud_rate. +//uart_clock_rate is 50MHz for USRP2.  static const uint16_t -divisor_table[MAX_WB_DIV+1][NSPEEDS] = { -  {   2,   2,   2,   2,  2,  2},   // 0: can't happen -  { 651, 326, 163, 109, 54, 27 },  // 1: 100 MHz -  { 326, 163,  81,  54, 27, 14 },  // 2:  50 MHz -  { 217, 109,  54,  36, 18,  9 },  // 3:  33.3333 MHz -  { 163,  81,  41,  27, 14,  7 },  // 4:  25 MHz +divisor_table[NSPEEDS] = { +  5208,	//    9600 +  2604,	//   19200 +  1302,	//   38400 +  868,	//   57600 +  434,	//  115200 +  217	//  230400  }; -#define u uart_regs +static char uart_mode[4] = { +  [UART_DEBUG] = UART_MODE_ONLCR,  +  [UART_EXP] = UART_MODE_ONLCR,  +  [UART_GPS] = UART_MODE_ONLCR +}; -static char uart_mode = UART_MODE_ONLCR; +static char uart_speeds[4] = { +  [UART_DEBUG] = US_230400, +  [UART_EXP] = US_230400, +  [UART_GPS] = US_115200 +};  void -hal_uart_set_mode(int mode) +hal_uart_set_mode(hal_uart_name_t uart, int mode) +{ +  uart_mode[uart] = mode; +} + +void hal_uart_set_speed(hal_uart_name_t uart, hal_uart_speed_t speed)  { -  uart_mode = mode; +  uart_regs[uart].clkdiv = divisor_table[speed];  }  void  hal_uart_init(void)  { -	hal_uart_set_mode(UART_MODE_ONLCR); -  u->clkdiv = 217;  // 230400 bps +  for(int i = 0; i < 3; i++) { +  	hal_uart_set_mode(i, uart_mode[i]); +    hal_uart_set_speed(i, uart_speeds[i]); +  }  }  void -hal_uart_putc(int ch) +hal_uart_putc(hal_uart_name_t u, int ch)  { -  if (ch == '\n')// && (uart_mode == UART_MODE_ONLCR))		//map \n->\r\n if necessary -    hal_uart_putc('\r'); +  if ((ch == '\n') && (uart_mode[u] == UART_MODE_ONLCR))		//map \n->\r\n if necessary +    hal_uart_putc(u, '\r'); -  while (u->txlevel == 0)	 // wait for fifo to have space +  while (uart_regs[u].txlevel == 0)	 // wait for fifo to have space      ; -  u->txchar = ch; +  uart_regs[u].txchar = ch;  }  void -hal_uart_putc_nowait(int ch) +hal_uart_putc_nowait(hal_uart_name_t u, int ch)  { -  if (ch == '\n')// && (uart_mode == UART_MODE_ONLCR))		//map \n->\r\n if necessary -    hal_uart_putc('\r'); +  if ((ch == '\n') && (uart_mode[u] == UART_MODE_ONLCR))		//map \n->\r\n if necessary +    hal_uart_putc(u, '\r'); -  if(u->txlevel)   // If fifo has space -    u->txchar = ch; +  if(uart_regs[u].txlevel)   // If fifo has space +    uart_regs[u].txchar = ch;  }  int -hal_uart_getc(void) +hal_uart_getc(hal_uart_name_t u)  { -  while ((u->rxlevel) == 0)  // wait for data to be ready +  while ((uart_regs[u].rxlevel) == 0)  // wait for data to be ready      ; -  return u->rxchar; +  return uart_regs[u].rxchar;  } + +int  +hal_uart_getc_timeout(hal_uart_name_t u) +{ +  int timeout = 0; +  while (((uart_regs[u].rxlevel) == 0) && (timeout++ < HAL_UART_TIMEOUT_MS)) +    mdelay(1); +  return (timeout >= HAL_UART_TIMEOUT_MS) ? -1 : uart_regs[u].rxchar; //return -1 if nothing there, cause fngets to quit +} + +int hal_uart_rx_flush(hal_uart_name_t u) +{ +  char x; +  while(uart_regs[u].rxlevel) x = uart_regs[u].rxchar; +  return x; +} + diff --git a/firmware/microblaze/lib/hal_uart.h b/firmware/microblaze/lib/hal_uart.h index dfd73c323..758c8cb5e 100644 --- a/firmware/microblaze/lib/hal_uart.h +++ b/firmware/microblaze/lib/hal_uart.h @@ -25,29 +25,39 @@  #define	UART_MODE_RAW		0x0000	// no mapping on input or output  #define	UART_MODE_ONLCR	0x0001	// map \n to \r\n on output (default) -/* - * \brief Set uart mode - */ -void hal_uart_set_mode(int flags); +#define DEFAULT_UART UART_DEBUG //which UART printf, gets, etc. use -/*! - * \brief one-time call to init - */ -void hal_uart_init(void); +#define HAL_UART_TIMEOUT_MS 300  typedef enum { -  US_9600, -  US_19200, -  US_38400, -  US_57600, -  US_115200, -  US_230400, +  US_9600   = 0, +  US_19200  = 1, +  US_38400  = 2, +  US_57600  = 3, +  US_115200 = 4, +  US_230400 = 5  } hal_uart_speed_t;  typedef struct {    hal_uart_speed_t	speed;  } hal_uart_config_t; +typedef enum { +  UART_DEBUG = 0, +  UART_EXP   = 1, +  UART_GPS   = 2 +} hal_uart_name_t; + +/* + * \brief Set uart mode + */ +void hal_uart_set_mode(hal_uart_name_t uart, int flags); + +/*! + * \brief one-time call to init + */ +void hal_uart_init(void); +  /*!   * \brief Set uart parameters   *  Default is 115,200 bps, 8N1. @@ -62,17 +72,23 @@ void hal_uart_get_config(hal_uart_config_t *c);  /*!   * \brief Enqueue \p ch for output over serial port   */ -void hal_uart_putc(int ch); +void hal_uart_putc(hal_uart_name_t u, int ch);  /*!   * \brief Enqueue \p ch for output over serial port, silent fail if queue is full   */ -void hal_uart_putc_nowait(int ch); +void hal_uart_putc_nowait(hal_uart_name_t u, int ch);  /*   * \brief Blocking read of next char from serial port   */ -int hal_uart_getc(void); +int hal_uart_getc(hal_uart_name_t u); + +/* + * \brief Blocking read of next char from serial port with timeout + */ +int hal_uart_getc_timeout(hal_uart_name_t u); +int hal_uart_rx_flush(hal_uart_name_t u);  #endif /* INCLUDED_HAL_UART_H */ diff --git a/firmware/microblaze/lib/i2c.c b/firmware/microblaze/lib/i2c.c index 177341267..d230f462c 100644 --- a/firmware/microblaze/lib/i2c.c +++ b/firmware/microblaze/lib/i2c.c @@ -20,7 +20,6 @@  #include "memory_map.h"  #include "stdint.h"  #include <string.h> -#include "pic.h"  #include "nonstdio.h"  #define MAX_WB_DIV 4	// maximum wishbone divisor (from 100 MHz MASTER_CLK) @@ -37,18 +36,6 @@ static uint16_t prescaler_values[MAX_WB_DIV+1] = {    PRESCALER(4), // 4:  25 MHz  }; -//asynchronous (interrupt-driven) i2c state variables -volatile uint8_t i2c_buf[17]; //tx/rx data transfer buffer -volatile uint8_t *volatile i2c_bufptr = i2c_buf; //ptr to current position -volatile uint8_t i2c_len = 0; //length remaining in current transfer -volatile i2c_state_t i2c_state = I2C_STATE_IDLE; //current I2C transfer state -i2c_dir_t i2c_dir; //I2C transfer direction - -void  (*volatile i2c_callback)(void); //function pointer to i2c callback to be called when transaction is complete - -static void i2c_irq_handler(unsigned irq); -inline void i2c_async_err(void); -  void  i2c_init(void)  { @@ -64,8 +51,8 @@ i2c_init(void)    i2c_regs->ctrl = I2C_CTRL_EN; //| I2C_CTRL_IE;	// enable core -  // FIXME interrupt driven?   -  pic_register_handler(IRQ_I2C, i2c_irq_handler); +  //now this is done separately to maintain common code for async and sync +  //pic_register_handler(IRQ_I2C, i2c_irq_handler);  }  static inline void @@ -140,163 +127,3 @@ i2c_write(unsigned char i2c_addr, const unsigned char *buf, unsigned int len)    return false;  } -static void i2c_irq_handler(unsigned irq) { -//i2c state machine. - -  //printf("I2C irq handler\n"); -  //first let's make sure nothing is f'ed up -  //TODO: uncomment this error checking when we have some way to handle errors -//  if(((i2c_regs->cmd_status & I2C_ST_RXACK) != 0) && i2c_dir == I2C_DIR_WRITE) { //we got a NACK and we didn't send it -//    printf("\tNACK received\n"); -//    i2c_async_err(); -//    return; -//  }// else printf("\tACK received, proceeding\n"); - -  if(i2c_regs->cmd_status & I2C_ST_AL) {  -    printf("\tArbitration lost!\n"); -    i2c_async_err(); -    return; -  } - -  if(i2c_regs->cmd_status & I2C_ST_TIP) { -    //printf("\tI2C still busy in interrupt\n"); -    return; -  } - -  //now decide what to do -  switch(i2c_state) { - -  case I2C_STATE_IDLE: -    //this is an error. in idle state, we shouldn't be transferring data, and the fact that the IRQ fired is terrible bad. -    printf("AAAAAHHHHH INTERRUPT IN THE IDLE STATE AAAHHHHHHHHH\n"); -    i2c_async_err(); -    break; - -  case I2C_STATE_CONTROL_BYTE_SENT: //here we've sent the control byte, and we're either clocking data in or out now, but we haven't received a byte yet. -  case I2C_STATE_DATA:      //here we're sending/receiving data and if we're receiving there's data in the data reg - -    //if(i2c_state == I2C_STATE_DATA) printf("\tI2C in state DATA with dir=%d and len=%d\n", i2c_dir, i2c_len); -    //else printf("\tI2C in state CONTROL_BYTE_SENT with dir=%d and len=%d\n", i2c_dir, i2c_len); - -    if(i2c_dir == I2C_DIR_READ) { -      if(i2c_state == I2C_STATE_DATA) *(i2c_bufptr++) = i2c_regs->data; -      //printf("\tRead %x\n", *(i2c_bufptr-1)); -      //set up another data byte -      if(i2c_len > 1) //only one more byte to transfer -        i2c_regs->cmd_status = I2C_CMD_RD; -      else -        i2c_regs->cmd_status = I2C_CMD_RD | I2C_CMD_NACK | I2C_CMD_STOP; -    } -    else if(i2c_dir == I2C_DIR_WRITE) { -      //write a byte -      //printf("\tWriting %x\n", *i2c_bufptr); -      i2c_regs->data = *(i2c_bufptr++); -      if(i2c_len > 1) -        i2c_regs->cmd_status = I2C_CMD_WR; -      else { -        //printf("\tGenerating STOP\n"); -        i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_STOP; -      } -    }; -    i2c_len--; -    if(i2c_len == 0) i2c_state = I2C_STATE_LAST_BYTE; -    else i2c_state = I2C_STATE_DATA; //takes care of the addr_sent->data transition -    break; - - -  case I2C_STATE_LAST_BYTE: //here we've already sent the last read request and the last data is waiting for us. -    //printf("\tI2C in state LAST BYTE\n"); - -    if(i2c_dir == I2C_DIR_READ) { -      *(i2c_bufptr++) = i2c_regs->data; -      //printf("\tRead %x\n", *(i2c_bufptr-1)); -      i2c_state = I2C_STATE_DATA_READY; -    } else { -      i2c_state = I2C_STATE_IDLE; -    } -    i2c_regs->ctrl &= ~I2C_CTRL_IE; //disable interrupts until next time - -    if(i2c_callback) { -      i2c_callback(); //if we registered a callback, call it! -    } - -    break; - - -  default: //terrible things have happened. -    break; -  } - -} - -void i2c_register_callback(void (*volatile callback)(void)) { -  i2c_callback = callback; -} - -inline void i2c_async_err(void) { -  i2c_state = I2C_STATE_IDLE; -  i2c_regs->ctrl &= ~I2C_CTRL_IE; -  printf("I2C error\n"); -//TODO: set an error flag instead of just dropping things on the floor -  i2c_regs->cmd_status = I2C_CMD_STOP; -} - -bool i2c_async_read(uint8_t addr, unsigned int len) { -  //printf("Starting async read\n"); -  if(i2c_state != I2C_STATE_IDLE) return false; //sorry mario but your i2c is in another castle -  if(len == 0) return true; //just idiot-proofing -  if(len > sizeof(i2c_buf)) return false; - -  //disable I2C interrupts and clear pending interrupts on the I2C device -  i2c_regs->ctrl &= ~I2C_CTRL_IE; -  i2c_regs->cmd_status |= I2C_CMD_IACK; - -  i2c_len = len; -  i2c_dir = I2C_DIR_READ; -  i2c_bufptr = i2c_buf; -  //then set up the transfer by issuing the control byte -  i2c_regs->ctrl |= I2C_CTRL_IE; -  i2c_regs->data = (addr << 1) | 0x01; //7 bit addr and read bit -  i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; //generate start & start writing addr -  //update the state so the irq handler knows what's going on -  i2c_state = I2C_STATE_CONTROL_BYTE_SENT; -  return true; -} - -bool i2c_async_write(uint8_t addr, const uint8_t *buf, unsigned int len) { -  //printf("Starting async write\n"); -  if(i2c_state != I2C_STATE_IDLE) return false; //sorry mario but your i2c is in another castle -  if(len > sizeof(i2c_buf)) return false; - -  //disable I2C interrupts and clear pending interrupts on the I2C device -  i2c_regs->ctrl &= ~I2C_CTRL_IE; -  i2c_regs->cmd_status |= I2C_CMD_IACK; - -  //copy the buffer into our own if writing -  memcpy((void *)i2c_buf, buf, len); - -  i2c_len = len; -  i2c_dir = I2C_DIR_WRITE; -  i2c_bufptr = i2c_buf; -  //then set up the transfer by issuing the control byte -  i2c_regs->ctrl |= I2C_CTRL_IE; -  i2c_regs->data = (addr << 1) | 0x00; //7 bit addr and read bit -  i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; //generate start & start writing addr -  //update the state so the irq handler knows what's going on -  i2c_state = I2C_STATE_CONTROL_BYTE_SENT; - -  return true; -} - -//TODO: determine if it's better to read sequentially into the user's buffer, copy on transfer complete, or copy on request (shown below). probably best to copy on request. -bool i2c_async_data_ready(void *buf) { -  if(i2c_state == I2C_STATE_DATA_READY) { -    i2c_state = I2C_STATE_IDLE; -    memcpy(buf, (void *)i2c_buf, (i2c_bufptr - i2c_buf)); //TODO: not really comfortable with this -    //printf("Copying %d bytes to user buffer\n", i2c_bufptr-i2c_buf); -    return true; -  } -  return false; -} - - diff --git a/firmware/microblaze/lib/i2c.h b/firmware/microblaze/lib/i2c.h index 77129e922..6ff0e6982 100644 --- a/firmware/microblaze/lib/i2c.h +++ b/firmware/microblaze/lib/i2c.h @@ -22,36 +22,15 @@  #include <stdbool.h>  #include "stdint.h" -typedef enum { I2C_STATE_IDLE,  -               I2C_STATE_CONTROL_BYTE_SENT,  -               I2C_STATE_DATA,  -               I2C_STATE_LAST_BYTE,  -               I2C_STATE_DATA_READY,  -               I2C_STATE_ERROR  -             } i2c_state_t; - -typedef enum { I2C_DIR_WRITE=0, I2C_DIR_READ=1 } i2c_dir_t; -  void i2c_init(void);  bool i2c_read (unsigned char i2c_addr, unsigned char *buf, unsigned int len);  bool i2c_write(unsigned char i2c_addr, const unsigned char *buf, unsigned int len); -bool i2c_async_read(uint8_t addr, unsigned int len); -bool i2c_async_write(uint8_t addr, const uint8_t *buf, unsigned int len); -bool i2c_async_data_ready(void *); -//static void i2c_irq_handler(unsigned irq); -void i2c_register_callback(void (*callback)(void)); - -// Write 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. -// Which EEPROM is determined by i2c_addr.  See i2c_addr.h -  bool eeprom_write (int i2c_addr, int eeprom_offset, const void *buf, int len); -bool eeprom_write_async (int i2c_addr, int eeprom_offset, const void *buf, int len, void (*callback)(void));  // Read 24LC024 / 24LC025 EEPROM on motherboard or daughterboard.  // Which EEPROM is determined by i2c_addr.  See i2c_addr.h  bool eeprom_read (int i2c_addr, int eeprom_offset, void *buf, int len); -bool eeprom_read_async(int i2c_addr, int eeprom_offset, void *buf, int len, void (*callback)(void));  #endif /* INCLUDED_I2C_H */ diff --git a/firmware/microblaze/lib/i2c_async.c b/firmware/microblaze/lib/i2c_async.c new file mode 100644 index 000000000..05c4c3a09 --- /dev/null +++ b/firmware/microblaze/lib/i2c_async.c @@ -0,0 +1,206 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ +  + //i2c_async.c: asynchronous (interrupt-driven) routines for I2C. + //separated out here so we can have a small I2C lib for bootloader and + //retain interrupt-driven I2C for the main app. + +#include "memory_map.h" +#include "stdint.h" +#include <string.h> +#include "pic.h" +#include "nonstdio.h" +#include "i2c_async.h" +  + //asynchronous (interrupt-driven) i2c state variables +volatile uint8_t i2c_buf[17]; //tx/rx data transfer buffer +volatile uint8_t *volatile i2c_bufptr = i2c_buf; //ptr to current position +volatile uint8_t i2c_len = 0; //length remaining in current transfer +volatile i2c_state_t i2c_state = I2C_STATE_IDLE; //current I2C transfer state +i2c_dir_t i2c_dir; //I2C transfer direction + +void  (*volatile i2c_callback)(void); //function pointer to i2c callback to be called when transaction is complete +static void i2c_irq_handler(unsigned irq); +inline void i2c_async_err(void); + +void i2c_register_handler(void) { +    pic_register_handler(IRQ_I2C, i2c_irq_handler); +} + + +static void i2c_irq_handler(unsigned irq) { +//i2c state machine. + +  //printf("I2C irq handler\n"); +  //first let's make sure nothing is f'ed up +  //TODO: uncomment this error checking when we have some way to handle errors +//  if(((i2c_regs->cmd_status & I2C_ST_RXACK) != 0) && i2c_dir == I2C_DIR_WRITE) { //we got a NACK and we didn't send it +//    printf("\tNACK received\n"); +//    i2c_async_err(); +//    return; +//  }// else printf("\tACK received, proceeding\n"); + +  if(i2c_regs->cmd_status & I2C_ST_AL) {  +    printf("\tArbitration lost!\n"); +    i2c_async_err(); +    return; +  } + +  if(i2c_regs->cmd_status & I2C_ST_TIP) { +    //printf("\tI2C still busy in interrupt\n"); +    return; +  } + +  //now decide what to do +  switch(i2c_state) { + +  case I2C_STATE_IDLE: +    //this is an error. in idle state, we shouldn't be transferring data, and the fact that the IRQ fired is terrible bad. +    printf("AAAAAHHHHH INTERRUPT IN THE IDLE STATE AAAHHHHHHHHH\n"); +    i2c_async_err(); +    break; + +  case I2C_STATE_CONTROL_BYTE_SENT: //here we've sent the control byte, and we're either clocking data in or out now, but we haven't received a byte yet. +  case I2C_STATE_DATA:      //here we're sending/receiving data and if we're receiving there's data in the data reg + +    //if(i2c_state == I2C_STATE_DATA) printf("\tI2C in state DATA with dir=%d and len=%d\n", i2c_dir, i2c_len); +    //else printf("\tI2C in state CONTROL_BYTE_SENT with dir=%d and len=%d\n", i2c_dir, i2c_len); + +    if(i2c_dir == I2C_DIR_READ) { +      if(i2c_state == I2C_STATE_DATA) *(i2c_bufptr++) = i2c_regs->data; +      //printf("\tRead %x\n", *(i2c_bufptr-1)); +      //set up another data byte +      if(i2c_len > 1) //only one more byte to transfer +        i2c_regs->cmd_status = I2C_CMD_RD; +      else +        i2c_regs->cmd_status = I2C_CMD_RD | I2C_CMD_NACK | I2C_CMD_STOP; +    } +    else if(i2c_dir == I2C_DIR_WRITE) { +      //write a byte +      //printf("\tWriting %x\n", *i2c_bufptr); +      i2c_regs->data = *(i2c_bufptr++); +      if(i2c_len > 1) +        i2c_regs->cmd_status = I2C_CMD_WR; +      else { +        //printf("\tGenerating STOP\n"); +        i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_STOP; +      } +    }; +    i2c_len--; +    if(i2c_len == 0) i2c_state = I2C_STATE_LAST_BYTE; +    else i2c_state = I2C_STATE_DATA; //takes care of the addr_sent->data transition +    break; + + +  case I2C_STATE_LAST_BYTE: //here we've already sent the last read request and the last data is waiting for us. +    //printf("\tI2C in state LAST BYTE\n"); + +    if(i2c_dir == I2C_DIR_READ) { +      *(i2c_bufptr++) = i2c_regs->data; +      //printf("\tRead %x\n", *(i2c_bufptr-1)); +      i2c_state = I2C_STATE_DATA_READY; +    } else { +      i2c_state = I2C_STATE_IDLE; +    } +    i2c_regs->ctrl &= ~I2C_CTRL_IE; //disable interrupts until next time + +    if(i2c_callback) { +      i2c_callback(); //if we registered a callback, call it! +    } + +    break; + + +  default: //terrible things have happened. +    break; +  } + +} + +void i2c_register_callback(void (*volatile callback)(void)) { +  i2c_callback = callback; +} + +inline void i2c_async_err(void) { +  i2c_state = I2C_STATE_IDLE; +  i2c_regs->ctrl &= ~I2C_CTRL_IE; +  printf("I2C error\n"); +//TODO: set an error flag instead of just dropping things on the floor +  i2c_regs->cmd_status = I2C_CMD_STOP; +} + +bool i2c_async_read(uint8_t addr, unsigned int len) { +  //printf("Starting async read\n"); +  if(i2c_state != I2C_STATE_IDLE) return false; //sorry mario but your i2c is in another castle +  if(len == 0) return true; //just idiot-proofing +  if(len > sizeof(i2c_buf)) return false; + +  //disable I2C interrupts and clear pending interrupts on the I2C device +  i2c_regs->ctrl &= ~I2C_CTRL_IE; +  i2c_regs->cmd_status |= I2C_CMD_IACK; + +  i2c_len = len; +  i2c_dir = I2C_DIR_READ; +  i2c_bufptr = i2c_buf; +  //then set up the transfer by issuing the control byte +  i2c_regs->ctrl |= I2C_CTRL_IE; +  i2c_regs->data = (addr << 1) | 0x01; //7 bit addr and read bit +  i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; //generate start & start writing addr +  //update the state so the irq handler knows what's going on +  i2c_state = I2C_STATE_CONTROL_BYTE_SENT; +  return true; +} + +bool i2c_async_write(uint8_t addr, const uint8_t *buf, unsigned int len) { +  //printf("Starting async write\n"); +  if(i2c_state != I2C_STATE_IDLE) return false; //sorry mario but your i2c is in another castle +  if(len > sizeof(i2c_buf)) return false; + +  //disable I2C interrupts and clear pending interrupts on the I2C device +  i2c_regs->ctrl &= ~I2C_CTRL_IE; +  i2c_regs->cmd_status |= I2C_CMD_IACK; + +  //copy the buffer into our own if writing +  memcpy((void *)i2c_buf, buf, len); + +  i2c_len = len; +  i2c_dir = I2C_DIR_WRITE; +  i2c_bufptr = i2c_buf; +  //then set up the transfer by issuing the control byte +  i2c_regs->ctrl |= I2C_CTRL_IE; +  i2c_regs->data = (addr << 1) | 0x00; //7 bit addr and read bit +  i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; //generate start & start writing addr +  //update the state so the irq handler knows what's going on +  i2c_state = I2C_STATE_CONTROL_BYTE_SENT; + +  return true; +} + +//TODO: determine if it's better to read sequentially into the user's buffer, copy on transfer complete, or copy on request (shown below). probably best to copy on request. +bool i2c_async_data_ready(void *buf) { +  if(i2c_state == I2C_STATE_DATA_READY) { +    i2c_state = I2C_STATE_IDLE; +    memcpy(buf, (void *)i2c_buf, (i2c_bufptr - i2c_buf)); //TODO: not really comfortable with this +    //printf("Copying %d bytes to user buffer\n", i2c_bufptr-i2c_buf); +    return true; +  } +  return false; +} + diff --git a/firmware/microblaze/lib/i2c_async.h b/firmware/microblaze/lib/i2c_async.h new file mode 100644 index 000000000..e6095fca6 --- /dev/null +++ b/firmware/microblaze/lib/i2c_async.h @@ -0,0 +1,50 @@ +// +// Copyright 2010 Ettus Research LLC +// +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_I2C_ASYNC_H +#define INCLUDED_I2C_ASYNC_H + +#include <stdbool.h> +#include "stdint.h" + +typedef enum { I2C_STATE_IDLE,  +               I2C_STATE_CONTROL_BYTE_SENT,  +               I2C_STATE_DATA,  +               I2C_STATE_LAST_BYTE,  +               I2C_STATE_DATA_READY,  +               I2C_STATE_ERROR  +             } i2c_state_t; + +typedef enum { I2C_DIR_WRITE=0, I2C_DIR_READ=1 } i2c_dir_t; + +bool i2c_async_read(uint8_t addr, unsigned int len); +bool i2c_async_write(uint8_t addr, const uint8_t *buf, unsigned int len); +bool i2c_async_data_ready(void *); +//static void i2c_irq_handler(unsigned irq); +void i2c_register_callback(void (*callback)(void)); +void i2c_register_handler(void); + +// Write 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. +// Which EEPROM is determined by i2c_addr.  See i2c_addr.h + +bool eeprom_write_async (int i2c_addr, int eeprom_offset, const void *buf, int len, void (*callback)(void)); +bool eeprom_read_async(int i2c_addr, int eeprom_offset, void *buf, int len, void (*callback)(void)); + +#endif /* INCLUDED_I2C_ASYNC_H */ diff --git a/firmware/microblaze/lib/ihex.c b/firmware/microblaze/lib/ihex.c new file mode 100644 index 000000000..97ecf73b6 --- /dev/null +++ b/firmware/microblaze/lib/ihex.c @@ -0,0 +1,57 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include "ihex.h" +#include <ctype.h> //man that pulls in a lot of shit + +//this is not safe and you should run isxdigit beforehand +uint8_t asc2nibble(char input) { +	if(input > 'Z') return input - 'W'; +	else if(input > '9') return input - '7'; +	else return input - '0'; +} + +int ihex_parse(char input[], ihex_record_t *record) { +	//given a NULL-TERMINATED input string (use gets()) in I16HEX format, write the binary record in record. return 0 on success. + +	uint8_t inputlen; +	uint8_t t, i, checksum_calc=0, checksum_read; + +	//first check for ":" leading character +	if(input[0] != ':') return -1; + +	//then check the string for only valid ASCII ['0'-'F'] +	inputlen=1; +	while(input[inputlen]) { +		if( !isxdigit(input[inputlen++]) ) return -2; +	} + +	//then read the length. +	record->length = (asc2nibble(input[1]) << 4) + asc2nibble(input[2]); +	if(input[(record->length<<1) + 11] != 0) return -3; //if we're missing a null terminator in the right place + +	//then read the address. +	record->addr = (asc2nibble(input[3]) << 12) + (asc2nibble(input[4]) << 8) + (asc2nibble(input[5]) << 4) + asc2nibble(input[6]); + +	//then read the record type. +	record->type = (asc2nibble(input[7]) << 4) + asc2nibble(input[8]); +//	if(record->type > 4) return -4; + +	//then read the data, which goes from input[9] to input[9+length*2]. +	for(i=0; i < record->length; i++) { +		t = 9 + (i<<1); +		record->data[i] = (asc2nibble(input[t]) << 4) + (asc2nibble(input[t + 1])); +		checksum_calc += record->data[i]; //might as well keep a running checksum as we read +	} +	checksum_calc += record->length + record->type + (record->addr >> 8) + (record->addr & 0xFF); //get the rest of the data into that checksum +	checksum_calc = ~checksum_calc + 1;	//checksum is 2's complement + +	//now read the checksum of the record +	checksum_read = (asc2nibble(input[9 + (record->length<<1)]) << 4) + asc2nibble(input[10 + (record->length<<1)]); +	if(checksum_calc != checksum_read) return -5; //compare 'em + +	return 0; +} diff --git a/firmware/microblaze/lib/ihex.h b/firmware/microblaze/lib/ihex.h new file mode 100644 index 000000000..9f471fbe2 --- /dev/null +++ b/firmware/microblaze/lib/ihex.h @@ -0,0 +1,18 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + */ + +#include <stdint.h> +#include <stddef.h> + +typedef struct { +	uint8_t type; +	size_t length; +	uint32_t addr; +	uint8_t *data; +} ihex_record_t; + + +int ihex_parse(char input[], ihex_record_t *record); diff --git a/firmware/microblaze/lib/u2_init.c b/firmware/microblaze/lib/u2_init.c index 8d666b76b..099aafe83 100644 --- a/firmware/microblaze/lib/u2_init.c +++ b/firmware/microblaze/lib/u2_init.c @@ -23,6 +23,7 @@  #include "buffer_pool.h"  #include "hal_uart.h"  #include "i2c.h" +#include "i2c_async.h"  #include "mdelay.h"  #include "clocks.h"  #include "usrp2/fw_common.h" @@ -59,6 +60,7 @@ u2_init(void)    // init i2c so we can read our rev    pic_init();	// progammable interrupt controller    i2c_init(); +  i2c_register_handler(); //for using async I2C    hal_enable_ints();    get_hw_rev(); diff --git a/firmware/microblaze/lib/udp_fw_update.h b/firmware/microblaze/lib/udp_fw_update.h new file mode 100644 index 000000000..d25525bd2 --- /dev/null +++ b/firmware/microblaze/lib/udp_fw_update.h @@ -0,0 +1,71 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "net_common.h" + +#define USRP2_UDP_UPDATE_PORT 49154 + +typedef enum { +  USRP2_FW_UPDATE_ID_WAT = ' ', + +  USRP2_FW_UPDATE_ID_OHAI_LOL = 'a', +  USRP2_FW_UPDATE_ID_OHAI_OMG = 'A', + +  USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = 'f', +  USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = 'F', + +  USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = 'e', +  USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = 'E', + +  USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = 'd', +  USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = 'D', +  USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = 'B', + +  USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = 'w', +  USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = 'W', + +  USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = 'r', +  USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = 'R', + +  USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = 's', +  USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = 'S', + +  USRP2_FW_UPDATE_ID_KTHXBAI = '~' + +} usrp2_fw_update_id_t; + +typedef struct { +  uint32_t proto_ver; +  uint32_t id; +  uint32_t seq; +  union { +      uint32_t ip_addr; +    struct { +      uint32_t flash_addr; +      uint32_t length; +      uint8_t  data[256]; +    } flash_args; +    struct { +      uint32_t sector_size_bytes; +      uint32_t memory_size_bytes; +    } flash_info_args; +  } data; +} usrp2_fw_update_data_t; + +void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, +                                 unsigned char *payload, int payload_len); | 
