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