diff options
26 files changed, 864 insertions, 930 deletions
diff --git a/firmware/octoclock/CMakeLists.txt b/firmware/octoclock/CMakeLists.txt index 80df0e9eb..0f2af35e9 100644 --- a/firmware/octoclock/CMakeLists.txt +++ b/firmware/octoclock/CMakeLists.txt @@ -31,7 +31,7 @@ if(NOT DEFINED PROGRAMMER)  endif(NOT DEFINED PROGRAMMER)  if(OCTOCLOCK_DEBUG) -    message(STATUS "Debug enabled. Interrupts will be disabled.") +    message(STATUS "Debug enabled.")      add_definitions(-DDEBUG)  endif(OCTOCLOCK_DEBUG) diff --git a/firmware/octoclock/bootloader/CMakeLists.txt b/firmware/octoclock/bootloader/CMakeLists.txt index 04bcfc492..7623a8698 100644 --- a/firmware/octoclock/bootloader/CMakeLists.txt +++ b/firmware/octoclock/bootloader/CMakeLists.txt @@ -49,7 +49,7 @@ add_custom_target(  add_custom_target(      upload_bootloader -    ${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xFF:m -U flash:w:octoclock_bootloader.hex:i +    ${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xEF:m -U flash:w:octoclock_bootloader.hex:i      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}      DEPENDS octoclock_bootloader_hex      COMMENT "Uploading OctoClock bootloader to device with ${PROGRAMMER}" diff --git a/firmware/octoclock/bootloader/main.c b/firmware/octoclock/bootloader/main.c index 5e2e6f17e..09f740d31 100644 --- a/firmware/octoclock/bootloader/main.c +++ b/firmware/octoclock/bootloader/main.c @@ -23,13 +23,13 @@  #include <avr/eeprom.h>  #include <avr/io.h>  #include <avr/pgmspace.h> +#include <avr/wdt.h> +#include <avrlibdefs.h>  #include <octoclock.h>  #include <debug.h>  #include <network.h> -#include <util/delay.h> -  #include <net/enc28j60.h>  #include "octoclock/common.h" @@ -40,10 +40,19 @@   */  #define TIME_PASSED (TCNT1 > (TIMER1_ONE_SECOND*5) || (TIFR & _BV(TOV1))) -//States -static bool received_cmd = false; -static bool done_burning = false; +/* + * States + */ +static bool received_cmd = false; // Received "PREPARE_FW_BURN_CMD" signal +static bool done_burning = false; // Received "FINALIZE_BURNING_CMD" signal +static bool app_checked  = false; // Ran validation check on firmware +/* + * After new firmware is burned onto the device, the bootloader calculates its + * CRC and burns it into the EEPROM. When the device boots, this CRC is used + * to validate the firmware before loading it. This struct represents how the + * information is stored in the EEPROM. + */  typedef struct {      uint16_t fw_len;      uint16_t fw_crc; @@ -51,15 +60,22 @@ typedef struct {  static crc_info_t crc_info; +/* + * What actually burns the firmware onto the device. + * + * Source: http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__boot.html + */  static void boot_program_page(uint8_t *buf, uint16_t page){ -    uint16_t i; +    // Disable interrupts +    uint8_t sreg = SREG; +    cli();      eeprom_busy_wait();      boot_page_erase(page);      boot_spm_busy_wait(); // Wait until the memory is erased. -    for(i = 0; i < SPM_PAGESIZE; i += 2){ +    for(uint16_t i = 0; i < SPM_PAGESIZE; i += 2){          // Set up little-endian word.          uint16_t w = *buf++;          w += ((*buf++) << 8); @@ -68,36 +84,31 @@ static void boot_program_page(uint8_t *buf, uint16_t page){      }      boot_page_write(page); // Store buffer in flash page. -    boot_spm_busy_wait(); // Wait until the memory is written. +    boot_spm_busy_wait();  // Wait until the memory is written.      // Reenable RWW-section again. We need this if we want to jump back      // to the application after bootloading.      boot_rww_enable(); + +    // Restore interrupt state +    SREG = sreg; +    sei();  } +/* + * Load firmware at given address into packet to send to host. + */  static void read_firmware(uint16_t addr, octoclock_packet_t *pkt_out){      for(size_t i = 0; i < SPM_PAGESIZE; i++){          pkt_out->data[i] = pgm_read_byte(addr+i);      }  } -void handle_udp_query_packet( -    struct socket_address src, struct socket_address dst, -    unsigned char *payload, int payload_len -){ -    const octoclock_packet_t *pkt_in = (octoclock_packet_t*)payload; - -    //Respond to query packets -    if(pkt_in->code == OCTOCLOCK_QUERY_CMD){ -        octoclock_packet_t pkt_out; -        pkt_out.proto_ver = OCTOCLOCK_BOOTLOADER_PROTO_VER; -        pkt_out.sequence = pkt_in->sequence; -        pkt_out.code = OCTOCLOCK_QUERY_ACK; -        pkt_out.len = 0; -        send_udp_pkt(OCTOCLOCK_UDP_CTRL_PORT, src, (void*)&pkt_out, sizeof(octoclock_packet_t)); -    } -} - +/* + * Calculate the CRC of the current firmware. + * + * Adapted from _crc16_update in <util/crc16.h>. + */  static void calculate_crc(uint16_t *crc, uint16_t len){      *crc = 0xFFFF; @@ -110,6 +121,11 @@ static void calculate_crc(uint16_t *crc, uint16_t len){      }  } +/* + * Calculate the CRC of the current firmware. If it matches the + * CRC burned into the EEPROM, the firmware is considered valid, + * and the bootloader can load it. + */  static bool valid_app(){      crc_info_t crc_eeprom_info;      eeprom_read_block(&crc_eeprom_info, (void*)OCTOCLOCK_EEPROM_APP_LEN, 4); @@ -118,6 +134,26 @@ static bool valid_app(){      return (crc_info.fw_crc == crc_eeprom_info.fw_crc);  } +/* + * UDP handlers + */ +void handle_udp_query_packet( +    struct socket_address src, struct socket_address dst, +    unsigned char *payload, int payload_len +){ +    const octoclock_packet_t *pkt_in = (octoclock_packet_t*)payload; + +    // Respond to uhd::device::find(), identify as bootloader +    if(pkt_in->code == OCTOCLOCK_QUERY_CMD){ +        octoclock_packet_t pkt_out; +        pkt_out.proto_ver = OCTOCLOCK_BOOTLOADER_PROTO_VER; +        pkt_out.sequence = pkt_in->sequence; +        pkt_out.code = OCTOCLOCK_QUERY_ACK; +        pkt_out.len = 0; +        send_udp_pkt(OCTOCLOCK_UDP_CTRL_PORT, src, (void*)&pkt_out, sizeof(octoclock_packet_t)); +    } +} +  void handle_udp_fw_packet(      struct socket_address src, struct socket_address dst,      unsigned char *payload, int payload_len @@ -132,24 +168,27 @@ void handle_udp_fw_packet(          case PREPARE_FW_BURN_CMD:              received_cmd = true;              done_burning = false; +            crc_info.fw_crc = pkt_in->crc;              crc_info.fw_len = pkt_in->len;              pkt_out.code = FW_BURN_READY_ACK;              break; +        // Burn firmware sent from the host          case FILE_TRANSFER_CMD:              boot_program_page(pkt_in->data, pkt_in->addr);              pkt_out.code = FILE_TRANSFER_ACK; +            pkt_out.addr = pkt_in->addr;              break; +        // Send firmware back to the host for verification          case READ_FW_CMD:              pkt_out.code = READ_FW_ACK;              read_firmware(pkt_in->addr, &pkt_out);              break; +        // Calculate the CRC of the new firmware and finish          case FINALIZE_BURNING_CMD: -            //With stuff verified, burn CRC info into EEPROM              done_burning = true; -            calculate_crc(&(crc_info.fw_crc), crc_info.fw_len);              eeprom_write_block(&crc_info, (void*)OCTOCLOCK_EEPROM_APP_LEN, 4);              pkt_out.code = FINALIZE_BURNING_ACK;              break; @@ -170,11 +209,12 @@ void handle_udp_eeprom_packet(      pkt_out.sequence = pkt_in->sequence;      pkt_out.len = 0; +    // Restore OctoClock's EEPROM to factory state      if(pkt_in->proto_ver == OCTOCLOCK_FW_COMPAT_NUM){          switch(pkt_in->code){              case CLEAR_EEPROM_CMD:                  received_cmd = true; -                uint8_t blank_eeprom[103]; +                uint8_t blank_eeprom[103]; // 103 is offset of CRC info                  memset(blank_eeprom, 0xFF, 103);                  eeprom_write_block(blank_eeprom, 0, 103);                  pkt_out.code = CLEAR_EEPROM_ACK; @@ -189,28 +229,45 @@ void handle_udp_eeprom_packet(  int main(void){ -    asm("cli"); +    // Disable watchdog timer +    wdt_disable(); + +    // Give interrupts to bootloader +    MCUCR = (1<<IVCE); +    MCUCR = (1<<IVSEL); +    cli(); -    //Initialization +    // Atmega128      setup_atmel_io_ports(); + +    // Start timer +    TIMER1_INIT(); + +    // Ethernet stack      network_init(); -    init_udp_listeners();      register_udp_listener(OCTOCLOCK_UDP_CTRL_PORT, handle_udp_query_packet);      register_udp_listener(OCTOCLOCK_UDP_FW_PORT, handle_udp_fw_packet);      register_udp_listener(OCTOCLOCK_UDP_EEPROM_PORT, handle_udp_eeprom_packet); -    //Turn LED's on to show we're in the bootloader +    // Turn LED's on to show we're in the bootloader      PORTC |= 0x20;      PORTC |= (0x20<<1);      PORTC |= (0x20<<2); -    TIMER1_INIT(); -    bool app_checked = false; - +    /* +     * This loop determines whether the OctoClock will remain in its bootloader +     * state or if it will load the main firmware. After five seconds, it will +     * check to see if valid firmware is installed. If so, it will immediately +     * load it. Otherwise, it will remain here until firmware is installed. +     * +     * This process can be stopped by an instruction from the firmware burner +     * utility, at which point the OctoClock will remain in bootloader state until +     * instructed by the utility to exit the loop and load the new firmware. +     */      while(true){          if(done_burning){              if(valid_app()) break; -            else done_burning = false; //Burning somehow failed and wasn't caught +            else done_burning = false; // Burning somehow failed and wasn't caught          }          if(!app_checked && !received_cmd && TIME_PASSED){              app_checked = true; @@ -220,16 +277,18 @@ int main(void){          network_check();      } -    //Turn LED's off before moving to application +    // Turn LED's off before moving to application      PORTC &= ~0x20;      PORTC &= ~(0x20<<1);      PORTC &= ~(0x20<<2);      /* -     * Whether the bootloader reaches here through five seconds of inactivity -     * or after a firmware burn just finished, it can be assumed that the application -     * is valid. +     * At this point, the bootloader has determined that there is valid +     * firmware installed on the device and that it is OK to load it.       */ +    TIMER1_DISABLE(); +    MCUCR = (1<<IVCE); +    MCUCR = 0; +    cli();      asm("jmp 0000"); -    return 0; //Will never get here, but AVR-GCC needs it  } diff --git a/firmware/octoclock/include/clkdist.h b/firmware/octoclock/include/clkdist.h index 633df9ddf..357daf37b 100644 --- a/firmware/octoclock/include/clkdist.h +++ b/firmware/octoclock/include/clkdist.h @@ -23,7 +23,7 @@  #include <octoclock.h>  typedef enum { -    Reg0, Reg1, Reg2, Reg3, Reg4, Reg5, Reg6, Reg7, +    Reg0=0, Reg1, Reg2, Reg3, Reg4, Reg5, Reg6, Reg7,      Reg8_Status_Control,      Read_Command=0xE,      RAM_EEPROM_Unlock=0x1F, @@ -46,6 +46,8 @@ void reset_TI_CDCE18005(void);  uint32_t get_TI_CDCE18005(CDCE18005 which_register); +void set_TI_CDCE18005(CDCE18005 which_register, uint32_t bits); +  bool check_TI_CDCE18005(TI_Input_10_MHz which_input, CDCE18005 which_register);  #endif /* _CLKDIST_H_ */ diff --git a/firmware/octoclock/include/debug.h b/firmware/octoclock/include/debug.h index ee0618bc6..1f69896f0 100644 --- a/firmware/octoclock/include/debug.h +++ b/firmware/octoclock/include/debug.h @@ -66,8 +66,8 @@                               DEBUG_LOG_HEX(((uint8_t*)&num)[0]);  #define DEBUG_LOG_INT(num) DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[3]); \ -                           DEBUG_LOG_HEX(((uint8_t*)&num)[2]); \ -                           DEBUG_LOG_HEX(((uint8_t*)&num)[1]); \ +                           DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[2]); \ +                           DEBUG_LOG_HEX_NNL(((uint8_t*)&num)[1]); \                             DEBUG_LOG_HEX(((uint8_t*)&num)[0]);  #else diff --git a/firmware/octoclock/include/gpsdo.h b/firmware/octoclock/include/gpsdo.h index fc7d87324..df0440404 100644 --- a/firmware/octoclock/include/gpsdo.h +++ b/firmware/octoclock/include/gpsdo.h @@ -27,6 +27,4 @@ gpsdo_cache_state_t gpsdo_state;  void send_gpsdo_cmd(char* buf, uint8_t size); -void process_gpsdo_output(void); -  #endif /* _GPSDO_H_ */ diff --git a/firmware/octoclock/include/net/enc28j60.h b/firmware/octoclock/include/net/enc28j60.h index 463303f3c..dc58b451b 100644 --- a/firmware/octoclock/include/net/enc28j60.h +++ b/firmware/octoclock/include/net/enc28j60.h @@ -1,299 +1,276 @@ -/*! \file enc28j60.h \brief Microchip ENC28J60 Ethernet Interface Driver. */ -//***************************************************************************** -// -// File Name	: 'enc28j60.h' -// Title		: Microchip ENC28J60 Ethernet Interface Driver -// Author		: Pascal Stang (c)2005 -// Created		: 9/22/2005 -// Revised		: 9/22/2005 -// Version		: 0.1 -// Target MCU	: Atmel AVR series -// Editor Tabs	: 4 -// -///	\ingroup network -///	\defgroup enc28j60 Microchip ENC28J60 Ethernet Interface Driver (enc28j60.c) -///	\code #include "net/enc28j60.h" \endcode -///	\par Overview -///		This driver provides initialization and transmit/receive -///	functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. -/// This chip is novel in that it is a full MAC+PHY interface all in a 28-pin -/// chip, using an SPI interface to the host processor. -/// -// -//***************************************************************************** -//@{ +/* + * Copyright 2015 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 ENC28J60_H -#define ENC28J60_H +#ifndef _NET_ENC28J60_H_ +#define _NET_ENC28J60_H_ -#include <avrlibdefs.h> +#include <avr/io.h> -#include "enc28j60conf.h" +#define SPI_DDR  DDRB +#define SPI_PORT PORTB +#define SPI_CS   0 +#define SPI_MOSI 2 +#define SPI_MISO 3 +#define SPI_SCK  1 -// ENC28J60 Control Registers -// Control register definitions are a combination of address, -// bank number, and Ethernet/MAC/PHY indicator bits. -// - Register address	(bits 0-4) -// - Bank number		(bits 5-6) -// - MAC/PHY indicator	(bit 7) -#define ADDR_MASK	0x1F -#define BANK_MASK	0x60 -#define SPRD_MASK	0x80 -// All-bank registers -#define EIE			0x1B -#define EIR			0x1C -#define ESTAT		0x1D -#define ECON2		0x1E -#define ECON1		0x1F -// Bank 0 registers -#define ERDPTL		(0x00|0x00) -#define ERDPTH		(0x01|0x00) -#define EWRPTL		(0x02|0x00) -#define EWRPTH		(0x03|0x00) -#define ETXSTL		(0x04|0x00) -#define ETXSTH		(0x05|0x00) -#define ETXNDL		(0x06|0x00) -#define ETXNDH		(0x07|0x00) -#define ERXSTL		(0x08|0x00) -#define ERXSTH		(0x09|0x00) -#define ERXNDL		(0x0A|0x00) -#define ERXNDH		(0x0B|0x00) -#define ERXRDPTL	(0x0C|0x00) -#define ERXRDPTH	(0x0D|0x00) -#define ERXWRPTL	(0x0E|0x00) -#define ERXWRPTH	(0x0F|0x00) -#define EDMASTL		(0x10|0x00) -#define EDMASTH		(0x11|0x00) -#define EDMANDL		(0x12|0x00) -#define EDMANDH		(0x13|0x00) -#define EDMADSTL	(0x14|0x00) -#define EDMADSTH	(0x15|0x00) -#define EDMACSL		(0x16|0x00) -#define EDMACSH		(0x17|0x00) -// Bank 1 registers -#define EHT0		(0x00|0x20) -#define EHT1		(0x01|0x20) -#define EHT2		(0x02|0x20) -#define EHT3		(0x03|0x20) -#define EHT4		(0x04|0x20) -#define EHT5		(0x05|0x20) -#define EHT6		(0x06|0x20) -#define EHT7		(0x07|0x20) -#define EPMM0		(0x08|0x20) -#define EPMM1		(0x09|0x20) -#define EPMM2		(0x0A|0x20) -#define EPMM3		(0x0B|0x20) -#define EPMM4		(0x0C|0x20) -#define EPMM5		(0x0D|0x20) -#define EPMM6		(0x0E|0x20) -#define EPMM7		(0x0F|0x20) -#define EPMCSL		(0x10|0x20) -#define EPMCSH		(0x11|0x20) -#define EPMOL		(0x14|0x20) -#define EPMOH		(0x15|0x20) -#define EWOLIE		(0x16|0x20) -#define EWOLIR		(0x17|0x20) -#define ERXFCON		(0x18|0x20) -#define EPKTCNT		(0x19|0x20) -// Bank 2 registers -#define MACON1		(0x00|0x40|0x80) -#define MACON2		(0x01|0x40|0x80) -#define MACON3		(0x02|0x40|0x80) -#define MACON4		(0x03|0x40|0x80) -#define MABBIPG		(0x04|0x40|0x80) -#define MAIPGL		(0x06|0x40|0x80) -#define MAIPGH		(0x07|0x40|0x80) -#define MACLCON1	(0x08|0x40|0x80) -#define MACLCON2	(0x09|0x40|0x80) -#define MAMXFLL		(0x0A|0x40|0x80) -#define MAMXFLH		(0x0B|0x40|0x80) -#define MAPHSUP		(0x0D|0x40|0x80) -#define MICON		(0x11|0x40|0x80) -#define MICMD		(0x12|0x40|0x80) -#define MIREGADR	(0x14|0x40|0x80) -#define MIWRL		(0x16|0x40|0x80) -#define MIWRH		(0x17|0x40|0x80) -#define MIRDL		(0x18|0x40|0x80) -#define MIRDH		(0x19|0x40|0x80) -// Bank 3 registers -#define MAADR1		(0x00|0x60|0x80) -#define MAADR0		(0x01|0x60|0x80) -#define MAADR3		(0x02|0x60|0x80) -#define MAADR2		(0x03|0x60|0x80) -#define MAADR5		(0x04|0x60|0x80) -#define MAADR4		(0x05|0x60|0x80) -#define EBSTSD		(0x06|0x60) -#define EBSTCON		(0x07|0x60) -#define EBSTCSL		(0x08|0x60) -#define EBSTCSH		(0x09|0x60) -#define MISTAT		(0x0A|0x60|0x80) -#define EREVID		(0x12|0x60) -#define ECOCON		(0x15|0x60) -#define EFLOCON		(0x17|0x60) -#define EPAUSL		(0x18|0x60) -#define EPAUSH		(0x19|0x60) -// PHY registers -#define PHCON1		0x00 -#define PHSTAT1		0x01 -#define PHHID1		0x02 -#define PHHID2		0x03 -#define PHCON2		0x10 -#define PHSTAT2		0x11 -#define PHIE		0x12 -#define PHIR		0x13 -#define PHLCON		0x14 +// Register Masks +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define SPRD_MASK 0x80 -// ENC28J60 EIE Register Bit Definitions -#define EIE_INTIE		0x80 -#define EIE_PKTIE		0x40 -#define EIE_DMAIE		0x20 -#define EIE_LINKIE		0x10 -#define EIE_TXIE		0x08 -#define EIE_WOLIE		0x04 -#define EIE_TXERIE		0x02 -#define EIE_RXERIE		0x01 -// ENC28J60 EIR Register Bit Definitions -#define EIR_PKTIF		0x40 -#define EIR_DMAIF		0x20 -#define EIR_LINKIF		0x10 -#define EIR_TXIF		0x08 -#define EIR_WOLIF		0x04 -#define EIR_TXERIF		0x02 -#define EIR_RXERIF		0x01 -// ENC28J60 ESTAT Register Bit Definitions -#define ESTAT_INT		0x80 -#define ESTAT_LATECOL	0x10 -#define ESTAT_RXBUSY	0x04 -#define ESTAT_TXABRT	0x02 -#define ESTAT_CLKRDY	0x01 -// ENC28J60 ECON2 Register Bit Definitions -#define ECON2_AUTOINC	0x80 -#define ECON2_PKTDEC	0x40 -#define ECON2_PWRSV		0x20 -#define ECON2_VRPS		0x08 -// ENC28J60 ECON1 Register Bit Definitions -#define ECON1_TXRST		0x80 -#define	ECON1_RXRST		0x40 -#define ECON1_DMAST		0x20 -#define ECON1_CSUMEN	0x10 -#define ECON1_TXRTS		0x08 -#define	ECON1_RXEN		0x04 -#define ECON1_BSEL1		0x02 -#define ECON1_BSEL0		0x01 -// ENC28J60 MACON1 Register Bit Definitions -#define MACON1_LOOPBK	0x10 -#define MACON1_TXPAUS	0x08 -#define MACON1_RXPAUS	0x04 -#define MACON1_PASSALL	0x02 -#define MACON1_MARXEN	0x01 -// ENC28J60 MACON2 Register Bit Definitions -#define MACON2_MARST	0x80 -#define MACON2_RNDRST	0x40 -#define MACON2_MARXRST	0x08 -#define MACON2_RFUNRST	0x04 -#define MACON2_MATXRST	0x02 -#define MACON2_TFUNRST	0x01 -// ENC28J60 MACON3 Register Bit Definitions -#define MACON3_PADCFG2	0x80 -#define MACON3_PADCFG1	0x40 -#define MACON3_PADCFG0	0x20 -#define MACON3_TXCRCEN	0x10 -#define MACON3_PHDRLEN	0x08 -#define MACON3_HFRMLEN	0x04 -#define MACON3_FRMLNEN	0x02 -#define MACON3_FULDPX	0x01 -// ENC28J60 MICMD Register Bit Definitions -#define MICMD_MIISCAN	0x02 -#define MICMD_MIIRD		0x01 -// ENC28J60 MISTAT Register Bit Definitions -#define MISTAT_NVALID	0x04 -#define MISTAT_SCAN		0x02 -#define MISTAT_BUSY		0x01 -// ENC28J60 PHY PHCON1 Register Bit Definitions -#define	PHCON1_PRST		0x8000 -#define	PHCON1_PLOOPBK	0x4000 -#define	PHCON1_PPWRSV	0x0800 -#define	PHCON1_PDPXMD	0x0100 -// ENC28J60 PHY PHSTAT1 Register Bit Definitions -#define	PHSTAT1_PFDPX	0x1000 -#define	PHSTAT1_PHDPX	0x0800 -#define	PHSTAT1_LLSTAT	0x0004 -#define	PHSTAT1_JBSTAT	0x0002 -// ENC28J60 PHY PHCON2 Register Bit Definitions -#define PHCON2_FRCLINK	0x4000 -#define PHCON2_TXDIS	0x2000 -#define PHCON2_JABBER	0x0400 -#define PHCON2_HDLDIS	0x0100 +// All Banks Registers +#define EIE      0x1B  +#define EIR      0x1C +#define ESTAT    0x1D +#define ECON2    0x1E +#define ECON1    0x1F -// ENC28J60 Packet Control Byte Bit Definitions -#define PKTCTRL_PHUGEEN		0x08 -#define PKTCTRL_PPADEN		0x04 -#define PKTCTRL_PCRCEN		0x02 -#define PKTCTRL_POVERRIDE	0x01 +// Bank 0 Registers +#define ERDPTL   0x00 +#define ERDPTH   0x01 +#define EWRPTL   0x02 +#define EWRPTH   0x03 +#define ETXSTL   0x04 +#define ETXSTH   0x05 +#define ETXNDL   0x06 +#define ETXNDH   0x07 +#define ERXSTL   0x08 +#define ERXSTH   0x09 +#define ERXNDL   0x0A +#define ERXNDH   0x0B +#define ERXRDPTL 0x0C +#define ERXRDPTH 0x0D +#define ERXWRPTL 0x0E +#define ERXWRPTH 0x0F +#define EDMASTL  0x10 +#define EDMASTH  0x11 +#define EDMANDL  0x12 +#define EDMANDH  0x13 +#define EDMADSTL 0x14 +#define EDMADSTH 0x15 +#define EDMACSL  0x16 +#define EDMACSH  0x17 -// SPI operation codes -#define ENC28J60_READ_CTRL_REG	0x00 -#define ENC28J60_READ_BUF_MEM	0x3A -#define ENC28J60_WRITE_CTRL_REG	0x40 -#define ENC28J60_WRITE_BUF_MEM	0x7A -#define ENC28J60_BIT_FIELD_SET	0x80 -#define ENC28J60_BIT_FIELD_CLR	0xA0 -#define ENC28J60_SOFT_RESET		0xFF +// Bank 1 Registers +#define EHT0     0x20 +#define EHT1     0x21 +#define EHT2     0x22 +#define EHT3     0x23 +#define EHT4     0x24 +#define EHT5     0x25 +#define EHT6     0x26 +#define EHT7     0x27 +#define EPMM0    0x28 +#define EPMM1    0x29 +#define EPMM2    0x2A +#define EPMM3    0x2B +#define EPMM4    0x2C +#define EPMM5    0x2D +#define EPMM6    0x2E +#define EPMM7    0x2F +#define EPMCSL   0x30 +#define EPMCSH   0x31 +#define EPMOL    0x34 +#define EPMOH    0x35 +#define EWOLIE   0x36 +#define EWOLIR   0x37 +#define ERXFCON  0x38 +#define EPKTCNT  0x39 +// Bank 2 Register +#define MACON1   0xC0 +#define MACON2   0xC1 +#define MACON3   0xC2 +#define MACON4   0xC3 +#define MABBIPG  0xC4 +#define MAIPGL   0xC6 +#define MAIPGH   0xC7 +#define MACLCON1 0xC8 +#define MACLCON2 0xC9 +#define MAMXFLL  0xCA +#define MAMXFLH  0xCB +#define MAPHSUP  0xCD +#define MICON    0xD1 +#define MICMD    0xD2 +#define MIREGADR 0xD4 +#define MIWRL    0xD6 +#define MIWRH    0xD7 +#define MIRDL    0xD8 +#define MIRDH    0xD9 -// buffer boundaries applied to internal 8K ram -//	entire available packet buffer space is allocated -#define TXSTART_INIT   	0x0000	// start TX buffer at 0 -#define RXSTART_INIT   	0x0600	// give TX buffer space for one full ethernet frame (~1500 bytes) -#define RXSTOP_INIT    	0x1FFF	// receive buffer gets the rest +// Bank 3 Registers +#define MAADR1   0xE0 +#define MAADR0   0xE1 +#define MAADR3   0xE2 +#define MAADR2   0xE3 +#define MAADR5   0xE4 +#define MAADR4   0xE5 +#define EBSTSD   0x66 +#define EBSTCON  0x67 +#define EBSTCSL  0x68 +#define EBSTCSH  0x69 +#define MISTAT   0xEA +#define EREVID   0x72 +#define ECOCON   0x75 +#define EFLOCON  0x77 +#define EPAUSL   0x78 +#define EPAUSH   0x79 -#define	MAX_FRAMELEN	1518	// maximum ethernet frame length +// PHY Registers +#define PHCON1    0x00 +#define PHSTAT1   0x01 +#define PHHID1    0x02 +#define PHHID2    0x03 +#define PHCON2    0x10 +#define PHSTAT2   0x11 +#define PHIE      0x12 +#define PHIR      0x13 +#define PHLCON    0x14 -// Ethernet constants -#define ETHERNET_MIN_PACKET_LENGTH	0x3C -//#define ETHERNET_HEADER_LENGTH		0x0E +// ERXFCON bit definitions +#define UCEN      0x80 +#define ANDOR     0x40 +#define CRCEN     0x20 +#define PMEN      0x10 +#define MPEN      0x08 +#define HTEN      0x04 +#define MCEN      0x02 +#define BCEN      0x01 -// setup ports for I/O -//void ax88796SetupPorts(void); +// EIE bit definitions +#define INTIE     0x80 +#define PKTIE     0x40 +#define DMAIE     0x20 +#define LINKIE    0x10 +#define TXIE      0x08 +#define WOLIE     0x04 +#define TXERIE    0x02 +#define RXERIE    0x01 -//! do a ENC28J60 read operation -u08 enc28j60ReadOp(u08 op, u08 address); -//! do a ENC28J60 write operation -void enc28j60WriteOp(u08 op, u08 address, u08 data); -//! read the packet buffer memory -void enc28j60ReadBuffer(u16 len, u08* data); -//! write the packet buffer memory -void enc28j60WriteBuffer(u16 len, u08* data); -//! set the register bank for register at address -void enc28j60SetBank(u08 address); -//! read ax88796 register -u08 enc28j60Read(u08 address); -//! write ax88796 register -void enc28j60Write(u08 address, u08 data); -//! read a PHY register -u16 enc28j60PhyRead(u08 address); -//! write a PHY register -void enc28j60PhyWrite(u08 address, u16 data); +// EIR bit definitions +#define PKTIF     0x40 +#define DMAIF     0x20 +#define LINKIF    0x10 +#define TXIF      0x08 +#define WOLIF     0x04 +#define TXERIF    0x02 +#define RXERIF    0x01 -//! initialize the ethernet interface for transmit/receive -void enc28j60Init(u08* macaddr); +// ESTAT bit definitions +#define INT       0x80 +#define LATECOL   0x10 +#define RXBUSY    0x04 +#define TXABRT    0x02 +#define CLKRDY    0x01 -//! Packet transmit function. -/// Sends a packet on the network.  It is assumed that the packet is headed by a valid ethernet header. -/// \param len		Length of packet in bytes. -/// \param packet	Pointer to packet data. -/// \param len2		Length of the secound packet in bytes, can be 0. -/// \param packet2	Pointer to the secound packet data, can be NULL. -void enc28j60PacketSend(unsigned int len1, unsigned char* packet1, unsigned int len2, unsigned char* packet2); +// ECON2 bit definitions +#define AUTOINC   0x80 +#define PKTDEC    0x40 +#define PWRSV     0x20 +#define VRPS      0x08 -//! Packet receive function. -/// Gets a packet from the network receive buffer, if one is available. -/// The packet will by headed by an ethernet header. -/// \param	maxlen	The maximum acceptable length of a retrieved packet. -/// \param  buf 	Pointer to buffer. -/// \return Packet length in bytes if a packet was retrieved, zero otherwise. -unsigned int enc28j60PacketReceive(unsigned int maxlen, u08* buf); +// ECON1 bit definitions +#define TXRST     0x80 +#define RXRST     0x40 +#define DMAST     0x20 +#define CSUMEN    0x10 +#define TXRTS     0x08 +#define ENCRXEN   0x04 +#define BSEL1     0x02 +#define BSEL0     0x01 -#endif -//@} +// MACON1 bit definitions +#define LOOPBK    0x10 +#define TXPAUS    0x08 +#define RXPAUS    0x04 +#define PASSALL   0x02 +#define MARXEN    0x01 +// MACON2 bit definitions +#define MARST     0x80 +#define RNDRST    0x40 +#define MARXRST   0x08 +#define RFUNRST   0x04 +#define MATXRST   0x02 +#define TFUNRST   0x01 + +// MACON3 bit definitions +#define PADCFG2   0x80 +#define PADCFG1   0x40 +#define PADCFG0   0x20 +#define TXCRCEN   0x10 +#define PHDRLEN   0x08 +#define HFRMLEN   0x04 +#define FRMLNEN   0x02 +#define FULDPX    0x01 + +// MICMD bit definitions +#define MIISCAN   0x02 +#define MIIRD     0x01 + +// MISTAT bit definitions +#define NVALID    0x04 +#define SCAN      0x02 +#define BUSY      0x01 + +// PHCON1 bit definitions +#define PRST      0x8000 +#define PLOOPBK   0x4000 +#define PPWRSV    0x0800 +#define PDPXMD    0x0100 + +// PHSTAT1 bit definitions +#define PFDPX     0x1000 +#define PHDPX     0x0800 +#define LLSTAT    0x0004 +#define JBSTAT    0x0002 + +// PHCON2 bit definitions +#define FRCLINK   0x4000 +#define TXDIS     0x2000 +#define JABBER    0x0400 +#define HDLDIS    0x0100 + +// Packet Control bit Definitions +#define PHUGEEN   0x08 +#define PPADEN    0x04 +#define PCRCEN    0x02 +#define POVERRIDE 0x01 + +// SPI Instruction Set +#define RCR 0x00 // Read Control Register +#define RBM 0x3A // Read Buffer Memory +#define WCR 0x40 // Write Control Register +#define WBM 0x7A // Write Buffer Memory +#define BFS 0x80 // Bit Field Set +#define BFC 0xA0 // Bit Field Clear +#define SC  0xFF // Soft Reset + +// Buffer +#define RXSTART_INIT 0x0000 +#define RXSTOP_INIT  (0x1FFF-0x0600-1) +#define TXSTART_INIT (0x1FFF-0x0600) +#define TXSTOP_INIT  0x1FFF +#define MAX_FRAMELEN 1500 + +void enc28j60_init(uint8_t* mac_addr); + +uint16_t enc28j60_recv(uint8_t* buffer, uint16_t max_len); + +void enc28j60_send(uint8_t* buffer, uint16_t len); + +#endif /* _NET_ENC28J60_H_ */ diff --git a/firmware/octoclock/include/net/enc28j60conf.h b/firmware/octoclock/include/net/enc28j60conf.h deleted file mode 100644 index 0acf5473c..000000000 --- a/firmware/octoclock/include/net/enc28j60conf.h +++ /dev/null @@ -1,49 +0,0 @@ -/*! \file enc28j60conf.h \brief Microchip ENC28J60 Ethernet Interface Driver Configuration. */ -//***************************************************************************** -// -// File Name	: 'enc28j60conf.h' -// Title		: Microchip ENC28J60 Ethernet Interface Driver Configuration -// Author		: Pascal Stang -// Created		: 10/5/2004 -// Revised		: 8/22/2005 -// Version		: 0.1 -// Target MCU	: Atmel AVR series -// Editor Tabs	: 4 -// -// Description	: This driver provides initialization and transmit/receive -//		functions for the ENC28J60 10Mb Ethernet Controller and PHY. -// -// This code is distributed under the GNU Public License -//		which can be found at http://www.gnu.org/licenses/gpl.txt -// -//***************************************************************************** - -#ifndef ENC28J60CONF_H -#define ENC28J60CONF_H - -#include <stdint.h> -typedef uint8_t  u08; -typedef uint16_t u16; -typedef uint32_t u32; - -// ENC28J60 SPI port -#define ENC28J60_SPI_PORT		PORTB -#define ENC28J60_SPI_DDR		DDRB -#define ENC28J60_SPI_SCK		PORTB1 -#define ENC28J60_SPI_MOSI		PORTB2 -#define ENC28J60_SPI_MISO		PORTB3 -#define ENC28J60_SPI_SS			PORTB0 -// ENC28J60 control port -#define ENC28J60_CONTROL_PORT	PORTB -#define ENC28J60_CONTROL_DDR	DDRB -#define ENC28J60_CONTROL_CS		PORTB0 - -// MAC address for this interface -#define ENC28J60_MAC0 '0' -#define ENC28J60_MAC1 'F' -#define ENC28J60_MAC2 'F' -#define ENC28J60_MAC3 'I' -#define ENC28J60_MAC4 'C' -#define ENC28J60_MAC5 'E' - -#endif /* ENC28J60CONF_H */ diff --git a/firmware/octoclock/include/net/eth_mac_addr.h b/firmware/octoclock/include/net/eth_mac_addr.h index 0c790aa4f..78986bf04 100644 --- a/firmware/octoclock/include/net/eth_mac_addr.h +++ b/firmware/octoclock/include/net/eth_mac_addr.h @@ -21,11 +21,8 @@  #include <stdint.h>  // Ethernet MAC address - -#pragma pack(push,1)  typedef struct {    uint8_t	addr[6]; -} eth_mac_addr_t; -#pragma pack(pop) +} eth_mac_addr_t __attribute__((aligned(1)));  #endif /* INCLUDED_ETH_MAC_ADDR_H */ diff --git a/firmware/octoclock/include/network.h b/firmware/octoclock/include/network.h index 83e398bc5..6d126a197 100644 --- a/firmware/octoclock/include/network.h +++ b/firmware/octoclock/include/network.h @@ -69,17 +69,11 @@  #define _IPH_PROTO_SET(hdr, proto) (hdr)->_ttl_proto = (htons((proto) | (_IPH_TTL(hdr) << 8)))  #define _IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum) -bool using_network_defaults; +volatile bool using_network_defaults;  // Ethernet I/O buffers -uint8_t buf_in[512]; -uint8_t buf_out[512]; - -// Default values loaded if EEPROM is incomplete -static const uint32_t blank_eeprom_ip = _IP(255,255,255,255); -static const uint32_t default_ip      = _IP(192,168,10,3); -static const uint32_t default_dr      = _IP(192,168,10,1); -static const uint32_t default_netmask = _IP(255,255,255,0); +#define ETH_BUF_SIZE 512 +uint8_t eth_buf[ETH_BUF_SIZE];  typedef void (*udp_receiver_t)(struct socket_address src, struct socket_address dst,  			       unsigned char *payload, int payload_len); diff --git a/firmware/octoclock/include/octoclock.h b/firmware/octoclock/include/octoclock.h index 849ab7f96..34cad1c12 100644 --- a/firmware/octoclock/include/octoclock.h +++ b/firmware/octoclock/include/octoclock.h @@ -1,5 +1,5 @@  /* - * Copyright 2014 Ettus Research LLC + * Copyright 2014-2015 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 @@ -24,26 +24,21 @@  #include <stdint.h>  // Define frequency -#define F_CPU 12500000UL +#define F_CPU 7372800UL  /* - * Timer 0 (8-bit) - *  * Set prescaler to 8 - *  * Enable overflow interrupt - *  * Set timer to 0 - */ -#define TIMER0_INIT() TCCR0 = (1 << CS01); \ -                      TIMSK |= (1 << TOIE0); \ -                      TCNT0 = 0; -/*   * Timer 1 (16-bit)   *  * Set prescaler to 1024   *  * Enable overflow interrupt   *  * Set timer to 0   */  #define TIMER1_INIT() TCCR1B = (1 << CS12) | (1 << CS10); \ -        TIMSK |= (1<<TOIE1); \ -        TCNT1 = 0; +                      TIMSK |= (1<<TOIE1); \ +                      TCNT1 = 0; + +#define TIMER1_DISABLE() TCCR1B = 0; \ +                         TIMSK = 0; \ +                         TCNT1 = 0;  #define TIMER1_ONE_SECOND ((uint32_t)(12207)) @@ -94,12 +89,6 @@   * Bits_32(10000000,11111111,10101010,01010101) = 2164238933   */ -typedef enum { -    Top, -    Middle, -    Bottom -} LEDs; -  void setup_atmel_io_ports(void);  #endif /* _OCTOCLOCK_H_ */ diff --git a/firmware/octoclock/include/state.h b/firmware/octoclock/include/state.h index 9734948cf..2170c2638 100644 --- a/firmware/octoclock/include/state.h +++ b/firmware/octoclock/include/state.h @@ -22,23 +22,24 @@  #include <octoclock.h> -// NOT PRESENT unless proven so... -static ref_t global_which_ref = NO_REF; -static bool global_gps_present = false; -static bool global_ext_ref_is_present = false; +// Global state variables +extern volatile bool         g_ext_ref_present; +extern volatile bool         g_gps_present; +extern volatile switch_pos_t g_switch_pos; +extern volatile ref_t        g_ref; -void led(LEDs which, int turn_it_on); +typedef enum { +    LED_TOP,    // Internal +    LED_MIDDLE, // External +    LED_BOTTOM  // Status +} led_t; -void LEDs_off(void); +void led(led_t which, bool on); -void force_internal(void); +void leds_off(void);  void prefer_internal(void);  void prefer_external(void); -ref_t which_ref(void); - -switch_pos_t get_switch_pos(void); -  #endif /* _STATE_H_ */ diff --git a/firmware/octoclock/include/usart.h b/firmware/octoclock/include/usart.h index 35ee9eb95..203255988 100644 --- a/firmware/octoclock/include/usart.h +++ b/firmware/octoclock/include/usart.h @@ -25,10 +25,6 @@ void usart_init(void);  char usart_getc(void); -char usart_getc_noblock(void); -  void usart_putc(char ch); -void usart_putc_nowait(char ch); -  #endif /* _USART_H_ */ diff --git a/firmware/octoclock/lib/clkdist.c b/firmware/octoclock/lib/clkdist.c index ed29510b6..0468ac38e 100644 --- a/firmware/octoclock/lib/clkdist.c +++ b/firmware/octoclock/lib/clkdist.c @@ -21,6 +21,8 @@  #include <clkdist.h>  #include <state.h> +#include <util/delay.h> +  #define wait() for(uint16_t u=14000; u; u--) asm("nop");  #define CLK   (PA0) // Shift by 0 bits @@ -33,7 +35,7 @@  // Table of 32-bit constants to be written to the TI chip's registers. These are  // from the "Special Settings" on Page 35 of the datasheet.  // For the GPS's 10 MHz output -static uint32_t table_Pri_Ref[] = { +static const uint32_t table_Pri_Ref[] = {      Bits_32(1,01010100,0,0),    // Reg 0      Bits_32(1,01010100,0,0),    // Outputs LVCMOS Positive&Negative Active - Non-inverted      Bits_32(1,01010100,0,0), @@ -47,7 +49,7 @@ static uint32_t table_Pri_Ref[] = {  // For the External 10 MHz input LVDS with external termination,   // Effectively DC coupled -static uint32_t table_Sec_Ref[] = { +static const uint32_t table_Sec_Ref[] = {      Bits_32(0001,01010100,0,100000),    // Reg 0 -- use Secondary Reference for all channels      Bits_32(0001,01010100,0,100000),    // Outputs LVCMOS Positive&Negative Active - Non-inverted      Bits_32(0001,01010100,0,100000), @@ -81,7 +83,6 @@ static bool get_bit(uint8_t  bit_number) {  // Send 32 bits to TI chip, LSB first.  // Don't worry about reading any bits back at this time  static void send_SPI(uint32_t bits) { -      // Basically, when the clock is low, one can set MOSI to anything, as it's      // ignored.      set_bit(CE_, Lo);    // Start SPI transaction with TI chip @@ -130,7 +131,8 @@ void setup_TI_CDCE18005(TI_Input_10_MHz which_input) {          for(uint8_t i=0; i<table_size; i++){              temp = table_Pri_Ref[i]<<4;              temp |= i; -            send_SPI(temp); // Make sure the register's address is in the LSBs +            // Make sure the register's address is in the LSBs +            send_SPI(temp);          }      } else {          // is Secondary_Ext -- External 10 MHz input from SMA connector @@ -169,6 +171,10 @@ uint32_t get_TI_CDCE18005(CDCE18005 which_register){      return receive_SPI();  } +void set_TI_CDCE18005(CDCE18005 which_register, uint32_t bits){ +    send_SPI((bits << 4) | which_register); +} +  bool check_TI_CDCE18005(TI_Input_10_MHz which_input,          CDCE18005 which_register) { diff --git a/firmware/octoclock/lib/enc28j60.c b/firmware/octoclock/lib/enc28j60.c index f0bbee0e7..ead7e4ec8 100644 --- a/firmware/octoclock/lib/enc28j60.c +++ b/firmware/octoclock/lib/enc28j60.c @@ -1,337 +1,211 @@ -/*! \file enc28j60.c \brief Microchip ENC28J60 Ethernet Interface Driver. */ -//***************************************************************************** -// -// File Name	: 'enc28j60.c' -// Title		: Microchip ENC28J60 Ethernet Interface Driver -// Author		: Pascal Stang (c)2005 -// Created		: 9/22/2005 -// Revised		: 5/19/2014 -// Version		: 0.1 -// Target MCU	: Atmel AVR series -// Editor Tabs	: 4 -// -// Description	: This driver provides initialization and transmit/receive -//	functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. -// This chip is novel in that it is a full MAC+PHY interface all in a 28-pin -// chip, using an SPI interface to the host processor. -// -//***************************************************************************** +/* + * Copyright 2015 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 <octoclock.h>  #include <net/enc28j60.h> -#include <net/enc28j60conf.h> -#include <avr/io.h>  #include <util/delay.h> -u08 Enc28j60Bank; -u16 NextPacketPtr; - -u08 enc28j60ReadOp(u08 op, u08 address) -{ -	u08 data; -    -	// assert CS -	ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); -	 -	// issue read command -	SPDR = op | (address & ADDR_MASK); -	while(!(SPSR & (1<<SPIF))); -	// read data -	SPDR = 0x00; -	while(!(SPSR & (1<<SPIF))); -	// do dummy read if needed -	if(address & 0x80) -	{ -		SPDR = 0x00; -		while(!(inb(SPSR) & (1<<SPIF))); -	} -	data = SPDR; -	 -	// release CS -	ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); - -	return data; -} +static uint8_t  current_bank; +static uint16_t next_pkt_ptr; -void enc28j60WriteOp(u08 op, u08 address, u08 data) -{ -	// assert CS -	ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); +#define SET_CS_ACTIVE()  SPI_PORT &= ~(1<<SPI_CS); +#define SET_CS_PASSIVE() SPI_PORT |= (1<<SPI_CS); +#define SPI_WAIT()       while(!(SPSR & (1<<SPIF))); -	// issue write command -	SPDR = op | (address & ADDR_MASK); -	while(!(SPSR & (1<<SPIF))); -	// write data -	SPDR = data; -	while(!(SPSR & (1<<SPIF))); +static uint8_t enc28j60_read_op(uint8_t op, uint8_t addr){ +    SET_CS_ACTIVE(); +    SPDR = (op | (addr & ADDR_MASK)); +    SPI_WAIT(); +    SPDR = 0x00; +    SPI_WAIT(); -	// release CS -	ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); -} +    if(addr & 0x80){ +        SPDR = 0x00; +        SPI_WAIT(); +    } -void enc28j60ReadBuffer(u16 len, u08* data) -{ -	// assert CS -	ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); -	 -	// issue read command -	SPDR = ENC28J60_READ_BUF_MEM; -	while(!(SPSR & (1<<SPIF))); -	while(len--) -	{ -		// read data -		SPDR = 0x00; -		while(!(SPSR & (1<<SPIF))); -		*data++ = SPDR; -	}	 -	// release CS -	ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); +    SET_CS_PASSIVE(); +    return SPDR;  } -void enc28j60WriteBuffer(u16 len, u08* data) -{ -	// assert CS -	ENC28J60_CONTROL_PORT &= ~(1<<ENC28J60_CONTROL_CS); -	 -	// issue write command -	SPDR = ENC28J60_WRITE_BUF_MEM; -	while(!(SPSR & (1<<SPIF))); -	while(len--) -	{ -		// write data -		SPDR = *data++; -		while(!(SPSR & (1<<SPIF))); -	}	 -	// release CS -	ENC28J60_CONTROL_PORT |= (1<<ENC28J60_CONTROL_CS); +static void enc28j60_write_op(uint8_t op, uint8_t addr, uint8_t value){ +    SET_CS_ACTIVE(); + +    SPDR = (op | (addr & ADDR_MASK)); +    SPI_WAIT(); +    SPDR = value; +    SPI_WAIT(); + +    SET_CS_PASSIVE();  } -void enc28j60SetBank(u08 address) -{ -	// set the bank (if needed) -	if((address & BANK_MASK) != Enc28j60Bank) -	{ -		// set the bank -		enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|ECON1_BSEL0)); -		enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (address & BANK_MASK)>>5); -		Enc28j60Bank = (address & BANK_MASK); -	} +static void enc28j60_read_buffer(uint8_t* buf, uint16_t len){ +    SET_CS_ACTIVE(); + +    SPDR = RBM; +    SPI_WAIT(); +    while(len){ +        len--; +        SPDR = 0x00; +        SPI_WAIT(); +        *buf = SPDR; +        buf++; +    } +    *buf = '\0'; + +    SET_CS_PASSIVE();  } -u08 enc28j60Read(u08 address) -{ -	// set the bank -	enc28j60SetBank(address); -	// do the read -	return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, address); +static void enc28j60_write_buffer(uint8_t* buf, uint16_t len){ +    SET_CS_ACTIVE(); + +    SPDR = WBM; +    SPI_WAIT(); +    while(len){ +        len--; +        SPDR = *buf; +        buf++; +        SPI_WAIT(); +    } + +    SET_CS_PASSIVE();  } -void enc28j60Write(u08 address, u08 data) -{ -	// set the bank -	enc28j60SetBank(address); -	// do the write -	enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, address, data); +static void enc28j60_set_bank(uint8_t addr){ +    if((addr & BANK_MASK) != current_bank){ +        enc28j60_write_op(BFC, ECON1, (BSEL1|BSEL0)); +        enc28j60_write_op(BFS, ECON1, ((addr & BANK_MASK) >> 5)); +        current_bank = (addr & BANK_MASK); +    }  } -u16 enc28j60PhyRead(u08 address) -{ -	u16 data; - -	// Set the right address and start the register read operation -	enc28j60Write(MIREGADR, address); -	enc28j60Write(MICMD, MICMD_MIIRD); - -	// wait until the PHY read completes -	while(enc28j60Read(MISTAT) & MISTAT_BUSY); - -	// quit reading -	enc28j60Write(MICMD, 0x00); -	 -	// get data value -	data  = enc28j60Read(MIRDL); -	data |= enc28j60Read(MIRDH); -	// return the data -	return data; +static uint8_t enc28j60_read(uint8_t addr){ +    enc28j60_set_bank(addr); +    return enc28j60_read_op(RCR, addr);  } -void enc28j60PhyWrite(u08 address, u16 data) -{ -	// set the PHY register address -	enc28j60Write(MIREGADR, address); -	 -	// write the PHY data -	enc28j60Write(MIWRL, data);	 -	enc28j60Write(MIWRH, data>>8); - -	// wait until the PHY write completes -	while(enc28j60Read(MISTAT) & MISTAT_BUSY); +static void enc28j60_write(uint8_t addr, uint16_t value){ +    enc28j60_set_bank(addr); +    enc28j60_write_op(WCR, addr, value);  } -void enc28j60Init(u08* macaddr) -{ -	// initialize I/O -	sbi(ENC28J60_CONTROL_DDR, ENC28J60_CONTROL_CS); -	sbi(ENC28J60_CONTROL_PORT, ENC28J60_CONTROL_CS); - -	// setup SPI I/O pins -	sbi(ENC28J60_SPI_PORT, ENC28J60_SPI_SCK);	// set SCK hi -	sbi(ENC28J60_SPI_DDR, ENC28J60_SPI_SCK);	// set SCK as output -	cbi(ENC28J60_SPI_DDR, ENC28J60_SPI_MISO);	// set MISO as input -	sbi(ENC28J60_SPI_DDR, ENC28J60_SPI_MOSI);	// set MOSI as output -	sbi(ENC28J60_SPI_DDR, ENC28J60_SPI_SS);		// SS must be output for Master mode to work -	// initialize SPI interface -	// master mode -	sbi(SPCR, MSTR); -	// select clock phase positive-going in middle of data -	cbi(SPCR, CPOL); -	// Data order MSB first -	cbi(SPCR,DORD); -	// switch to f/4 2X = f/2 bitrate -	cbi(SPCR, SPR0); -	cbi(SPCR, SPR1); -	sbi(SPSR, SPI2X); -	// enable SPI -	sbi(SPCR, SPE); - -	// perform system reset -	enc28j60WriteOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); - -    /* -     * "After sending an SPI Reset command, the PHY -     * clock is stopped but the ESTAT.CLKRDY bit is not -     * cleared. Therefore, polling the CLKRDY bit will not -     * work to detect if the PHY is ready. -     * -     * Additionally, the hardware start-up time of 300 us -     * may expire before the device is ready to operate. -     * -     * Work around -     * After issuing the Reset command, wait at least -     * 1 ms in firmware for the device to be ready." -     * -     * Source: http://ww1.microchip.com/downloads/en/DeviceDoc/80349c.pdf -     */ -	_delay_ms(1); - -	// do bank 0 stuff -	// initialize receive buffer -	// 16-bit transfers, must write low byte first -	// set receive buffer start address -	NextPacketPtr = RXSTART_INIT; -	enc28j60Write(ERXSTL, RXSTART_INIT&0xFF); -	enc28j60Write(ERXSTH, RXSTART_INIT>>8); -	// set receive pointer address -	enc28j60Write(ERXRDPTL, RXSTART_INIT&0xFF); -	enc28j60Write(ERXRDPTH, RXSTART_INIT>>8); -	// set receive buffer end -	// ERXND defaults to 0x1FFF (end of ram) -	enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF); -	enc28j60Write(ERXNDH, RXSTOP_INIT>>8); -	// set transmit buffer start -	// ETXST defaults to 0x0000 (beginnging of ram) -	enc28j60Write(ETXSTL, TXSTART_INIT&0xFF); -	enc28j60Write(ETXSTH, TXSTART_INIT>>8); - -	// do bank 2 stuff -	// enable MAC receive -	enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); -	// bring MAC out of reset -	enc28j60Write(MACON2, 0x00); -	// enable automatic padding and CRC operations -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); -	// set inter-frame gap (non-back-to-back) -	enc28j60Write(MAIPGL, 0x12); -	enc28j60Write(MAIPGH, 0x0C); -	// set inter-frame gap (back-to-back) -	enc28j60Write(MABBIPG, 0x12); -	// Set the maximum packet size which the controller will accept -	enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF);	 -	enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8); - -	// do bank 3 stuff -	// write MAC address -	// NOTE: MAC address in ENC28J60 is byte-backward -	enc28j60Write(MAADR5, macaddr[0]); -	enc28j60Write(MAADR4, macaddr[1]); -	enc28j60Write(MAADR3, macaddr[2]); -	enc28j60Write(MAADR2, macaddr[3]); -	enc28j60Write(MAADR1, macaddr[4]); -	enc28j60Write(MAADR0, macaddr[5]); - -	// no loopback of transmitted frames -	enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); - -	// switch to bank 0 -	enc28j60SetBank(ECON1); -	// enable interrutps -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); -	// enable packet reception -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); +void enc28j60_init(uint8_t* mac_addr){ +    SPI_DDR |= (1 << SPI_CS); +    SET_CS_PASSIVE(); + +    SPI_DDR  |= ((1 << SPI_MOSI) | (1 << SPI_SCK)); +    SPI_DDR  &= ~(1 << SPI_MISO); +    SPI_PORT &= ~(1 << SPI_MOSI); +    SPI_PORT &= ~(1 << SPI_SCK); +    SPCR      = ((1 << SPE) | (1 << MSTR)); +    SPSR     |= (1 << SPI2X); +    enc28j60_write_op(SC, 0, SC); +    next_pkt_ptr = RXSTART_INIT; + +    // Designate RX addresses +    enc28j60_write(ERXSTL,   (RXSTART_INIT & 0xFF)); +    enc28j60_write(ERXSTH,   (RXSTART_INIT >> 8)); +    enc28j60_write(ERXNDL,   (RXSTOP_INIT & 0xFF)); +    enc28j60_write(ERXNDH,   (RXSTOP_INIT >> 8)); + +    // Designate TX addresses +    enc28j60_write(ETXSTL,   (TXSTART_INIT & 0xFF)); +    enc28j60_write(ETXSTH,   (TXSTART_INIT >> 8)); +    enc28j60_write(ETXNDL,   (TXSTOP_INIT & 0xFF)); +    enc28j60_write(ETXNDH,   (TXSTOP_INIT >> 8)); + +    // Configure filters +    enc28j60_write(ERXFCON,  (UCEN|CRCEN|PMEN|BCEN)); +    enc28j60_write(EPMM0,    0x3F); +    enc28j60_write(EPMM1,    0x30); +    enc28j60_write(EPMCSL,   0xF9); +    enc28j60_write(EPMCSH,   0xF7); + +    // MAC initialization +    enc28j60_write(MACON1,   (MARXEN|TXPAUS|RXPAUS)); +    enc28j60_write(MACON2,   0x00); +    enc28j60_write_op(BFS,   MACON3, (PADCFG0|TXCRCEN|FRMLNEN)); +    enc28j60_write(MAIPGL,   0x12); +    enc28j60_write(MAIPGH,   0x0C); +    enc28j60_write(MABBIPG,  0x12); +    enc28j60_write(MAMXFLL,  (MAX_FRAMELEN & 0xFF));  +    enc28j60_write(MAMXFLH,  (MAX_FRAMELEN >> 8)); +    enc28j60_write(MAADR5,   mac_addr[0]); +    enc28j60_write(MAADR4,   mac_addr[1]); +    enc28j60_write(MAADR3,   mac_addr[2]); +    enc28j60_write(MAADR2,   mac_addr[3]); +    enc28j60_write(MAADR1,   mac_addr[4]); +    enc28j60_write(MAADR0,   mac_addr[5]); + +    enc28j60_set_bank(ECON1); +    enc28j60_write_op(BFS, ECON1, ENCRXEN);  } -void enc28j60PacketSend(unsigned int len1, unsigned char* packet1, unsigned int len2, unsigned char* packet2) -{ -	//Errata: Transmit Logic reset -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); -	enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); - -	// Set the write pointer to start of transmit buffer area -	enc28j60Write(EWRPTL, TXSTART_INIT&0xff); -	enc28j60Write(EWRPTH, TXSTART_INIT>>8); -	// Set the TXND pointer to correspond to the packet size given -	enc28j60Write(ETXNDL, (TXSTART_INIT+len1+len2)); -	enc28j60Write(ETXNDH, (TXSTART_INIT+len1+len2)>>8); - -	// write per-packet control byte -	enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); - -	// copy the packet into the transmit buffer -	enc28j60WriteBuffer(len1, packet1); -	if(len2>0) enc28j60WriteBuffer(len2, packet2); -	 -	// send the contents of the transmit buffer onto the network -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); +uint16_t enc28j60_recv(uint8_t* buf, uint16_t max_len){ +    uint16_t rxstat, len; + +    // Return if no data is available +    if(enc28j60_read(EPKTCNT) == 0) return 0; + +    enc28j60_write(ERDPTL, (next_pkt_ptr & 0xFF)); +    enc28j60_write(ERDPTH, (next_pkt_ptr >> 8)); +    next_pkt_ptr  = enc28j60_read_op(RBM, 0) | ((uint16_t)enc28j60_read_op(RBM, 0) << 8); +    len           = enc28j60_read_op(RBM, 0) | ((uint16_t)enc28j60_read_op(RBM, 0) << 8); +    len -= 4; +    rxstat        = enc28j60_read_op(RBM, 0) | ((uint16_t)enc28j60_read_op(RBM, 0) << 8); + +    // Length sanity check and actual enc28j60_read call +    if(len > (max_len - 1))  len = max_len - 1; +    if((rxstat & 0x80) == 0) len = 0; +    else enc28j60_read_buffer(buf, len); + +    // Update next packet pointer +    enc28j60_write(ERXRDPTL, (next_pkt_ptr & 0xFF)); +    enc28j60_write(ERXRDPTH, (next_pkt_ptr >> 8)); +    if(((next_pkt_ptr - 1) < RXSTART_INIT) || ((next_pkt_ptr - 1) > RXSTOP_INIT)){ +        enc28j60_write(ERXRDPTL, (RXSTOP_INIT & 0xFF)); +        enc28j60_write(ERXRDPTH, (RXSTOP_INIT >> 8)); +    } +    else{ +        enc28j60_write(ERXRDPTL, ((next_pkt_ptr - 1) & 0xFF)); +        enc28j60_write(ERXRDPTH, ((next_pkt_ptr - 1) >> 8)); +    } +    enc28j60_write_op(BFS, ECON2, PKTDEC); + +    return len;  } -unsigned int enc28j60PacketReceive(unsigned int maxlen, u08* buf) -{ -	u16 rxstat; -	u16 len; - -	// check if a packet has been received and buffered -	if( !enc28j60Read(EPKTCNT) ) -		return 0; - -	// Set the read pointer to the start of the received packet -	enc28j60Write(ERDPTL, (NextPacketPtr)); -	enc28j60Write(ERDPTH, (NextPacketPtr)>>8); -	// read the next packet pointer -	NextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); -	NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; -	// read the packet length -	len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); -	len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; -	// read the receive status -	rxstat  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); -	rxstat |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8; - -	// limit retrieve length -	// (we reduce the MAC-reported length by 4 to remove the CRC) -	len = MIN(len, maxlen); - -	// copy the packet from the receive buffer -	enc28j60ReadBuffer(len, buf); - -	// Move the RX read pointer to the start of the next received packet -	// This frees the memory we just read out -	enc28j60Write(ERXRDPTL, (NextPacketPtr)); -	enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8); - -	// decrement the packet counter indicate we are done with this packet -	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); - -	return len; +void enc28j60_send(uint8_t* buf, uint16_t len){ + +    // Wait for any current transmission to finish +    while(enc28j60_read_op(RCR, ECON1) & TXRTS){ +        if(enc28j60_read(EIR) & TXERIF){ +            enc28j60_write_op(BFS, ECON1, TXRST); +            enc28j60_write_op(BFC, ECON1, TXRST); +        } +    } + +    enc28j60_write(EWRPTL, (TXSTART_INIT & 0xFF)); +    enc28j60_write(EWRPTH, (TXSTART_INIT >> 8)); +    enc28j60_write(ETXNDL, ((TXSTART_INIT + len) & 0xFF)); +    enc28j60_write(ETXNDH, ((TXSTART_INIT + len) >> 8)); +    enc28j60_write_op(WBM, 0, 0x00); +    enc28j60_write_buffer(buf, len); +    enc28j60_write_op(BFS, ECON1, TXRTS);  } diff --git a/firmware/octoclock/lib/init.c b/firmware/octoclock/lib/init.c index 827ccb376..8592aa091 100644 --- a/firmware/octoclock/lib/init.c +++ b/firmware/octoclock/lib/init.c @@ -71,7 +71,7 @@ void setup_atmel_io_ports(){  // /pd_cdcd, /sync_code, /ce need to be 1 (disabled) to start  // all bits are outputs, except PA7 (gps_lock) and PA3 (MISO_CDCE) are inputs -PORTA = Bits_8(00110010); +PORTA = Bits_8(00100010);  DDRA =   1<<DDA6 | 1<<DDA5 | 1<<DDA4 | 1<<DDA2 | 1<<DDA1 | 1<<DDA0;  /* @@ -90,8 +90,8 @@ DDRA =   1<<DDA6 | 1<<DDA5 | 1<<DDA4 | 1<<DDA2 | 1<<DDA1 | 1<<DDA0;   *   */ -PORTB = Bits_8(01100001);        // Initial Value is all zeros -DDRB = 1<<DDB2 | 1<<DDB4 | 1<<DDB7;  // MOSI is an output; the Not Connected pins are also outputs +PORTB = Bits_8(01100001);   // Initial Value is all zeros +DDRB = Bits_8(11110111);    // MOSI is an output; the Not Connected pins are also outputs  /*   * Port C diff --git a/firmware/octoclock/lib/network.c b/firmware/octoclock/lib/network.c index bb49de4f6..22fc2b54b 100644 --- a/firmware/octoclock/lib/network.c +++ b/firmware/octoclock/lib/network.c @@ -27,6 +27,7 @@  #include <debug.h>  #include <octoclock.h> +#include <state.h>  #include <network.h>  #include <net/enc28j60.h> @@ -34,12 +35,14 @@  #include <net/if_arp.h>  #include <net/ethertype.h> +#include <util/delay.h> +  #include "arp_cache.h"  /***********************************************************************   * Constants + Globals   **********************************************************************/ -static const size_t out_buff_size = 512; +static const size_t out_buff_size = ETH_BUF_SIZE;  static const eth_mac_addr_t BCAST_MAC_ADDR = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};  #define MAX_UDP_LISTENERS 10 @@ -139,7 +142,7 @@ send_pkt(      //grab an out buffer and pointer      //select the output buffer based on type of packet      uint8_t *p; -    p = buf_out; +    p = eth_buf;      size_t total_len = 0;      //create a list of all buffers to copy @@ -149,7 +152,7 @@ send_pkt(      //copy each buffer into the out buffer      for (size_t i = 0; i < sizeof(buffs)/sizeof(buffs[0]); i++){          total_len += lens[i]; //use full length (not clipped) -        size_t bytes_remaining = out_buff_size - (size_t)(p - (uint8_t*)buf_out); +        size_t bytes_remaining = out_buff_size - (size_t)(p - (uint8_t*)eth_buf);          if (lens[i] > bytes_remaining) lens[i] = bytes_remaining;          memcpy(p, buffs[i], lens[i]);          p += lens[i]; @@ -161,7 +164,7 @@ send_pkt(      //For some reason, the ENC28J60 won't send the CRC      //if you don't tell it to send another byte after      //the given packet -    enc28j60PacketSend(total_len+1, buf_out, 0, 0); +    enc28j60_send(eth_buf, total_len);  }  static void @@ -333,15 +336,15 @@ handle_arp_packet(struct arp_eth_ipv4 *p, size_t size)  void  handle_eth_packet(size_t recv_len)  { -    eth_hdr_t *eth_hdr = (eth_hdr_t *)buf_in; +    eth_hdr_t *eth_hdr = (eth_hdr_t *)eth_buf;      uint16_t ethertype = htons(eth_hdr->ethertype);      if (ethertype == ETHERTYPE_ARP){ -        struct arp_eth_ipv4 *arp = (struct arp_eth_ipv4 *)(buf_in + sizeof(eth_hdr_t)); +        struct arp_eth_ipv4 *arp = (struct arp_eth_ipv4 *)(eth_buf + sizeof(eth_hdr_t));          handle_arp_packet(arp, recv_len-ETH_HLEN);      }      else if (ethertype == ETHERTYPE_IPV4){ -        struct ip_hdr *ip = (struct ip_hdr *)(buf_in + sizeof(eth_hdr_t)); +        struct ip_hdr *ip = (struct ip_hdr *)(eth_buf + sizeof(eth_hdr_t));          if (_IPH_V(ip) != 4 || _IPH_HL(ip) != 5)	// ignore pkts w/ bad version or options              return; @@ -357,7 +360,7 @@ handle_eth_packet(size_t recv_len)          bool is_my_ip = memcmp(&ip->dest, &htonl_local_ip_addr, sizeof(_local_ip_addr)) == 0;          if (!is_bcast && !is_my_ip) return; -        arp_cache_update(&ip->src, (eth_mac_addr_t *)(((char *)buf_in)+6)); +        arp_cache_update(&ip->src, (eth_mac_addr_t *)(((char *)eth_buf)+6));          switch (_IPH_PROTO(ip)){              case IP_PROTO_UDP: @@ -381,7 +384,6 @@ handle_eth_packet(size_t recv_len)   **********************************************************************/  static bool send_garp = false; -static bool sent_initial_garp = false;  static uint32_t num_overflows = 0;  // Six overflows is the closest overflow count to one minute. @@ -390,6 +392,7 @@ ISR(TIMER1_OVF_vect){      if(!(num_overflows % 6)) send_garp = true;  } +// Send a GARP packet once per minute.  static void  send_gratuitous_arp(){      send_garp = false; @@ -415,18 +418,9 @@ send_gratuitous_arp(){  // Executed every loop  void network_check(void){ -    size_t recv_len = enc28j60PacketReceive(512, buf_in); +    size_t recv_len = enc28j60_recv(eth_buf, ETH_BUF_SIZE);      if(recv_len > 0) handle_eth_packet(recv_len); -    /* -     * Send a gratuitous ARP packet two seconds after Ethernet -     * initialization. -     */ -    if(!sent_initial_garp && (num_overflows == 0 && TCNT1 > (TIMER1_ONE_SECOND*2))){ -        sent_initial_garp = true; -        send_garp = true; -    } -      if(send_garp) send_gratuitous_arp();  } @@ -435,9 +429,10 @@ void network_init(void){       * Read MAC address from EEPROM and initialize Ethernet driver. If EEPROM is blank,       * use default MAC address instead.       */ +    eeprom_busy_wait();      if(eeprom_read_byte(0) == 0xFF){          _MAC_ADDR(_local_mac_addr.addr, 0x00,0x80,0x2F,0x11,0x22,0x33); -        _local_ip_addr.addr = default_ip; +        _local_ip_addr.addr = _IP(192,168,10,3);          using_network_defaults = true;      }      else{ @@ -446,5 +441,8 @@ void network_init(void){          using_network_defaults = false;      } -    enc28j60Init((uint8_t*)&_local_mac_addr); +    enc28j60_init((uint8_t*)&_local_mac_addr); +    init_udp_listeners(); + +    send_garp = true;  } diff --git a/firmware/octoclock/lib/state.c b/firmware/octoclock/lib/state.c index 0dbcc6ece..26e1b783c 100644 --- a/firmware/octoclock/lib/state.c +++ b/firmware/octoclock/lib/state.c @@ -15,110 +15,80 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ -#include <avr/interrupt.h>  #include <avr/io.h> +#include <avrlibdefs.h>  #include <debug.h>  #include <octoclock.h>  #include <clkdist.h>  #include <state.h> -void led(LEDs which, int turn_it_on) { +// Global state variables +volatile bool g_ext_ref_present    = false; +volatile bool g_gps_present        = false; +volatile switch_pos_t g_switch_pos = PREFER_INTERNAL; +volatile ref_t g_ref               = NO_REF; +void led(led_t which, bool on){      // selects the proper bit      uint8_t LED = 0x20 << which; -    if(turn_it_on) +    if(on)          PORTC |= LED;      else          PORTC &= ~LED;  } -void LEDs_Off(void){ -    led(Top,false); -    led(Middle,false); -    led(Bottom,false); +void leds_off(void){ +    led(LED_TOP,    false); +    led(LED_MIDDLE, false); +    led(LED_BOTTOM, false);  } -void force_internal(void){ -    led(Top,true); -    led(Middle,false); -    led(Bottom,true); +static void force_internal(void){ +    led(LED_TOP,    true); +    led(LED_MIDDLE, false); +    led(LED_BOTTOM, true); +    // Tell ClkDist chip to use internal signals +    cli();      setup_TI_CDCE18005(Primary_GPS); +    sei(); -    // Set PPS to Primary (1) n.b.:  "1" in general means "Internal" for all -    // such signals +    // Set PPS to internal      PORTA |= (1<<PA6);  } -void force_external(void){ -    led(Top, false); -    led(Middle, true); -    led(Bottom, true); +static void force_external(void){ +    led(LED_TOP,    false); +    led(LED_MIDDLE, true); +    led(LED_BOTTOM, true); +    // Tell Clkdist chip to use external signals +    cli();      setup_TI_CDCE18005(Secondary_Ext); +    sei(); -    // Set PPS to External +    // Set PPS to external      PORTA &= ~(1<<PA6);  }  void prefer_internal(void){ -    // if internal is NOT OK, then force external -    if(global_gps_present) +    // If internal is NOT OK, then force external +    if(g_gps_present)          force_internal(); -    else if(global_ext_ref_is_present) +    else if(g_ext_ref_present)          force_external();      else -        LEDs_Off(); +        leds_off();  }  void prefer_external(void){ -    // if external is NOT OK, then force internal -    if(global_ext_ref_is_present) +    // If external is NOT OK, then force internal +    if(g_ext_ref_present)          force_external(); -    else if(global_gps_present) +    else if(g_gps_present)          force_internal();      else -        LEDs_Off(); -} - -static uint8_t prev_PE7 = 0; -static uint32_t timer0_num_overflows = 0; - -ISR(TIMER0_OVF_vect){ -    global_gps_present = (PIND & (1<<DDD4)); - -    // Every ~1/10 second -    if(!(timer0_num_overflows % 610)){ -        prev_PE7 = (PINE & (1<<DDE7)); - -        if(get_switch_pos() == UP) prefer_internal(); -        else prefer_external(); - -        global_ext_ref_is_present = false; -    } - -    if(!global_ext_ref_is_present){ -        global_ext_ref_is_present = (prev_PE7 != (PINE & (1<<DDE7))); -    } - -    timer0_num_overflows++; -} - -ref_t which_ref(void){ -    if(!global_gps_present && !global_ext_ref_is_present) global_which_ref = NO_REF; -    else if(global_gps_present && !global_ext_ref_is_present) global_which_ref = INTERNAL; -    else if(!global_gps_present && global_ext_ref_is_present) global_which_ref = EXTERNAL; -    else global_which_ref = (get_switch_pos() == UP) ? INTERNAL : EXTERNAL; - -    return global_which_ref; -} - -switch_pos_t get_switch_pos(void){ -    uint8_t portC = PINC; - -    // UP is prefer internal, -    // DOWN is prefer external -    return (portC & (1<<DDC1)) ? DOWN : UP; +        leds_off();  } diff --git a/firmware/octoclock/lib/udp_handlers.c b/firmware/octoclock/lib/udp_handlers.c index 1f20112c9..49b9b8023 100644 --- a/firmware/octoclock/lib/udp_handlers.c +++ b/firmware/octoclock/lib/udp_handlers.c @@ -36,7 +36,7 @@ void handle_udp_ctrl_packet(      pkt_out.proto_ver = OCTOCLOCK_FW_COMPAT_NUM;      pkt_out.sequence = pkt_in->sequence; -    //If the firmware is incompatible, only respond to queries +    // If the firmware is incompatible, only respond to queries      if(pkt_in->code == OCTOCLOCK_QUERY_CMD){          pkt_out.code = OCTOCLOCK_QUERY_ACK;          pkt_out.len = 0; @@ -50,44 +50,43 @@ void handle_udp_ctrl_packet(                  octoclock_fw_eeprom_t *eeprom_info = (octoclock_fw_eeprom_t*)pkt_out.data; -                //Read values from EEPROM into packet +                // Read values from EEPROM into packet +                eeprom_busy_wait();                  eeprom_read_block(eeprom_info, 0, sizeof(octoclock_fw_eeprom_t)); -                //If EEPROM network fields are not fully populated, copy defaults +                // If EEPROM network fields are not fully populated, copy defaults                  if(using_network_defaults){                      _MAC_ADDR(eeprom_info->mac_addr, 0x00,0x80,0x2F,0x11,0x22,0x33); -                    eeprom_info->ip_addr = default_ip; -                    eeprom_info->dr_addr = default_dr; -                    eeprom_info->netmask = default_netmask; +                    eeprom_info->ip_addr = _IP(192,168,10,3); +                    eeprom_info->dr_addr = _IP(192,168,10,1); +                    eeprom_info->netmask = _IP(255,255,255,0);                  } -                //Check if strings or revision is empty -                if(eeprom_info->serial[0] == 0xFF) memset(eeprom_info->serial, 0, 10); -                if(eeprom_info->name[0] == 0xFF) memset(eeprom_info->name, 0, 10); +                // Check if strings or revision is empty                  if(eeprom_info->revision == 0xFF) eeprom_info->revision = 0;                  break;              case BURN_EEPROM_CMD:{ -                //Confirm length of data +                // Confirm length of data                  if(pkt_in->len != sizeof(octoclock_fw_eeprom_t)){                      pkt_out.code = BURN_EEPROM_FAILURE_ACK;                      break;                  }                  /* -                 * In all cases, a full octoclock_fw_eeprom_t is written to lower the overall -                 * number of writes due to this EEPROM's smaller amount of safe writes.                   * It is up to the host to make sure that the values that should be                   * preserved are present in the octoclock_fw_eeprom_t struct.                   */                  const octoclock_fw_eeprom_t *eeprom_pkt = (octoclock_fw_eeprom_t*)pkt_in->data;                  pkt_out.len = 0; -                //Write EEPROM data from packet +                // Write EEPROM data from packet +                eeprom_busy_wait();                  eeprom_write_block(eeprom_pkt, 0, sizeof(octoclock_fw_eeprom_t)); -                //Read back and compare to packet to confirm successful write +                // Read back and compare to packet to confirm successful write                  uint8_t eeprom_contents[sizeof(octoclock_fw_eeprom_t)]; +                eeprom_busy_wait();                  eeprom_read_block(eeprom_contents, 0, sizeof(octoclock_fw_eeprom_t));                  uint8_t n = memcmp(eeprom_contents, eeprom_pkt, sizeof(octoclock_fw_eeprom_t));                  pkt_out.code = n ? BURN_EEPROM_FAILURE_ACK @@ -99,12 +98,12 @@ void handle_udp_ctrl_packet(                  pkt_out.code = SEND_STATE_ACK;                  pkt_out.len = sizeof(octoclock_state_t); -                //Populate octoclock_state_t fields +                // Populate octoclock_state_t fields                  octoclock_state_t *state = (octoclock_state_t*)pkt_out.data; -                state->external_detected = global_ext_ref_is_present ? 1 : 0; -                state->gps_detected = (PIND & _BV(DDD4)) ? 1 : 0; -                state->which_ref = (uint8_t)which_ref(); -                state->switch_pos = (uint8_t)get_switch_pos(); +                state->external_detected = g_ext_ref_present ? 1 : 0; +                state->gps_detected      = g_gps_present     ? 1 : 0; +                state->which_ref         = (uint8_t)g_ref; +                state->switch_pos        = (uint8_t)g_switch_pos;                  break;              case RESET_CMD: diff --git a/firmware/octoclock/lib/usart.c b/firmware/octoclock/lib/usart.c index 3620ac5e9..a267e43c6 100644 --- a/firmware/octoclock/lib/usart.c +++ b/firmware/octoclock/lib/usart.c @@ -34,16 +34,8 @@ char usart_getc(void){      return UDR1;  } -char usart_getc_noblock(void){ -    return ((UCSR1A & (1 << RXC))) ? UDR1 : -1; -} -  void usart_putc(char ch){      while((UCSR1A & (1 << UDRE1)) == 0);      UDR1 = ch;  } - -void usart_putc_nowait(char ch){ -    if((UCSR1A & (1 << UDRE1)) != 0) UDR1 = ch; -} diff --git a/firmware/octoclock/octoclock_r4/CMakeLists.txt b/firmware/octoclock/octoclock_r4/CMakeLists.txt index c3559d8d4..9223721c8 100644 --- a/firmware/octoclock/octoclock_r4/CMakeLists.txt +++ b/firmware/octoclock/octoclock_r4/CMakeLists.txt @@ -43,7 +43,7 @@ add_custom_target(  )  add_custom_target(      upload_r4 -    ${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x81:m -U lfuse:w:0xFF:m -U flash:w:octoclock_r4_fw.hex:i +    ${AVRDUDE} -p atmega128 -c ${PROGRAMMER} -P usb -U efuse:w:0xFF:m -U hfuse:w:0x81:m -U lfuse:w:0xEF:m -U flash:w:octoclock_r4_fw.hex:i      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}      DEPENDS octoclock_r4_fw_hex      COMMENT "Uploading OctoClock firmware to device with ${PROGRAMMER}" diff --git a/firmware/octoclock/octoclock_r4/octoclock_r4_main.c b/firmware/octoclock/octoclock_r4/octoclock_r4_main.c index 5e8e6d09b..b1df34195 100644 --- a/firmware/octoclock/octoclock_r4/octoclock_r4_main.c +++ b/firmware/octoclock/octoclock_r4/octoclock_r4_main.c @@ -36,7 +36,7 @@   * JTAGEN    = [X]   * SPIEN     = [X]   * EESAVE    = [X] - * BOOTSZ    = 4096W_F000 + * BOOTSZ    = 4095W_F000   * BOOTRST   = [ ]   * CKOPT     = [X]   * BODLEVEL  = 2V7 @@ -49,57 +49,165 @@   *   */ +#include <stdlib.h>  #include <string.h>  #include <stdint.h>  #include <stdbool.h>  #include <avr/eeprom.h> +#include <avr/interrupt.h>  #include <avr/io.h> +#include <avrlibdefs.h>  #include <octoclock.h> -#include <clkdist.h>  #include <debug.h> +#include <clkdist.h>  #include <state.h>  #include <network.h>  #include <usart.h> -#include <gpsdo.h> -#include <net/enc28j60.h>  #include <net/udp_handlers.h> +/* + * Timer 3 (16-bit) + *  * Set input capture trigger to rising edge + *  * Set prescaler to 1 + *  * Enable overflow interrupt + *  * Set timer to 0 + */ +#define TIMER3_INIT() TCCR3B = (1 << ICES3) | (1 << CS30); \ +                      ETIMSK |= (1 << TOIE3); \ +                      TCNT3 = 0; \ +                      ICR3 = 0; + +/* + * We use TIMER3 as a watchdog timer for external reference + * detection. Once a signal is detected, we allow for five + * timer overflows (~26 ms) without another signal before + * deciding that there is no external reference connected. + */ +#define EXT_REF_TIMEOUT 5 + +static volatile uint16_t num_overflows = 0; +static uint16_t current_num_overflows = 0; +static uint16_t prev_num_overflows = 0; +static uint16_t current_ICR3 = 0; +static uint16_t prev_ICR3 = 0; +static ref_t        prev_ref           = NO_REF; +static switch_pos_t prev_switch_pos    = PREFER_EXTERNAL; +bool top = false; + +ISR(TIMER3_OVF_vect){ +    num_overflows++; +} +  /*******************************************************************************  *   Main Routine  *******************************************************************************/  int main(void){ -    asm("cli"); +    /* +     * Initializations +     */ +    cli(); +    // Make sure interrupts belong to us +    MCUCR = (1<<IVCE); +    MCUCR = 0; + +    // Initialize global variables +    g_ext_ref_present = false; +    g_gps_present = false; +    g_switch_pos = PREFER_INTERNAL; +    g_ref = NO_REF; + +    // Atmega128      setup_atmel_io_ports(); -    network_init(); -    #ifndef DEBUG -    asm("sei"); -    #endif +    // Reset ClkDist chip +    reset_TI_CDCE18005(); -    init_udp_listeners(); -    register_udp_listener(OCTOCLOCK_UDP_CTRL_PORT, handle_udp_ctrl_packet); +    // GPSDO communication +    usart_init(); + +    // Ethernet stack +    network_init(); +    register_udp_listener(OCTOCLOCK_UDP_CTRL_PORT,  handle_udp_ctrl_packet);      register_udp_listener(OCTOCLOCK_UDP_GPSDO_PORT, handle_udp_gpsdo_packet); -    DEBUG_INIT(); // Does nothing when not in debug mode -    DEBUG_LOG(" "); //Force a newline between runs -    usart_init(); +    // Timers +    TIMER1_INIT(); // Network +    TIMER3_INIT(); // External reference check + +    // Debug (does nothing when not in debug mode) +    DEBUG_INIT(); +    DEBUG_LOG(" "); // Force a newline between runs + +    leds_off(); + +    sei(); + +    // Check if GPS present (should only need to happen once) +    g_gps_present = (PIND & (1<<DDD4)); + +    // State of previous iteration +    prev_ref           = NO_REF; +    prev_switch_pos    = PREFER_EXTERNAL; +    cli(); +    prev_ICR3          = ICR3; +    sei(); +    prev_num_overflows = 0; + +    /* +     * Main loop +     */ +    while(true){ +        // Check switch position +        g_switch_pos = (PINC & (1<<DDC1)) ? PREFER_EXTERNAL : PREFER_INTERNAL; + +        /* +         * Check input capture pin for external reference detection. +         * +         * 16-bit reads could be corrupted during an interrupt, so +         * disable interrupts for safety. +         */ +        cli(); +        current_ICR3          = ICR3; +        current_num_overflows = num_overflows; +        sei(); + +        // Signal detected, reset timer +        if(current_ICR3 != prev_ICR3){ +            cli(); +            TCNT3 = 0; +            num_overflows = 0; +            sei(); +            g_ext_ref_present = true; +        } + +        // Timeout, no external reference detected +        if(current_num_overflows >= EXT_REF_TIMEOUT){ +            g_ext_ref_present = false; +        } -    //Set initial ClkDist and front panel settings -    led(Middle,true); -    setup_TI_CDCE18005(Primary_GPS);    // 10 MHz from Internal Source +        // Determine and set reference based on state +        if(!g_gps_present && !g_ext_ref_present)     g_ref = NO_REF; +        else if(g_gps_present && !g_ext_ref_present) g_ref = INTERNAL; +        else if(!g_gps_present && g_ext_ref_present) g_ref = EXTERNAL; +        else g_ref = (g_switch_pos == PREFER_INTERNAL) ? INTERNAL : EXTERNAL; -    led(Top,true); -    PORTA |= (1<<PA6);    // PPS from Internal source +        if((g_ref != prev_ref) || (g_switch_pos != prev_switch_pos)){ +            if(g_switch_pos == PREFER_INTERNAL) prefer_internal(); +            else prefer_external(); +        } -    TIMER0_INIT(); -    TIMER1_INIT(); +        // Record this iteration's state +        prev_ref           = g_ref; +        prev_switch_pos    = g_switch_pos; +        prev_ICR3          = current_ICR3; +        prev_num_overflows = current_num_overflows; -    while(true) { +        // Handle incoming Ethernet packets          network_check();      }  } diff --git a/host/docs/octoclock.dox b/host/docs/octoclock.dox index 45a12e93a..58d2b1f99 100644 --- a/host/docs/octoclock.dox +++ b/host/docs/octoclock.dox @@ -7,41 +7,24 @@  - Hardware Capabilities:      -   Fully integrated timing source with 8-Way distribution (10 MHz and 1 PPS)      -   User selection between internal GPSDO (when present) or external 10 MHz/1 PPS source +    -   Ethernet bootloader for easy firmware upgrade      -   Source detection with automatic switch over in case of failure or disconnect      -   Streaming GPS time and NMEA strings over Ethernet (OctoClock-G only)  \section octoclock_load Loading Firmware onto the Octoclock -\subsection bootloader OctoClock bootloader - -If you purchased your OctoClock device before Ethernet functionality was introduced, or if your unit's -bootloader has somehow become corrupted, you must burn the bootloader onto the device before you can load -the primary firmware. - -To load the bootloader onto the OctoClock, two things are needed: - -- AVR programmer -- AVRdude software - -Connect the AVR programmer to J108, as specified on the <a href="http://files.ettus.com/schematics/octoclock/octoclock.pdf"> -schematics</a>. Once you verify that the programmer is properly connected, run the following commands to burn the firmware: +First, the OctoClock's bootloader needs to be loaded onto the device. Connect the AVR programmer to J108, as +specified on the <a href="http://files.ettus.com/schematics/octoclock/octoclock.pdf">schematics</a>. Once you +verify that the programmer is properly connected, run the following commands to load the bootloader:      cd <install path>/share/uhd/images -    avrdude -p atmega128 -c <programmer name> -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xFF:m -U flash:w:octoclock_bootloader.hex:i +    avrdude -p atmega128 -c <programmer name> -P usb -U efuse:w:0xFF:m -U hfuse:w:0x80:m -U lfuse:w:0xEF:m -U flash:w:octoclock_r4_fw.hex:i + +When the bootloader is loaded, it will have a default IP address of `192.168.10.3`.  \b Note:  On Linux, `sudo avrdude ...` might be necessary to gain access to the programmer. -Once the bootloader has been burned, power-cycle your OctoClock device and refer to the below instructions on burning the OctoClock's -primary firmware. - -\subsection application Primary Octoclock firmware - -To load firmware onto the OctoClock, you must use the `octoclock_firmware_burner` utility, specifying the IP -address of the OctoClock device, as follows: - -    octoclock_firmware_burner --addr=192.168.10.3 -  \section octoclock_network Setting Up Networking  \subsection host_interface Setting up the host interface diff --git a/host/lib/usrp_clock/octoclock/common.h b/host/lib/usrp_clock/octoclock/common.h index 96acbb30f..5861bc4b1 100644 --- a/host/lib/usrp_clock/octoclock/common.h +++ b/host/lib/usrp_clock/octoclock/common.h @@ -1,5 +1,5 @@  /* - * Copyright 2014 Ettus Research LLC + * Copyright 2014-2015 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 @@ -42,11 +42,11 @@ extern "C" {   * only valid C code should go in this section.   */ -//These values are placed in the octoclock_packet_t.proto_ver field +// These values are placed in the octoclock_packet_t.proto_ver field  #define OCTOCLOCK_BOOTLOADER_PROTO_VER 1234 -#define OCTOCLOCK_FW_COMPAT_NUM 2 +#define OCTOCLOCK_FW_COMPAT_NUM           3 -//UDP ports assigned for different tasks +// UDP ports assigned for different tasks  #define OCTOCLOCK_UDP_CTRL_PORT   50000  #define OCTOCLOCK_UDP_GPSDO_PORT  50001  #define OCTOCLOCK_UDP_FW_PORT     50002 @@ -98,11 +98,21 @@ typedef enum {  } ref_t;  typedef enum { -    UP, -    DOWN +    PREFER_INTERNAL, +    PREFER_EXTERNAL  } switch_pos_t; +/* + * Some versions of AVR-GCC ignore #pragma pack, so + * if AVR-GCC is being used, use __attribute__ + * instead. + */ +#ifdef AVR +#define __AVR_ALIGNED__ __attribute__((aligned(1))) +#else +#define __AVR_ALIGNED__  #pragma pack(push,1) +#endif  // Structure of values in EEPROM, starting in location 0  typedef struct { @@ -113,34 +123,37 @@ typedef struct {      uint8_t serial[10];      uint8_t name[10];      uint8_t revision; -} octoclock_fw_eeprom_t; +} octoclock_fw_eeprom_t __AVR_ALIGNED__;  typedef struct {      uint8_t external_detected;      uint8_t gps_detected;      uint8_t which_ref;      uint8_t switch_pos; -} octoclock_state_t; +} octoclock_state_t __AVR_ALIGNED__;  typedef struct {      uint8_t num_wraps;      uint8_t pos; -} gpsdo_cache_state_t; +} gpsdo_cache_state_t __AVR_ALIGNED__;  typedef struct {      uint32_t proto_ver;      uint32_t sequence;      uint8_t code;      union { -        uint16_t len; +        uint16_t crc;          gpsdo_cache_state_t state;          uint16_t poolsize;          uint16_t addr;      };      uint8_t data[256]; -} octoclock_packet_t; +    uint16_t len; +} octoclock_packet_t __AVR_ALIGNED__; +#ifndef AVR  #pragma pack(pop) +#endif  #ifdef __cplusplus  } diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.cpp b/host/lib/usrp_clock/octoclock/octoclock_impl.cpp index b98d95725..ef1bc8ca0 100644 --- a/host/lib/usrp_clock/octoclock/octoclock_impl.cpp +++ b/host/lib/usrp_clock/octoclock/octoclock_impl.cpp @@ -357,14 +357,14 @@ void octoclock_impl::_get_state(const std::string &oc){  }  uhd::dict<ref_t, std::string> _ref_strings = boost::assign::map_list_of -    (NO_REF, "none") +    (NO_REF,   "none")      (INTERNAL, "internal")      (EXTERNAL, "external")  ;  uhd::dict<switch_pos_t, std::string> _switch_pos_strings = boost::assign::map_list_of -    (UP, "Prefer internal") -    (DOWN, "Prefer external") +    (PREFER_INTERNAL, "Prefer internal") +    (PREFER_EXTERNAL, "Prefer external")  ;  sensor_value_t octoclock_impl::_ext_ref_detected(const std::string &oc){ @@ -410,7 +410,7 @@ boost::uint32_t octoclock_impl::_get_time(const std::string &oc){  }  std::string octoclock_impl::_get_images_help_message(const std::string &addr){ -    const std::string image_name = "octoclock_r4_fw.bin"; +    const std::string image_name = "octoclock_r4_fw.hex";      //Check to see if image is in default location      std::string image_location; diff --git a/host/utils/octoclock_firmware_burner.cpp b/host/utils/octoclock_firmware_burner.cpp index d624095e6..eb8198a2b 100644 --- a/host/utils/octoclock_firmware_burner.cpp +++ b/host/utils/octoclock_firmware_burner.cpp @@ -1,5 +1,5 @@  // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2015 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 @@ -74,8 +74,23 @@ boost::uint8_t firmware_image[MAX_FIRMWARE_SIZE];  size_t firmware_size = 0;  boost::uint8_t octoclock_data[udp_simple::mtu];  octoclock_packet_t *pkt_in = reinterpret_cast<octoclock_packet_t *>(octoclock_data); -std::string firmware_path; +std::string firmware_path, actual_firmware_path;  size_t num_blocks = 0; +bool hex = true; + +static uint16_t calculate_crc(boost::uint8_t* buffer, boost::uint16_t len){ +    boost::uint16_t crc = 0xFFFF; + +    for(size_t i = 0; i < len; i++){ +        crc ^= buffer[i]; +        for(boost::uint8_t j = 0; j < 8; ++j){ +            if(crc & 1) crc = (crc >> 1) ^ 0xA001; +            else crc = (crc >> 1); +        } +    } + +    return crc; +}  /*   * Functions @@ -121,26 +136,25 @@ device_addrs_t bootloader_find(const std::string &ip_addr){  }  void read_firmware(){ -    std::ifstream firmware_file(firmware_path.c_str(), std::ios::binary); -    firmware_file.seekg(0, std::ios::end); -    firmware_size = size_t(firmware_file.tellg()); +    std::ifstream firmware_file(actual_firmware_path.c_str(), std::ios::binary); +    firmware_size = size_t(fs::file_size(actual_firmware_path));      if(firmware_size > MAX_FIRMWARE_SIZE){          firmware_file.close();          throw uhd::runtime_error(str(boost::format("Firmware file too large: %d > %d")                                       % firmware_size % (MAX_FIRMWARE_SIZE)));      } -    firmware_file.seekg(0, std::ios::beg);      firmware_file.read((char*)firmware_image, firmware_size);      firmware_file.close(); -    num_blocks = (firmware_size % BLOCK_SIZE) ? (firmware_size / BLOCK_SIZE) -                                              : ((firmware_size / BLOCK_SIZE) + 1); +    num_blocks = (firmware_size % BLOCK_SIZE) ? ((firmware_size / BLOCK_SIZE) + 1) +                                              : (firmware_size / BLOCK_SIZE);  }  void burn_firmware(udp_simple::sptr udp_transport){      octoclock_packet_t pkt_out;      pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand()); -    pkt_out.len = uhd::htonx<boost::uint16_t>((boost::uint16_t)firmware_size); +    pkt_out.len = (boost::uint16_t)firmware_size; +    pkt_out.crc = calculate_crc(firmware_image, firmware_size);      size_t len = 0, current_pos = 0;      //Tell OctoClock not to jump to application, wait for us instead @@ -149,6 +163,7 @@ void burn_firmware(udp_simple::sptr udp_transport){      if(UHD_OCTOCLOCK_PACKET_MATCHES(FW_BURN_READY_ACK, pkt_out, pkt_in, len)) std::cout << "ready." << std::endl;      else{          std::cout << std::endl; +        if(hex) fs::remove(actual_firmware_path);          throw uhd::runtime_error("Could not get OctoClock in valid state for firmware download.");      } @@ -165,7 +180,7 @@ void burn_firmware(udp_simple::sptr udp_transport){                    << "% (" << (i+1) << "/" << num_blocks << " blocks)" << std::flush;          memset(pkt_out.data, 0, BLOCK_SIZE); -        memcpy((void*)(pkt_out.data), &firmware_image[i*BLOCK_SIZE], std::min(int(firmware_size-current_pos), BLOCK_SIZE)); +        memcpy((void*)(pkt_out.data), &firmware_image[i*BLOCK_SIZE], BLOCK_SIZE);          bool success = false;          while(num_tries <= 5){ @@ -181,6 +196,7 @@ void burn_firmware(udp_simple::sptr udp_transport){          }          if(not success){              std::cout << std::endl; +            if(hex) fs::remove(actual_firmware_path);              throw uhd::runtime_error("Failed to burn firmware to OctoClock!");          } @@ -196,7 +212,6 @@ void verify_firmware(udp_simple::sptr udp_transport){      pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand());      size_t len = 0, current_pos = 0; -      for(size_t i = 0; i < num_blocks; i++){          pkt_out.sequence++;          pkt_out.addr = i*BLOCK_SIZE; @@ -208,11 +223,13 @@ void verify_firmware(udp_simple::sptr udp_transport){              if(memcmp((void*)(pkt_in->data), &firmware_image[i*BLOCK_SIZE],                        std::min(int(firmware_size-current_pos), BLOCK_SIZE))){                  std::cout << std::endl; +                if(hex) fs::remove(actual_firmware_path);                  throw uhd::runtime_error("Failed to verify OctoClock firmware!");              }          }          else{              std::cout << std::endl; +            if(hex) fs::remove(actual_firmware_path);              throw uhd::runtime_error("Failed to verify OctoClock firmware!");          }      } @@ -230,6 +247,7 @@ bool reset_octoclock(const std::string &ip_addr){      UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, RESET_CMD, pkt_out, len, octoclock_data);      if(not UHD_OCTOCLOCK_PACKET_MATCHES(RESET_ACK, pkt_out, pkt_in, len)){          std::cout << std::endl; +        if(hex) fs::remove(actual_firmware_path);          throw uhd::runtime_error("Failed to place device in state to receive firmware.");      } @@ -246,11 +264,13 @@ void finalize(udp_simple::sptr udp_transport){      UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, FINALIZE_BURNING_CMD, pkt_out, len, octoclock_data);      if(not UHD_OCTOCLOCK_PACKET_MATCHES(FINALIZE_BURNING_ACK, pkt_out, pkt_in, len)){          std::cout << std::endl; +        if(hex) fs::remove(actual_firmware_path);          std::cout << "no ACK. Bootloader may not have loaded application." << std::endl;      }  } -int UHD_SAFE_MAIN(int argc, char *argv[]){ +int UHD_SAFE_MAIN(UHD_UNUSED(int argc), UHD_UNUSED(char *argv[])){ +      std::string ip_addr;      po::options_description desc("Allowed options");      desc.add_options() @@ -300,7 +320,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){              throw uhd::runtime_error(str(boost::format("This filepath does not exist: %s") % firmware_path));          }      } -    else firmware_path = find_image_path("octoclock_r4_fw.bin"); +    else firmware_path = find_image_path("octoclock_r4_fw.hex");      //If Intel hex file detected, convert to binary      std::string ext = fs::extension(firmware_path); @@ -312,9 +332,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){                                                            % time_spec_t::get_system_time().get_full_secs()));          Hex2Bin(firmware_path.c_str(), temp_bin.string().c_str(), false); -        firmware_path = temp_bin.string(); +        actual_firmware_path = temp_bin.string();      }      else if(ext == ".bin"){ +        hex = false; +        actual_firmware_path = firmware_path;          std::cout << "Found firmware at path: " << firmware_path << std::endl;      }      else throw uhd::runtime_error("The firmware file has in improper extension (must be .hex or .bin)."); @@ -327,6 +349,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){              if(reset_octoclock(ip_addr)) std::cout << "successful." << std::endl;              else{                  std::cout << "failed." << std::endl; +                if(hex) fs::remove(actual_firmware_path);                  throw uhd::runtime_error("Failed to reset OctoClock device into its bootloader.");              }          } @@ -334,6 +357,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      }      else{          std::cout << "failed." << std::endl; +        if(hex) fs::remove(actual_firmware_path);          throw uhd::runtime_error("Could not find OctoClock with given IP address!");      } @@ -354,7 +378,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      if(octoclocks.size() == 1){          if(octoclocks[0]["type"] == "octoclock-bootloader"){              std::cout << std::endl; -            throw uhd::runtime_error("OctoClock failed to leave bootloader state."); +            if(hex) fs::remove(actual_firmware_path); +            throw uhd::runtime_error("Firmware did not load properly.");          }          else{              std::cout << "found." << std::endl << std::endl @@ -363,8 +388,10 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){      }      else{          std::cout << std::endl; +        if(hex) fs::remove(actual_firmware_path);          throw uhd::runtime_error("Failed to reinitialize OctoClock.");      } +    if(hex) fs::remove(actual_firmware_path);      return EXIT_SUCCESS;  }  | 
