From f72ad02f5ec370f45995ecd2b3e8b322e9a7e4dc Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Mon, 26 Jul 2010 15:33:30 -0700 Subject: New branch with firmware dir from USRP2P branch. --- firmware/microblaze/usrp2/.gitignore | 9 + firmware/microblaze/usrp2/Makefile.am | 43 ++ firmware/microblaze/usrp2/clocks.c | 239 ++++++++++ firmware/microblaze/usrp2/memory_map.h | 793 +++++++++++++++++++++++++++++++++ firmware/microblaze/usrp2/sd.c | 197 ++++++++ firmware/microblaze/usrp2/sd.h | 122 +++++ 6 files changed, 1403 insertions(+) create mode 100644 firmware/microblaze/usrp2/.gitignore create mode 100644 firmware/microblaze/usrp2/Makefile.am create mode 100644 firmware/microblaze/usrp2/clocks.c create mode 100644 firmware/microblaze/usrp2/memory_map.h create mode 100644 firmware/microblaze/usrp2/sd.c create mode 100644 firmware/microblaze/usrp2/sd.h (limited to 'firmware/microblaze/usrp2') diff --git a/firmware/microblaze/usrp2/.gitignore b/firmware/microblaze/usrp2/.gitignore new file mode 100644 index 000000000..18f715618 --- /dev/null +++ b/firmware/microblaze/usrp2/.gitignore @@ -0,0 +1,9 @@ +/Makefile +/Makefile.in +/*.a +/*.bin +/*.dump +/*.ihx +/*.elf +/*.rom +/*.map diff --git a/firmware/microblaze/usrp2/Makefile.am b/firmware/microblaze/usrp2/Makefile.am new file mode 100644 index 000000000..859ded9e5 --- /dev/null +++ b/firmware/microblaze/usrp2/Makefile.am @@ -0,0 +1,43 @@ +# +# Copyright 2010 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +include $(top_srcdir)/Makefile.common + +AM_CFLAGS = \ + $(COMMON_CFLAGS) + +AM_LDFLAGS = \ + $(COMMON_LFLAGS) \ + libusrp2.a \ + -Wl,-defsym -Wl,_TEXT_START_ADDR=0x0050 \ + -Wl,-defsym -Wl,_STACK_SIZE=3072 + +######################################################################## +# USRP2 specific library and programs +######################################################################## +noinst_LIBRARIES = libusrp2.a + +libusrp2_a_SOURCES = \ + $(COMMON_SRCS) \ + clocks.c \ + sd.c + +noinst_PROGRAMS = \ + usrp2_txrx_uhd.elf + +usrp2_txrx_uhd_elf_SOURCES = \ + $(top_srcdir)/apps/txrx_uhd.c diff --git a/firmware/microblaze/usrp2/clocks.c b/firmware/microblaze/usrp2/clocks.c new file mode 100644 index 000000000..ccc4a7cc7 --- /dev/null +++ b/firmware/microblaze/usrp2/clocks.c @@ -0,0 +1,239 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include + +#include "memory_map.h" +#include "ad9510.h" +#include "spi.h" +#include "u2_init.h" +#include "nonstdio.h" + +void +clocks_init(void) +{ + // Set up basic clocking functions in AD9510 + ad9510_write_reg(0x45, 0x00); // CLK2 drives distribution + + clocks_enable_fpga_clk(true, 1); + + spi_wait(); + + // Set up PLL for 10 MHz reference + // Reg 4, A counter, Don't Care + ad9510_write_reg(0x05, 0x00); // Reg 5, B counter MSBs, 0 + ad9510_write_reg(0x06, 0x05); // Reg 6, B counter LSBs, 5 + // Reg 7, Loss of reference detect, doesn't work yet, 0 + ad9510_write_reg(0x5A, 0x01); // Update Regs + + // Primary clock configuration + clocks_mimo_config(MC_WE_DONT_LOCK); + + // Set up other clocks + //clocks_enable_test_clk(false, 0); + //clocks_enable_tx_dboard(false, 0); + //clocks_enable_rx_dboard(false, 0); + clocks_enable_eth_phyclk(false, 0); + + // Enable clock to ADCs and DACs + //clocks_enable_dac_clk(true, 1); + //clocks_enable_adc_clk(true, 1); +} + + +void +clocks_mimo_config(int flags) +{ + if (flags & _MC_WE_LOCK){ + // Reg 8, Charge pump on, dig lock det, positive PFD, 47 + ad9510_write_reg(0x08, 0x47); + } + else { + // Reg 8, Charge pump off, dig lock det, positive PFD + ad9510_write_reg(0x08, 0x00); + } + + // Reg 9, Charge pump current, 0x40=3mA, 0x00=650uA + ad9510_write_reg(0x09, 0x00); + // Reg A, Prescaler of 2, everything normal 04 + ad9510_write_reg(0x0A, 0x04); + // Reg B, R Div MSBs, 0 + ad9510_write_reg(0x0B, 0x00); + // Reg C, R Div LSBs, 1 + ad9510_write_reg(0x0C, 0x01); + // Reg D, Antibacklash, Digital lock det, 0 + + ad9510_write_reg(0x5A, 0x01); // Update Regs + + spi_wait(); + + // Allow for clock switchover + + if (flags & _MC_WE_LOCK){ // WE LOCK + if (flags & _MC_MIMO_CLK_INPUT) { + // Turn on ref output and choose the MIMO connector + output_regs->clk_ctrl = 0x15; + } + else { + // turn on ref output and choose the SMA + output_regs->clk_ctrl = 0x1C; + } + } + else { // WE DONT LOCK + // Disable both ext clk inputs + output_regs->clk_ctrl = 0x10; + } + + // Do we drive a clock onto the MIMO connector? + if (flags & MC_PROVIDE_CLK_TO_MIMO) + clocks_enable_clkexp_out(true,10); + else + clocks_enable_clkexp_out(false,0); +} + +bool +clocks_lock_detect() +{ + if(pic_regs->pending & PIC_CLKSTATUS) + return true; + return false; +} + +int inline +clocks_gen_div(int divisor) +{ + int L,H; + L = (divisor>>1)-1; + H = divisor-L-2; + return (L<<4)|H; +} + +#define CLOCK_OUT_EN 0x08 +#define CLOCK_OUT_DIS_CMOS 0x01 +#define CLOCK_OUT_DIS_PECL 0x02 +#define CLOCK_DIV_DIS 0x80 +#define CLOCK_DIV_EN 0x00 + +#define CLOCK_MODE_PECL 1 +#define CLOCK_MODE_LVDS 2 +#define CLOCK_MODE_CMOS 3 + +void +clocks_enable_XXX_clk(bool enable, int divisor, int reg_en, int reg_div, int mode) +{ + int enable_word, div_word, div_en_word; + + switch(mode) { + case CLOCK_MODE_PECL : + enable_word = enable ? 0x08 : 0x0A; + break; + case CLOCK_MODE_LVDS : + enable_word = enable ? 0x02 : 0x03; + break; + case CLOCK_MODE_CMOS : + enable_word = enable ? 0x08 : 0x09; + break; + } + if(enable && (divisor>1)) { + div_word = clocks_gen_div(divisor); + div_en_word = CLOCK_DIV_EN; + } + else { + div_word = 0; + div_en_word = CLOCK_DIV_DIS; + } + + ad9510_write_reg(reg_en,enable_word); // Output en/dis + ad9510_write_reg(reg_div,div_word); // Set divisor + ad9510_write_reg(reg_div+1,div_en_word); // Enable or Bypass Divider + ad9510_write_reg(0x5A, 0x01); // Update Regs +} + +// Clock 0 +/*void +clocks_enable_test_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x3C,0x48,CLOCK_MODE_PECL); +}*/ + +// Clock 1 +void +clocks_enable_fpga_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x3D,0x4A,CLOCK_MODE_PECL); +} + +// Clock 2 on Rev 3, Clock 5 on Rev 4 +void +clocks_enable_clkexp_out(bool enable, int divisor) +{ + if(u2_hw_rev_major == 3) + clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); + else if(u2_hw_rev_major == 4) { + ad9510_write_reg(0x34,0x00); // Turn on fine delay + ad9510_write_reg(0x35,0x00); // Set Full Scale to nearly 10ns + ad9510_write_reg(0x36,0x1c); // Set fine delay. 0x20 is midscale + clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); + + } + else + putstr("ERR: Invalid Rev\n"); +} + +// Clock 5 on Rev 3, none (was 2) on Rev 4 +void +clocks_enable_eth_phyclk(bool enable, int divisor) +{ + if(u2_hw_rev_major == 3) + clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); + else if(u2_hw_rev_major == 4) + clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); + else + putstr("ERR: Invalid Rev\n"); +} + +// Clock 3 +/*void +clocks_enable_dac_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x3F,0x4E,CLOCK_MODE_PECL); +}*/ + +// Clock 4 +/*void +clocks_enable_adc_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x40,0x50,CLOCK_MODE_LVDS); +}*/ + +// Clock 6 +/*void +clocks_enable_tx_dboard(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x42,0x54,CLOCK_MODE_CMOS); +}*/ + +// Clock 7 +/*void +clocks_enable_rx_dboard(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x43,0x56,CLOCK_MODE_CMOS); +}*/ diff --git a/firmware/microblaze/usrp2/memory_map.h b/firmware/microblaze/usrp2/memory_map.h new file mode 100644 index 000000000..4c9ead615 --- /dev/null +++ b/firmware/microblaze/usrp2/memory_map.h @@ -0,0 +1,793 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2008,2009 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Overall Memory Map + * 0000-7FFF 32K RAM space (16K on 1500, 24K on 2000, 32K on DSP) + * 8000-BFFF 16K Buffer Pool + * C000-FFFF 16K Peripherals + */ + + +#ifndef INCLUDED_MEMORY_MAP_H +#define INCLUDED_MEMORY_MAP_H + +#include + + +#define MASTER_CLK_RATE 100000000 // 100 MHz + + +//////////////////////////////////////////////////////////////// +// +// Memory map for embedded wishbone bus +// +//////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////// +// Main RAM, Slave 0 + +#define RAM_BASE 0x0000 + +//////////////////////////////////////////////////////////////// +// Buffer Pool RAM, Slave 1 +// +// The buffers themselves are located in Slave 1, Buffer Pool RAM. +// The status registers are in Slave 5, Buffer Pool Status. +// The control register is in Slave 7, Settings Bus. + +#define BUFFER_POOL_RAM_BASE 0x8000 + +#define NBUFFERS 8 +#define BP_NLINES 0x0200 // number of 32-bit lines in a buffer +#define BP_LAST_LINE (BP_NLINES - 1) // last line in a buffer + +#define buffer_pool_ram \ + ((uint32_t *) BUFFER_POOL_RAM_BASE) + +#define buffer_ram(n) (&buffer_pool_ram[(n) * BP_NLINES]) + + +///////////////////////////////////////////////////// +// SPI Core, Slave 2. See core docs for more info +#define SPI_BASE 0xC000 // Base address (16-bit) + +typedef struct { + volatile uint32_t txrx0; + volatile uint32_t txrx1; + volatile uint32_t txrx2; + volatile uint32_t txrx3; + volatile uint32_t ctrl; + volatile uint32_t div; + volatile uint32_t ss; +} spi_regs_t; + +#define spi_regs ((spi_regs_t *) SPI_BASE) + + +// Masks for controlling different peripherals +#define SPI_SS_AD9510 1 +#define SPI_SS_AD9777 2 +#define SPI_SS_RX_DAC 4 +#define SPI_SS_RX_ADC 8 +#define SPI_SS_RX_DB 16 +#define SPI_SS_TX_DAC 32 +#define SPI_SS_TX_ADC 64 +#define SPI_SS_TX_DB 128 +#define SPI_SS_ADS64P44 256 + +// Masks for different parts of CTRL reg +#define SPI_CTRL_ASS (1<<13) +#define SPI_CTRL_IE (1<<12) +#define SPI_CTRL_LSB (1<<11) +#define SPI_CTRL_TXNEG (1<<10) +#define SPI_CTRL_RXNEG (1<< 9) +#define SPI_CTRL_GO_BSY (1<< 8) +#define SPI_CTRL_CHAR_LEN_MASK 0x7F + +//////////////////////////////////////////////// +// I2C, Slave 3 +// See Wishbone I2C-Master Core Specification. + +#define I2C_BASE 0xC400 + +typedef struct { + volatile uint32_t prescaler_lo; // r/w + volatile uint32_t prescaler_hi; // r/w + volatile uint32_t ctrl; // r/w + volatile uint32_t data; // wr = transmit reg; rd = receive reg + volatile uint32_t cmd_status; // wr = command reg; rd = status reg +} i2c_regs_t; + +#define i2c_regs ((i2c_regs_t *) I2C_BASE) + +#define I2C_CTRL_EN (1 << 7) // core enable +#define I2C_CTRL_IE (1 << 6) // interrupt enable + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// +#define I2C_CMD_START (1 << 7) // generate (repeated) start condition +#define I2C_CMD_STOP (1 << 6) // generate stop condition +#define I2C_CMD_RD (1 << 5) // read from slave +#define I2C_CMD_WR (1 << 4) // write to slave +#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2 (1 << 2) // reserved +#define I2C_CMD_RSVD_1 (1 << 1) // reserved +#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt + +#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected +#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration +#define I2C_ST_RSVD_4 (1 << 4) // reserved +#define I2C_ST_RSVD_3 (1 << 3) // reserved +#define I2C_ST_RSVD_2 (1 << 2) // reserved +#define I2C_ST_TIP (1 << 1) // Transfer-in-progress +#define I2C_ST_IP (1 << 0) // Interrupt pending + + +//////////////////////////////////////////////// +// GPIO, Slave 4 +// +// These go to the daughterboard i/o pins + +#define GPIO_BASE 0xC800 + +typedef struct { + volatile uint32_t io; // tx data in high 16, rx in low 16 + volatile uint32_t ddr; // 32 bits, 1 means output. tx in high 16, rx in low 16 + volatile uint32_t tx_sel; // 16 2-bit fields select which source goes to TX DB + volatile uint32_t rx_sel; // 16 2-bit fields select which source goes to RX DB +} gpio_regs_t; + +// each 2-bit sel field is layed out this way +#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg +#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic +#define GPIO_SEL_DEBUG_0 2 // if pin is an output, debug lines from FPGA fabric +#define GPIO_SEL_DEBUG_1 3 // if pin is an output, debug lines from FPGA fabric + +#define gpio_base ((gpio_regs_t *) GPIO_BASE) + +/////////////////////////////////////////////////// +// Buffer Pool Status, Slave 5 +// +// The buffers themselves are located in Slave 1, Buffer Pool RAM. +// The status registers are in Slave 5, Buffer Pool Status. +// The control register is in Slave 7, Settings Bus. + +#define BUFFER_POOL_STATUS_BASE 0xCC00 + +typedef struct { + volatile uint32_t last_line[NBUFFERS]; // last line xfer'd in buffer + volatile uint32_t status; // error and done flags + volatile uint32_t hw_config; // see below + volatile uint32_t dummy[3]; + volatile uint32_t irqs; + volatile uint32_t pri_enc_bp_status; + volatile uint32_t cycle_count; +} buffer_pool_status_t; + +#define buffer_pool_status ((buffer_pool_status_t *) BUFFER_POOL_STATUS_BASE) + +/* + * Buffer n's xfer is done. + * Clear this bit by issuing bp_clear_buf(n) + */ +#define BPS_DONE(n) (0x00000001 << (n)) +#define BPS_DONE_0 BPS_DONE(0) +#define BPS_DONE_1 BPS_DONE(1) +#define BPS_DONE_2 BPS_DONE(2) +#define BPS_DONE_3 BPS_DONE(3) +#define BPS_DONE_4 BPS_DONE(4) +#define BPS_DONE_5 BPS_DONE(5) +#define BPS_DONE_6 BPS_DONE(6) +#define BPS_DONE_7 BPS_DONE(7) + +/* + * Buffer n's xfer had an error. + * Clear this bit by issuing bp_clear_buf(n) + */ +#define BPS_ERROR(n) (0x00000100 << (n)) +#define BPS_ERROR_0 BPS_ERROR(0) +#define BPS_ERROR_1 BPS_ERROR(1) +#define BPS_ERROR_2 BPS_ERROR(2) +#define BPS_ERROR_3 BPS_ERROR(3) +#define BPS_ERROR_4 BPS_ERROR(4) +#define BPS_ERROR_5 BPS_ERROR(5) +#define BPS_ERROR_6 BPS_ERROR(6) +#define BPS_ERROR_7 BPS_ERROR(7) + +/* + * Buffer n is idle. A buffer is idle if it's not + * DONE, ERROR, or processing a transaction. If it's + * IDLE, it's safe to start a new transaction. + * + * Clear this bit by starting a xfer with + * bp_send_from_buf or bp_receive_to_buf. + */ +#define BPS_IDLE(n) (0x00010000 << (n)) +#define BPS_IDLE_0 BPS_IDLE(0) +#define BPS_IDLE_1 BPS_IDLE(1) +#define BPS_IDLE_2 BPS_IDLE(2) +#define BPS_IDLE_3 BPS_IDLE(3) +#define BPS_IDLE_4 BPS_IDLE(4) +#define BPS_IDLE_5 BPS_IDLE(5) +#define BPS_IDLE_6 BPS_IDLE(6) +#define BPS_IDLE_7 BPS_IDLE(7) + +/* + * Buffer n has a "slow path" packet in it. + * This bit is orthogonal to the bits above and indicates that + * the FPGA ethernet rx protocol engine has identified this packet + * as one requiring firmware intervention. + */ +#define BPS_SLOWPATH(n) (0x01000000 << (n)) +#define BPS_SLOWPATH_0 BPS_SLOWPATH(0) +#define BPS_SLOWPATH_1 BPS_SLOWPATH(1) +#define BPS_SLOWPATH_2 BPS_SLOWPATH(2) +#define BPS_SLOWPATH_3 BPS_SLOWPATH(3) +#define BPS_SLOWPATH_4 BPS_SLOWPATH(4) +#define BPS_SLOWPATH_5 BPS_SLOWPATH(5) +#define BPS_SLOWPATH_6 BPS_SLOWPATH(6) +#define BPS_SLOWPATH_7 BPS_SLOWPATH(7) + + +#define BPS_DONE_ALL 0x000000ff // mask of all dones +#define BPS_ERROR_ALL 0x0000ff00 // mask of all errors +#define BPS_IDLE_ALL 0x00ff0000 // mask of all idles +#define BPS_SLOWPATH_ALL 0xff000000 // mask of all slowpaths + +// The hw_config register + +#define HWC_SIMULATION 0x80000000 +#define HWC_WB_CLK_DIV_MASK 0x0000000f + +/*! + * \brief return non-zero if we're running under the simulator + */ +inline static int +hwconfig_simulation_p(void) +{ + return buffer_pool_status->hw_config & HWC_SIMULATION; +} + +/*! + * \brief Return Wishbone Clock divisor. + * The processor runs at the Wishbone Clock rate which is MASTER_CLK_RATE / divisor. + */ +inline static int +hwconfig_wishbone_divisor(void) +{ + return buffer_pool_status->hw_config & HWC_WB_CLK_DIV_MASK; +} + +/////////////////////////////////////////////////// +// Ethernet Core, Slave 6 + +#define ETH_BASE 0xD000 + +#include "eth_mac_regs.h" + +#define eth_mac ((eth_mac_regs_t *) ETH_BASE) + +//////////////////////////////////////////////////// +// Settings Bus, Slave #7, Not Byte Addressable! +// +// Output-only from processor point-of-view. +// 1KB of address space (== 256 32-bit write-only regs) + + +#define MISC_OUTPUT_BASE 0xD400 +#define TX_PROTOCOL_ENGINE_BASE 0xD480 +#define RX_PROTOCOL_ENGINE_BASE 0xD4C0 +#define BUFFER_POOL_CTRL_BASE 0xD500 +#define LAST_SETTING_REG 0xD7FC // last valid setting register + +#define SR_MISC 0 +#define SR_TX_PROT_ENG 32 +#define SR_RX_PROT_ENG 48 +#define SR_BUFFER_POOL_CTRL 64 +#define SR_UDP_SM 96 +#define SR_TX_DSP 208 +#define SR_TX_CTRL 224 +#define SR_RX_DSP 160 +#define SR_RX_CTRL 176 +#define SR_TIME64 192 +#define SR_SIMTIMER 198 +#define SR_LAST 255 + +#define _SR_ADDR(sr) (MISC_OUTPUT_BASE + (sr) * sizeof(uint32_t)) + +// --- buffer pool control regs --- + +typedef struct { + volatile uint32_t ctrl; +} buffer_pool_ctrl_t; + +// buffer pool ports + +#define PORT_SERDES 0 // serial/deserializer +#define PORT_DSP 1 // DSP tx or rx pipeline +#define PORT_ETH 2 // ethernet tx or rx +#define PORT_RAM 3 // RAM tx or rx + +// the buffer pool ctrl register fields + +#define BPC_BUFFER(n) (((n) & 0xf) << 28) +#define BPC_BUFFER_MASK BPC_BUFFER(~0) +#define BPC_BUFFER_0 BPC_BUFFER(0) +#define BPC_BUFFER_1 BPC_BUFFER(1) +#define BPC_BUFFER_2 BPC_BUFFER(2) +#define BPC_BUFFER_3 BPC_BUFFER(3) +#define BPC_BUFFER_4 BPC_BUFFER(4) +#define BPC_BUFFER_5 BPC_BUFFER(5) +#define BPC_BUFFER_6 BPC_BUFFER(6) +#define BPC_BUFFER_7 BPC_BUFFER(7) +#define BPC_BUFFER_NIL BPC_BUFFER(0x8) // disable + +#define BPC_PORT(n) (((n) & 0x7) << 25) +#define BPC_PORT_MASK BPC_PORT(~0) +#define BPC_PORT_SERDES BPC_PORT(PORT_SERDES) +#define BPC_PORT_DSP BPC_PORT(PORT_DSP) +#define BPC_PORT_ETH BPC_PORT(PORT_ETH) +#define BPC_PORT_RAM BPC_PORT(PORT_RAM) +#define BPC_PORT_NIL BPC_PORT(0x4) // disable + +#define BPC_CLR (1 << 24) // mutually excl commands +#define BPC_READ (1 << 23) +#define BPC_WRITE (1 << 22) + +#define BPC_STEP(step) (((step) & 0xf) << 18) +#define BPC_STEP_MASK BPC_STEP(~0) +#define BPC_LAST_LINE(line) (((line) & 0x1ff) << 9) +#define BPC_LAST_LINE_MASK BPC_LAST_LINE(~0) +#define BPC_FIRST_LINE(line) (((line) & 0x1ff) << 0) +#define BPC_FIRST_LINE_MASK BPC_FIRST_LINE(~0) + +#define buffer_pool_ctrl ((buffer_pool_ctrl_t *) BUFFER_POOL_CTRL_BASE) + +// --- misc outputs --- + +typedef struct { + volatile uint32_t clk_ctrl; + volatile uint32_t serdes_ctrl; + volatile uint32_t adc_ctrl; + volatile uint32_t leds; + volatile uint32_t phy_ctrl; // LSB is reset line to eth phy + volatile uint32_t debug_mux_ctrl; + volatile uint32_t ram_page; // FIXME should go somewhere else... + volatile uint32_t flush_icache; // Flush the icache + volatile uint32_t led_src; // HW or SW control for LEDs +} output_regs_t; + +#define SERDES_ENABLE 8 +#define SERDES_PRBSEN 4 +#define SERDES_LOOPEN 2 +#define SERDES_RXEN 1 + +#define ADC_CTRL_ON 0x0F +#define ADC_CTRL_OFF 0x00 + +// crazy order that matches the labels on the case + +#define LED_A (1 << 4) +#define LED_B (1 << 1) +#define LED_C (1 << 3) +#define LED_D (1 << 0) +#define LED_E (1 << 2) +// LED_F // controlled by CPLD +#define LED_RJ45 (1 << 5) + +#define output_regs ((output_regs_t *) MISC_OUTPUT_BASE) + +// --- udp tx regs --- + +typedef struct { + // Bits 19:16 are control info; bits 15:0 are data (see below) + // First two words are unused. + volatile uint32_t _nope[2]; + //--- ethernet header - 14 bytes--- + volatile struct{ + uint32_t mac_dst_0_1; //word 2 + uint32_t mac_dst_2_3; + uint32_t mac_dst_4_5; + uint32_t mac_src_0_1; + uint32_t mac_src_2_3; + uint32_t mac_src_4_5; + uint32_t ether_type; //word 8 + } eth_hdr; + //--- ip header - 20 bytes --- + volatile struct{ + uint32_t ver_ihl_tos; //word 9 + uint32_t total_length; + uint32_t identification; + uint32_t flags_frag_off; + uint32_t ttl_proto; + uint32_t checksum; + uint32_t src_addr_high; + uint32_t src_addr_low; + uint32_t dst_addr_high; + uint32_t dst_addr_low; //word 18 + } ip_hdr; + //--- udp header - 8 bytes --- + volatile struct{ + uint32_t src_port; //word 19 + uint32_t dst_port; + uint32_t length; + uint32_t checksum; //word 22 + } udp_hdr; + volatile uint32_t _pad[32-23]; +} sr_udp_sm_t; + +// control bits (all expect UDP_SM_LAST_WORD are mutually exclusive) + +// This is the last word of the header +#define UDP_SM_LAST_WORD (1 << 19) + +// Insert IP header checksum here. Data is the xor of 16'hFFFF and +// the values written into regs 9-13 and 15-18. +#define UDP_SM_INS_IP_HDR_CHKSUM (1 << 18) + +// Insert IP Length here (data ignored) +#define UDP_SM_INS_IP_LEN (1 << 17) + +// Insert UDP Length here (data ignore) +#define UDP_SM_INS_UDP_LEN (1 << 16) + +#define sr_udp_sm ((sr_udp_sm_t *) _SR_ADDR(SR_UDP_SM)) + +// --- dsp tx regs --- + +#define MIN_CIC_INTERP 1 +#define MAX_CIC_INTERP 128 + +typedef struct { + volatile uint32_t num_chan; + volatile uint32_t clear_state; // clears out state machine, fifos, +} sr_tx_ctrl_t; + +#define sr_tx_ctrl ((sr_tx_ctrl_t *) _SR_ADDR(SR_TX_CTRL)) + +typedef struct { + volatile int32_t freq; + volatile uint32_t scale_iq; // {scale_i,scale_q} + volatile uint32_t interp_rate; + volatile uint32_t _padding0; // padding for the tx_mux + // NOT freq, scale, interp + /*! + * \brief output mux configuration. + * + *
+   *     3                   2                   1                       
+   *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+   *  +-------------------------------+-------+-------+-------+-------+
+   *  |                                               | DAC1  |  DAC0 |
+   *  +-------------------------------+-------+-------+-------+-------+
+   * 
+   *  There are N DUCs (1 now) with complex inputs and outputs.
+   *  There are two DACs.
+   * 
+   *  Each 4-bit DACx field specifies the source for the DAC
+   *  Each subfield is coded like this: 
+   * 
+   *     3 2 1 0
+   *    +-------+
+   *    |   N   |
+   *    +-------+
+   * 
+   *  N specifies which DUC output is connected to this DAC.
+   * 
+   *   N   which interp output
+   *  ---  -------------------
+   *   0   DUC 0 I
+   *   1   DUC 0 Q
+   *   2   DUC 1 I
+   *   3   DUC 1 Q
+   *   F   All Zeros
+   *   
+   * The default value is 0x10
+   * 
+ */ + volatile uint32_t tx_mux; + +} dsp_tx_regs_t; + +#define dsp_tx_regs ((dsp_tx_regs_t *) _SR_ADDR(SR_TX_DSP)) + +// --- VITA RX CTRL regs --- +typedef struct { + // The following 3 are logically a single command register. + // They are clocked into the underlying fifo when time_ticks is written. + volatile uint32_t cmd; // {now, chain, num_samples(30) + volatile uint32_t time_secs; + volatile uint32_t time_ticks; + + volatile uint32_t clear_overrun; // write anything to clear overrun + volatile uint32_t vrt_header; // word 0 of packet. FPGA fills in packet counter + volatile uint32_t vrt_stream_id; // word 1 of packet. + volatile uint32_t vrt_trailer; + volatile uint32_t nsamples_per_pkt; + volatile uint32_t nchannels; // 1 in basic case, up to 4 for vector sources + volatile uint32_t pad[7]; // Make each structure 16 elements long +} sr_rx_ctrl_t; + +#define sr_rx_ctrl ((sr_rx_ctrl_t *) _SR_ADDR(SR_RX_CTRL)) + +// --- dsp rx regs --- +#define MIN_CIC_DECIM 1 +#define MAX_CIC_DECIM 128 + +typedef struct { + volatile int32_t freq; + volatile uint32_t scale_iq; // {scale_i,scale_q} + volatile uint32_t decim_rate; + volatile uint32_t dcoffset_i; // Bit 31 high sets fixed offset mode, using lower 14 bits, + // otherwise it is automatic + volatile uint32_t dcoffset_q; // Bit 31 high sets fixed offset mode, using lower 14 bits + + /*! + * \brief input mux configuration. + * + * This determines which ADC (or constant zero) is connected to + * each DDC input. There are N DDCs (1 now). Each has two inputs. + * + *
+   * Mux value:
+   *
+   *    3                   2                   1                       
+   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+   * +-------+-------+-------+-------+-------+-------+-------+-------+
+   * |                                                       |Q0 |I0 |
+   * +-------+-------+-------+-------+-------+-------+-------+-------+
+   *
+   * Each 2-bit I field is either 00 (A/D A), 01 (A/D B) or 1X (const zero)
+   * Each 2-bit Q field is either 00 (A/D A), 01 (A/D B) or 1X (const zero)
+   *
+   * The default value is 0x4
+   * 
+ */ + volatile uint32_t rx_mux; // called adc_mux in dsp_core_rx.v + + /*! + * \brief Streaming GPIO configuration + * + * This determines whether the LSBs of I and Q samples come from the DSP + * pipeline or from the io_rx GPIO pins. To stream GPIO, one must first + * set the GPIO data direction register to have io_rx[15] and/or io_rx[14] + * configured as inputs. The GPIO pins will be sampled at the time the + * remainder of the DSP sample is strobed into the RX sample FIFO. There + * will be a decimation-dependent fixed time offset between the GPIO + * sample stream and the associated RF samples. + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | MBZ |Q|I| + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * I 0=LSB comes from DSP pipeline (default) + * 1=LSB comes from io_rx[15] + * + * Q 0=LSB comes from DSP pipeline (default) + * 1=LSB comes from io_rx[14] + */ + volatile uint32_t gpio_stream_enable; + +} dsp_rx_regs_t; + +#define dsp_rx_regs ((dsp_rx_regs_t *) _SR_ADDR(SR_RX_DSP)) + +// ---------------------------------------------------------------- +// VITA49 64 bit time (write only) + /*! + * \brief Time 64 flags + * + *
+   *
+   *    3                   2                   1                       
+   *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+   * +-----------------------------------------------------------+-+-+
+   * |                                                           |S|P|
+   * +-----------------------------------------------------------+-+-+
+   *
+   * P - PPS edge selection (0=negedge, 1=posedge, default=0)
+   * S - Source (0=sma, 1=mimo, 0=default)
+   *
+   * 
+ */ +typedef struct { + volatile uint32_t secs; // value to set absolute secs to on next PPS + volatile uint32_t ticks; // value to set absolute ticks to on next PPS + volatile uint32_t flags; // flags - see chart above + volatile uint32_t imm; // set immediate (0=latch on next pps, 1=latch immediate, default=0) +} sr_time64_t; + +#define sr_time64 ((sr_time64_t *) _SR_ADDR(SR_TIME64)) + + +/* + * --- ethernet tx protocol engine regs (write only) --- + * + * These registers control the transmit portion of the ethernet + * protocol engine (out of USRP2). The protocol engine handles fifo + * status and sequence number insertion in outgoing packets, and + * automagically generates status packets when required to inform the + * host of changes in fifo availability. + * + * All outgoing packets have their fifo_status field set to the number + * of 32-bit lines of fifo available in the ethernet Rx fifo (see + * usrp2_eth_packet.h). Seqno's are set if FIXME, else 0. + * + * FIXME clean this up once we know how it's supposed to behave. + */ + +typedef struct { + volatile uint32_t flags; // not yet fully defined (channel?) + volatile uint32_t mac_dst0123; // 4 bytes of destination mac addr + volatile uint32_t mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr + volatile uint32_t mac_src2345; // 4 bytes of destination mac addr + volatile uint32_t seqno; // Write to init seqno. It autoincs on match +} tx_proto_engine_regs_t; + +#define tx_proto_engine ((tx_proto_engine_regs_t *) TX_PROTOCOL_ENGINE_BASE) + +/* + * --- ethernet rx protocol engine regs (write only) --- + * + * These registers control the receive portion of the ethernet + * protocol engine (into USRP2). The protocol engine offloads common + * packet inspection operations so that firmware has less to do on + * "fast path" packets. + * + * The registers define conditions which must be matched for a packet + * to be considered a "fast path" packet. If a received packet + * matches the src and dst mac address, ethertype, flags field, and + * expected seqno number it is considered a "fast path" packet, and + * the expected seqno is updated. If the packet fails to satisfy any + * of the above conditions it's a "slow path" packet, and the + * corresponding SLOWPATH flag will be set buffer_status register. + */ + +typedef struct { + volatile uint32_t flags; // not yet fully defined (channel?) + volatile uint32_t mac_dst0123; // 4 bytes of destination mac addr + volatile uint32_t mac_dst45src01; // 2 bytes of dest mac addr; 2 bytes of src mac addr + volatile uint32_t mac_src2345; // 4 bytes of destination mac addr + volatile uint32_t ethertype_pad; // ethertype in high 16-bits +} rx_proto_engine_regs_t; + +#define rx_proto_engine ((rx_proto_engine_regs_t *) RX_PROTOCOL_ENGINE_BASE) + + + +/////////////////////////////////////////////////// +// Simple Programmable Interrupt Controller, Slave 8 + +#define PIC_BASE 0xD800 + +// Interrupt request lines +// Bit numbers (LSB == 0) that correpond to interrupts into PIC + +#define IRQ_BUFFER 0 // buffer manager +#define IRQ_ONETIME 1 +#define IRQ_SPI 2 +#define IRQ_I2C 3 +#define IRQ_PHY 4 // ethernet PHY +#define IRQ_UNDERRUN 5 +#define IRQ_OVERRUN 6 +#define IRQ_PPS 7 // pulse per second +#define IRQ_UART_RX 8 +#define IRQ_UART_TX 9 +#define IRQ_SERDES 10 +#define IRQ_CLKSTATUS 11 +#define IRQ_PERIODIC 12 + +#define IRQ_TO_MASK(x) (1 << (x)) + +#define PIC_BUFFER_INT IRQ_TO_MASK(IRQ_BUFFER) +#define PIC_ONETIME_INT IRQ_TO_MASK(IRQ_ONETIME) +#define PIC_SPI_INT IRQ_TO_MASK(IRQ_SPI) +#define PIC_I2C_INT IRQ_TO_MASK(IRQ_I2C) +#define PIC_PHY_INT IRQ_TO_MASK(IRQ_PHY) +#define PIC_UNDERRUN_INT IRQ_TO_MASK(IRQ_UNDERRUN) +#define PIC_OVERRUN_INT IRQ_TO_MASK(IRQ_OVERRUN) +#define PIC_PPS_INT IRQ_TO_MASK(IRQ_PPS) +#define PIC_UART_RX_INT IRQ_TO_MASK(IRQ_UART_RX) +#define PIC_UART_TX_INT IRQ_TO_MASK(IRQ_UART_TX) +#define PIC_SERDES IRQ_TO_MASK(IRQ_SERDES) +#define PIC_CLKSTATUS IRQ_TO_MASK(IRQ_CLKSTATUS) + +typedef struct { + volatile uint32_t edge_enable; // mask: 1 -> edge triggered, 0 -> level + volatile uint32_t polarity; // mask: 1 -> rising edge + volatile uint32_t mask; // mask: 1 -> disabled + volatile uint32_t pending; // mask: 1 -> pending; write 1's to clear pending ints +} pic_regs_t; + +#define pic_regs ((pic_regs_t *) PIC_BASE) + +// ---------------------------------------------------------------- +// WB_CLK_RATE is the time base for this +typedef struct { + volatile uint32_t onetime; // Number of wb clk cycles till the onetime interrupt + volatile uint32_t periodic; // Repeat rate of periodic interrupt +} sr_simple_timer_t; + +#define sr_simple_timer ((sr_simple_timer_t *) _SR_ADDR(SR_SIMTIMER)) + +/////////////////////////////////////////////////// +// UART, Slave 10 + +#define UART_BASE 0xE000 + +typedef struct { + // All elements are 8 bits except for clkdiv (16), but we use uint32 to make + // the hardware for decoding easier + volatile uint32_t clkdiv; // Set to 50e6 divided by baud rate (no x16 factor) + volatile uint32_t txlevel; // Number of spaces in the FIFO for writes + volatile uint32_t rxlevel; // Number of available elements in the FIFO for reads + volatile uint32_t txchar; // Write characters to be sent here + volatile uint32_t rxchar; // Read received characters here +} uart_regs_t; + +#define uart_regs ((uart_regs_t *) UART_BASE) + +/////////////////////////////////////////////////// +// ATR Controller, Slave 11 + +#define ATR_BASE 0xE400 + +typedef struct { + volatile uint32_t v[16]; +} atr_regs_t; + +#define ATR_IDLE 0x0 // indicies into v +#define ATR_TX 0x1 +#define ATR_RX 0x2 +#define ATR_FULL 0x3 + +#define atr_regs ((atr_regs_t *) ATR_BASE) + +/////////////////////////////////////////////////// +// SD Card SPI interface, Slave 13 +// All regs are 8 bits wide, but are accessed as if they are 32 bits + +#define SDSPI_BASE 0xEC00 + +typedef struct { + volatile uint32_t status; // Write a 1 or 0 for controlling CS + volatile uint32_t clkdiv; + volatile uint32_t send_dat; + volatile uint32_t receive_dat; +} sdspi_regs_t; + +#define sdspi_regs ((sdspi_regs_t *) SDSPI_BASE) + +/////////////////////////////////////////////////// +// External RAM interface, Slave 14 +// Pages are 1K. Page is 10 bits, set by a control register +// output_regs->ram_page + +#define EXTRAM_BASE 0xF000 +#define extram ((volatile uint32_t *) EXTRAM_BASE) + + +/////////////////////////////////////////////////// + +#endif + diff --git a/firmware/microblaze/usrp2/sd.c b/firmware/microblaze/usrp2/sd.c new file mode 100644 index 000000000..d000b28ae --- /dev/null +++ b/firmware/microblaze/usrp2/sd.c @@ -0,0 +1,197 @@ +/* -*- c -*- */ +/* + * Copyright 2008 Ettus Research LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "sd.h" +#include "memory_map.h" +#include "stdint.h" +#include "stdio.h" + +static inline void +sd_packarg(unsigned char *argument,unsigned int value) +{ + argument[3] = (unsigned char)(value >> 24); + argument[2] = (unsigned char)(value >> 16); + argument[1] = (unsigned char)(value >> 8); + argument[0] = (unsigned char)(value); +} + +int +sd_init(void) +{ + unsigned char response[5]; + unsigned char argument[4]; + int i,j; + + for(i=0;i<4;i++) + argument[i] = 0; + + // Set clock to less than 400 kHz to start out + sdspi_regs->clkdiv = 128; + + // Delay at least 74 cycles + sd_assert_cs(); + for(i = 0; i < 100; i++) + sd_send_byte(SD_IDLE); + sd_deassert_cs(); + + // Initialization Sequence -- CMD0 CMD55 ACMD41 CMD58 + // Put card in idle state + if(sd_send_command(SD_CMD0,SD_CMD0_R,response,argument)==0) + return 0; // Something went wrong in command + + j = 0; + do { + j++; + if(sd_send_command(SD_CMD55,SD_CMD55_R,response,argument)==1) + sd_send_command(SD_ACMD41,SD_ACMD41_R,response,argument); + else + j = SD_IDLE_WAIT_MAX; + } + while(((response[0] & SD_MSK_IDLE) == SD_MSK_IDLE) && (j < SD_IDLE_WAIT_MAX)); + + if(j>= SD_IDLE_WAIT_MAX) // IDLE timeout exceeded, card asleep + return 0; + + // CMD58 reads the SD card capabilities + if(sd_send_command(SD_CMD58,SD_CMD58_R,response,argument)==0) + return 0; // CMD58 FAIL + + if((response[2] & SD_MSK_OCR_33) != SD_MSK_OCR_33) + return 0; // Card doesn't do 3.3V + + //printf("OCR = %x %x %x %x\n",response[0],response[1],response[2],response[3]); + + // Set blocklen here + sd_packarg(argument,SD_BLOCKLEN); + if(sd_send_command(SD_CMD16,SD_CMD16_R,response,argument)==0) + return 0; // Set Blocklen failed + + // Reset back to high speed + sdspi_regs->clkdiv = 4; + //puts("finished init\n"); + return 1; +} + +int sd_send_command(unsigned char cmd,unsigned char response_type, + unsigned char *response,unsigned char *argument) +{ + int i; + char response_length; + unsigned char tmp; + + sd_assert_cs(); + sd_send_byte((cmd & 0x3F) | 0x40); + for(i=3;i>=0;i--) + sd_send_byte(argument[i]); + sd_send_byte(SD_CRC); // Always the same + + response_length = 0; + switch(response_type) + { + case SD_R1: + case SD_R1B: + response_length = 1; + break; + case SD_R2: + response_length = 2; + break; + case SD_R3: + response_length = 5; + break; + default: + break; + } + + // Wait for a response, which will have a 0 start bit + i = 0; + do + { + tmp = sd_rcv_byte(); + i++; + } + while(((tmp & 0x80) != 0) && i < SD_CMD_TIMEOUT); + + if(i>= SD_CMD_TIMEOUT) + { + sd_deassert_cs(); + //puts("cmd send timeout\n"); + return 0; + } + + for(i=response_length-1; i>=0; i--) + { + response[i] = tmp; + tmp = sd_rcv_byte(); + } + i = 0; + if(response_type == SD_R1B) + { + do + { + i++; + tmp = sd_rcv_byte(); + } + while(tmp != SD_IDLE); + sd_send_byte(SD_IDLE); + } + + //puts("send cmd success\n"); + sd_deassert_cs(); + return 1; +} + +int +sd_read_block (unsigned int blockaddr, unsigned char *buf) +{ + unsigned char response[5]; + unsigned char argument[4]; + unsigned int i = 0; + unsigned char tmp; + + blockaddr <<= SD_BLOCKLEN_NBITS; + sd_packarg(argument,blockaddr); + if(sd_send_command(SD_CMD17,SD_CMD17_R,response,argument)==0) + return 0; //Failed READ; + if(response[0] != 0) + return 0; //Misaligned READ + + sd_assert_cs(); + i = 0; + do + { + tmp = sd_rcv_byte(); + i++; + } + while((tmp == 0xFF) && (i < SD_RD_TIMEOUT)); + if((i>= SD_RD_TIMEOUT) ||((tmp & SD_MSK_TOK_DATAERROR) == 0)) + { + sd_send_byte(SD_IDLE); // Send a dummy before quitting + return 0; // Data ERROR + } + for(i=0;i. + */ + +#ifndef INCLUDED_SD_H +#define INCLUDED_SD_H + +#include "memory_map.h" + +#define SD_READY 1 +#define SD_IDLE_WAIT_MAX 100 +#define SD_CMD_TIMEOUT 100 +#define SD_RD_TIMEOUT 1000 + +#define SD_CMD0 0 +#define SD_CMD1 1 +#define SD_CMD9 9 +#define SD_CMD10 10 +#define SD_CMD12 12 +#define SD_CMD13 13 +#define SD_CMD16 16 +#define SD_CMD17 17 +#define SD_CMD18 18 +#define SD_CMD24 24 +#define SD_CMD25 25 +#define SD_CMD27 27 +#define SD_CMD28 28 +#define SD_CMD29 29 +#define SD_CMD30 30 +#define SD_CMD32 32 +#define SD_CMD33 33 +#define SD_CMD38 38 +#define SD_CMD55 55 +#define SD_CMD58 58 +#define SD_CMD59 59 +#define SD_ACMD41 41 +#define SD_IDLE 0xFF +#define SD_CRC 0x95 + +#define SD_R1 1 +#define SD_R1B 2 +#define SD_R2 3 +#define SD_R3 4 + +#define SD_CMD0_R SD_R1 +#define SD_CMD16_R SD_R1 +#define SD_CMD17_R SD_R1 +#define SD_CMD55_R SD_R1 +#define SD_ACMD41_R SD_R1 +#define SD_CMD58_R SD_R3 + +#define SD_BLOCKLEN 512 +#define SD_BLOCKLEN_NBITS 9 + +#define SD_MSK_IDLE 0x01 +#define SD_MSK_OCR_33 0xC0 +#define SD_MSK_TOK_DATAERROR 0xE0 + + +int sd_init(void); + +static inline void +sd_assert_cs(void) +{ + // Wait for idle before doing anything + while(sdspi_regs->status != SD_READY) + ; + sdspi_regs->status = 1; +} + +static inline void +sd_deassert_cs(void) +{ + // Wait for idle before doing anything + while(sdspi_regs->status != SD_READY) + ; + sdspi_regs->status = 0; +} + +static inline char +sd_rcv_byte(void) +{ + // Wait for idle before doing anything + while(sdspi_regs->status != SD_READY) + ; + sdspi_regs->send_dat = SD_IDLE; + while(sdspi_regs->status != SD_READY) + ; + return sdspi_regs-> receive_dat; +} + +static inline void +sd_send_byte(char dat) +{ + // Wait for idle before doing anything + while(sdspi_regs->status != SD_READY) + ; // Wait for status = 1 (ready) + sdspi_regs->send_dat = dat; +} + + +int sd_send_command(unsigned char cmd,unsigned char response_type, + unsigned char *response,unsigned char *argument); + +int sd_read_block (unsigned int blockaddr, unsigned char *buf); +int sd_write_block(unsigned int blockaddr, const unsigned char *buf); + +#endif /* INCLUDED_SD_H */ -- cgit v1.2.3 From 06b0fc51c92a933d2641395c0b3974bb95df18d0 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Tue, 27 Jul 2010 19:18:48 -0700 Subject: Ethernet PHY re-do. --- firmware/microblaze/lib/Makefile.inc | 1 - firmware/microblaze/lib/eth_mac.c | 8 - firmware/microblaze/lib/eth_phy.h | 219 ------------------- firmware/microblaze/lib/ethernet.c | 389 --------------------------------- firmware/microblaze/usrp2/Makefile.am | 3 +- firmware/microblaze/usrp2/eth_phy.h | 219 +++++++++++++++++++ firmware/microblaze/usrp2/ethernet.c | 389 +++++++++++++++++++++++++++++++++ firmware/microblaze/usrp2p/Makefile.am | 3 +- firmware/microblaze/usrp2p/eth_phy.h | 210 ++++++++++++++++++ firmware/microblaze/usrp2p/ethernet.c | 388 ++++++++++++++++++++++++++++++++ 10 files changed, 1210 insertions(+), 619 deletions(-) delete mode 100644 firmware/microblaze/lib/eth_phy.h delete mode 100644 firmware/microblaze/lib/ethernet.c create mode 100644 firmware/microblaze/usrp2/eth_phy.h create mode 100644 firmware/microblaze/usrp2/ethernet.c create mode 100644 firmware/microblaze/usrp2p/eth_phy.h create mode 100644 firmware/microblaze/usrp2p/ethernet.c (limited to 'firmware/microblaze/usrp2') diff --git a/firmware/microblaze/lib/Makefile.inc b/firmware/microblaze/lib/Makefile.inc index 0e88298b8..02a267d35 100644 --- a/firmware/microblaze/lib/Makefile.inc +++ b/firmware/microblaze/lib/Makefile.inc @@ -25,7 +25,6 @@ COMMON_SRCS = \ $(top_srcdir)/lib/buffer_pool.c \ $(top_srcdir)/lib/dbsm.c \ $(top_srcdir)/lib/eeprom.c \ - $(top_srcdir)/lib/ethernet.c \ $(top_srcdir)/lib/eth_mac.c \ $(top_srcdir)/lib/_exit.c \ $(top_srcdir)/lib/exit.c \ diff --git a/firmware/microblaze/lib/eth_mac.c b/firmware/microblaze/lib/eth_mac.c index 375d3f4d4..cd0179deb 100644 --- a/firmware/microblaze/lib/eth_mac.c +++ b/firmware/microblaze/lib/eth_mac.c @@ -83,14 +83,6 @@ eth_mac_read_rmon(int addr) int eth_mac_miim_read(int addr) { - if (hwconfig_simulation_p()){ - switch(addr){ - case PHY_LINK_AN: - return LANSR_MASTER | LANSR_LINK_GOOD | LANSR_SPEED_1000; - default: - return 0; - } - } int phy_addr = PHY_ADDR; eth_mac->miiaddress = ((addr & 0x1f) << 8) | phy_addr; diff --git a/firmware/microblaze/lib/eth_phy.h b/firmware/microblaze/lib/eth_phy.h deleted file mode 100644 index 6c16f97b7..000000000 --- a/firmware/microblaze/lib/eth_phy.h +++ /dev/null @@ -1,219 +0,0 @@ -/* -*- c -*- */ -/* - * Copyright 2007 Free Software Foundation, Inc. - * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/* Much of this was extracted from the Linux e1000_hw.h file */ - -#ifndef INCLUDED_ETH_PHY_H -#define INCLUDED_ETH_PHY_H - -/* PHY 1000 MII Register/Bit Definitions */ -/* PHY Registers defined by IEEE */ - -#define PHY_CTRL 0x00 /* Control Register */ -#define PHY_STATUS 0x01 /* Status Regiser */ -#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ -#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ -#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ -#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ -#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ -#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ -#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ -#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ -#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ -#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ - -/* PHY 1000 MII Register additions in DP83856 */ -/* The part implements 0x00 thru 0x1f; we use these. */ - -#define PHY_LINK_AN 0x11 /* Link and Auto Negotiation Status Reg */ -#define PHY_INT_STATUS 0x14 /* Interupt Status Reg (RO) */ -#define PHY_INT_MASK 0x15 /* Interrupt Mask Reg (RW) */ -#define PHY_INT_CLEAR 0x17 /* Interrupt Clear Reg (RW) */ - - -/* Bit definitions for some of the registers above */ - -/* PHY Control Register (PHY_CTRL) */ -#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ -#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ -#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ -#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ -#define MII_CR_POWER_DOWN 0x0800 /* Power down */ -#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ -#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ -#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ -#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ - -/* PHY Status Register (PHY_STATUS) */ -#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ -#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ -#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ -#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ -#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ -#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ -#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ -#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ -#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ -#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ -#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ -#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ -#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ -#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ -#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ - -/* Autoneg Advertisement Register (PHY_AUTONEG_ADV) */ -#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ -#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ -#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ -#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ -#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ -#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ -#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ -#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ -#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ -#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ - -/* Link Partner Ability Register (Base Page) (PHY_LP_ABILITY) */ -#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ -#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ -#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ -#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ -#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ -#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ -#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ -#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ -#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ -#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ -#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ - -/* Autoneg Expansion Register (PHY_AUTONEG_EXP) */ -#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ -#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ -#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ -#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ -#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */ - -/* Next Page TX Register (PHY_NEXT_PAGE_TX) */ -#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ -#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges - * of different NP - */ -#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg - * 0 = cannot comply with msg - */ -#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ -#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow - * 0 = sending last NP - */ - -/* Link Partner Next Page Register (PHY_LP_NEXT_PAGE) */ -#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ -#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges - * of different NP - */ -#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg - * 0 = cannot comply with msg - */ -#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ -#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ -#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow - * 0 = sending last NP - */ - -/* 1000BASE-T Control Register (PHY_1000T_CTRL) */ -#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ -#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ -#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ -#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ - /* 0=DTE device */ -#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ - /* 0=Configure PHY as Slave */ -#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ - /* 0=Automatic Master/Slave config */ -#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ -#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ -#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ -#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ -#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ - -/* 1000BASE-T Status Register (PHY_1000T_STATUS) */ -#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ -#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ -#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ -#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ -#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ -#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ -#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ -#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ -#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 -#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 -#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 -#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20 -#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100 - -/* Extended Status Register (PHY_EXT_STATUS) */ -#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ -#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ -#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ -#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ - -#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ -#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ - -#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ - /* (0=enable, 1=disable) */ - -/* Link and Auto Negotiation Status Reg (PHY_LINK_AN) [READ-ONLY] */ -#define LANSR_MASTER 0x0001 /* 1=PHY is currently in master mode */ -#define LANSR_FULL_DUPLEX 0x0002 /* 1=PHY is currently full duplex */ -#define LANSR_LINK_GOOD 0x0004 /* 1=a good link is established */ -#define LANSR_SPEED_MASK 0x0018 -#define LANSR_SPEED_10 0x0000 /* 10Mb/s */ -#define LANSR_SPEED_100 0x0008 /* 100Mb/s */ -#define LANSR_SPEED_1000 0x0010 /* 1000Mb/s */ -#define LANSR_SPEED_RSRVD 0x0018 /* reserved */ -#define LANSR_NON_COMP_MODE 0x0020 /* 1=detects only in non-compliant mode */ -#define LANSR_DEEP_LOOPBACK 0x0040 /* 1=the PHY operates in deep loopback mode */ -#define LANSR_SHALLOW_LOOPBACK 0x0080 /* 1=the PHY operates in shallow loopback mode */ -#define LANSR_RSRVD_8 0x0100 /* reserved */ -#define LANSR_FIFO_ERR 0x0200 /* 1=FIFO error occurred */ -#define LANSR_MDIX_XOVER 0x0400 /* 1=PHY's MDI is in cross-over mode */ -#define LANSR_RSRVD_11 0x0800 /* resevered */ -#define LANSR_TP_POLARITY_REV 0xf000 /* Twisted pair polarity status A:D([15:12]) 1=reversed */ - -/* Interrupt status, mask and clear regs (PHY_INT_{STATUS,MASK,CLEAR}) */ -#define PHY_INT_RSRVD_0 0x0001 /* reserved */ -#define PHY_INT_RSRVD_1 0x0002 /* reserved */ -#define PHY_INT_RSRVD_2 0x0004 /* reserved */ -#define PHY_INT_REM_FLT_CNG 0x0008 /* Remote Fault Changed */ -#define PHY_INT_AN_CMPL 0x0010 /* Auto-negotiation completion */ -#define PHY_INT_NXT_PG_RCVD 0x0020 /* Next Page Received */ -#define PHY_INT_JABBER_CNG 0x0040 /* Jabber Changed */ -#define PHY_INT_NO_LINK 0x0080 /* No link after auto-negotiation */ -#define PHY_INT_NO_HCD 0x0100 /* AN couldn't determine highest common denominator */ -#define PHY_INT_MAS_SLA_ERR 0x0200 /* Master / Slave Error: couldn't resolve */ -#define PHY_INT_PRL_DET_FLT 0x0400 /* Parallel detection fault */ -#define PHY_INT_POL_CNG 0x0800 /* Polarity of any channel changed */ -#define PHY_INT_MDIX_CNG 0x1000 /* MDIX changed. A pair swap occurred. */ -#define PHY_INT_DPLX_CNG 0x2000 /* Duplex changed */ -#define PHY_INT_LNK_CNG 0x4000 /* Link changed (asserted when a link is established or broken) */ -#define PHY_INT_SPD_CNG 0x8000 /* Speed changed */ - -#endif /* INCLUDED_ETH_PHY_H */ diff --git a/firmware/microblaze/lib/ethernet.c b/firmware/microblaze/lib/ethernet.c deleted file mode 100644 index 2d7c4d7bd..000000000 --- a/firmware/microblaze/lib/ethernet.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright 2007 Free Software Foundation, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "ethernet.h" -#include "memory_map.h" -#include "eth_phy.h" -#include "eth_mac.h" -#include "eth_mac_regs.h" -#include "pic.h" -#include "hal_io.h" -#include "nonstdio.h" -#include -#include "i2c.h" -#include "usrp2/fw_common.h" - -#define VERBOSE 0 - -static ethernet_t ed_state; -static ethernet_link_changed_callback_t ed_callback = 0; - -void -ethernet_register_link_changed_callback(ethernet_link_changed_callback_t new_callback) -{ - ed_callback = new_callback; -} - - -static void -ed_set_mac_speed(int speed) -{ - printf("Speed set to %d\n",speed); - /* - switch(speed){ - case 10: - eth_mac->speed = 1; - break; - case 100: - eth_mac->speed = 2; - break; - case 1000: - eth_mac->speed = 4; - break; - default: - break; - } - */ -} - -static void -ed_link_up(int speed) -{ - // putstr("ed_link_up: "); puthex16_nl(speed); - - ed_set_mac_speed(speed); - - if (ed_callback) // fire link changed callback - (*ed_callback)(speed); -} - -static void -ed_link_down(void) -{ - // putstr("ed_link_down\n"); - - if (ed_callback) // fire link changed callback - (*ed_callback)(0); -} - - -static void -ed_link_speed_change(int speed) -{ - ed_link_down(); - ed_link_up(speed); -} - -static void -print_flow_control(int flow_control) -{ - static const char *flow_control_msg[4] = { - "NONE", "WE_TX", "WE_RX", "SYMMETRIC" - }; - putstr("ethernet flow control: "); - puts(flow_control_msg[flow_control & 0x3]); -} - -static void -check_flow_control_resolution(void) -{ - static const unsigned char table[16] = { - // index = {local_asm, local_pause, partner_asm, partner_pause} - FC_NONE, FC_NONE, FC_NONE, FC_NONE, - FC_NONE, FC_SYMM, FC_NONE, FC_SYMM, - FC_NONE, FC_NONE, FC_NONE, FC_WE_TX, - FC_NONE, FC_SYMM, FC_WE_RX, FC_SYMM - }; - - int us = eth_mac_miim_read(PHY_AUTONEG_ADV); - int lp = eth_mac_miim_read(PHY_LP_ABILITY); - int index = (((us >> 10) & 0x3) << 2) | ((lp >> 10) & 0x3); - ed_state.flow_control = table[index]; - - if (1) - print_flow_control(ed_state.flow_control); -} - -/* - * Read the PHY state register to determine link state and speed - */ -static void -ed_check_phy_state(void) -{ - int lansr = eth_mac_miim_read(PHY_LINK_AN); - eth_link_state_t new_state = LS_UNKNOWN; - int new_speed = S_UNKNOWN; - - if (VERBOSE){ - putstr("LANSR: "); - puthex16_nl(lansr); - } - - if (lansr & LANSR_LINK_GOOD){ // link's up - if (VERBOSE) - puts(" LINK_GOOD"); - - new_state = LS_UP; - switch (lansr & LANSR_SPEED_MASK){ - case LANSR_SPEED_10: - new_speed = 10; - break; - - case LANSR_SPEED_100: - new_speed = 100; - break; - - case LANSR_SPEED_1000: - new_speed = 1000; - break; - - default: - new_speed = S_UNKNOWN; - break; - } - - check_flow_control_resolution(); - } - else { // link's down - if (VERBOSE) - puts(" NOT LINK_GOOD"); - - new_state = LS_DOWN; - new_speed = S_UNKNOWN; - } - - if (new_state != ed_state.link_state){ - ed_state.link_state = new_state; // remember new state - if (new_state == LS_UP) - ed_link_up(new_speed); - else if (new_state == LS_DOWN) - ed_link_down(); - } - else if (new_state == LS_UP && new_speed != ed_state.link_speed){ - ed_state.link_speed = new_speed; // remember new speed - ed_link_speed_change(new_speed); - } -} - -/* - * This is fired when the ethernet PHY state changes - */ -static void -eth_phy_irq_handler(unsigned irq) -{ - ed_check_phy_state(); - eth_mac_miim_write(PHY_INT_CLEAR, ~0); // clear all ints -} - -void -ethernet_init(void) -{ - eth_mac_init(ethernet_mac_addr()); - - ed_state.link_state = LS_UNKNOWN; - ed_state.link_speed = S_UNKNOWN; - - // initialize MAC registers - // eth_mac->tx_hwmark = 0x1e; - //eth_mac->tx_lwmark = 0x19; - - //eth_mac->crc_chk_en = 1; - //eth_mac->rx_max_length = 2048; - - // configure PAUSE frame stuff - //eth_mac->tx_pause_en = 1; // pay attn to pause frames sent to us - - //eth_mac->pause_quanta_set = 38; // a bit more than 1 max frame 16kb/512 + fudge - //eth_mac->pause_frame_send_en = 1; // enable sending pause frames - - - // setup PHY to interrupt on changes - - unsigned mask = - (PHY_INT_AN_CMPL // auto-neg completed - | PHY_INT_NO_LINK // no link after auto-neg - | PHY_INT_NO_HCD // no highest common denominator - | PHY_INT_MAS_SLA_ERR // couldn't resolve master/slave - | PHY_INT_PRL_DET_FLT // parallel detection fault - | PHY_INT_LNK_CNG // link established or broken - | PHY_INT_SPD_CNG // speed changed - ); - - eth_mac_miim_write(PHY_INT_CLEAR, ~0); // clear all pending interrupts - eth_mac_miim_write(PHY_INT_MASK, mask); // enable the ones we want - - pic_register_handler(IRQ_PHY, eth_phy_irq_handler); - - // Advertise our flow control configuation. - // - // We and the link partner each specify two bits in the base page - // related to autoconfiguration: NWAY_AR_PAUSE and NWAY_AR_ASM_DIR. - // The bits say what a device is "willing" to do, not what may actually - // happen as a result of the negotiation. There are 4 cases: - // - // PAUSE ASM_DIR - // - // 0 0 I have no flow control capability. - // - // 1 0 I both assert and respond to flow control. - // - // 0 1 I assert flow control, but cannot respond. That is, - // I want to be able to send PAUSE frames, but will ignore any - // you send to me. (This is our configuration.) - // - // 1 1 I can both assert and respond to flow control AND I am willing - // to operate symmetrically OR asymmetrically in EITHER direction. - // (We hope the link partner advertises this, otherwise we don't - // get what we want.) - - int t = eth_mac_miim_read(PHY_AUTONEG_ADV); - t &= ~(NWAY_AR_PAUSE | NWAY_AR_ASM_DIR); - t |= NWAY_AR_ASM_DIR; - - // Say we can't to 10BASE-T or 100BASE-TX, half or full duplex - t &= ~(NWAY_AR_10T_HD_CAPS | NWAY_AR_10T_FD_CAPS | NWAY_AR_100TX_HD_CAPS | NWAY_AR_100TX_FD_CAPS); - - eth_mac_miim_write(PHY_AUTONEG_ADV, t); - int r = eth_mac_miim_read(PHY_AUTONEG_ADV); // DEBUG, read back - if (t != r){ - printf("PHY_AUTONEG_ADV: wrote 0x%x, got 0x%x\n", t, r); - } - - // Restart autonegotation. - // We want to ensure that we're advertising our PAUSE capabilities. - t = eth_mac_miim_read(PHY_CTRL); - eth_mac_miim_write(PHY_CTRL, t | MII_CR_RESTART_AUTO_NEG); -} - -static bool -unprogrammed(const void *t, size_t len) -{ - int i; - uint8_t *p = (uint8_t *)t; - bool all_zeros = true; - bool all_ones = true; - for (i = 0; i < len; i++){ - all_zeros &= p[i] == 0x00; - all_ones &= p[i] == 0xff; - } - return all_ones | all_zeros; -} - -//////////////////// MAC Addr Stuff /////////////////////// - -static int8_t src_mac_addr_initialized = false; -static eth_mac_addr_t src_mac_addr = {{ - 0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff - }}; - -const eth_mac_addr_t * -ethernet_mac_addr(void) -{ - if (!src_mac_addr_initialized){ // fetch from eeprom - src_mac_addr_initialized = true; - - // if we're simulating, don't read the EEPROM model, it's REALLY slow - if (hwconfig_simulation_p()) - return &src_mac_addr; - - eth_mac_addr_t tmp; - bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, &tmp, sizeof(tmp)); - if (!ok || unprogrammed(&tmp, sizeof(tmp))){ - // use the default - } - else - src_mac_addr = tmp; - } - - return &src_mac_addr; -} - -bool -ethernet_set_mac_addr(const eth_mac_addr_t *t) -{ - bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, t, sizeof(eth_mac_addr_t)); - if (ok){ - src_mac_addr = *t; - src_mac_addr_initialized = true; - //eth_mac_set_addr(t); //this breaks the link - } - - return ok; -} - -//////////////////// IP Addr Stuff /////////////////////// - -static int8_t src_ip_addr_initialized = false; -static struct ip_addr src_ip_addr = { - (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) -}; - - -const struct ip_addr *get_ip_addr(void) -{ - if (!src_ip_addr_initialized){ // fetch from eeprom - src_ip_addr_initialized = true; - - // if we're simulating, don't read the EEPROM model, it's REALLY slow - if (hwconfig_simulation_p()) - return &src_ip_addr; - - struct ip_addr tmp; - bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, &tmp, sizeof(tmp)); - if (!ok || unprogrammed(&tmp, sizeof(tmp))){ - // use the default - } - else - src_ip_addr = tmp; - } - - return &src_ip_addr; -} - -bool set_ip_addr(const struct ip_addr *t){ - bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, t, sizeof(struct ip_addr)); - if (ok){ - src_ip_addr = *t; - src_ip_addr_initialized = true; - } - - return ok; -} - -int -ethernet_check_errors(void) -{ - // these registers are reset when read - - int r = 0; - /* - if (eth_mac_read_rmon(0x05) != 0) - r |= RME_RX_CRC; - if (eth_mac_read_rmon(0x06) != 0) - r |= RME_RX_FIFO_FULL; - if (eth_mac_read_rmon(0x07) != 0) - r |= RME_RX_2SHORT_2LONG; - - if (eth_mac_read_rmon(0x25) != 0) - r |= RME_TX_JAM_DROP; - if (eth_mac_read_rmon(0x26) != 0) - r |= RME_TX_FIFO_UNDER; - if (eth_mac_read_rmon(0x27) != 0) - r |= RME_TX_FIFO_OVER; - */ - return r; -} diff --git a/firmware/microblaze/usrp2/Makefile.am b/firmware/microblaze/usrp2/Makefile.am index 859ded9e5..b45d2481b 100644 --- a/firmware/microblaze/usrp2/Makefile.am +++ b/firmware/microblaze/usrp2/Makefile.am @@ -34,7 +34,8 @@ noinst_LIBRARIES = libusrp2.a libusrp2_a_SOURCES = \ $(COMMON_SRCS) \ clocks.c \ - sd.c + sd.c \ + ethernet.c noinst_PROGRAMS = \ usrp2_txrx_uhd.elf diff --git a/firmware/microblaze/usrp2/eth_phy.h b/firmware/microblaze/usrp2/eth_phy.h new file mode 100644 index 000000000..6c16f97b7 --- /dev/null +++ b/firmware/microblaze/usrp2/eth_phy.h @@ -0,0 +1,219 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Much of this was extracted from the Linux e1000_hw.h file */ + +#ifndef INCLUDED_ETH_PHY_H +#define INCLUDED_ETH_PHY_H + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ + +#define PHY_CTRL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Regiser */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +/* PHY 1000 MII Register additions in DP83856 */ +/* The part implements 0x00 thru 0x1f; we use these. */ + +#define PHY_LINK_AN 0x11 /* Link and Auto Negotiation Status Reg */ +#define PHY_INT_STATUS 0x14 /* Interupt Status Reg (RO) */ +#define PHY_INT_MASK 0x15 /* Interrupt Mask Reg (RW) */ +#define PHY_INT_CLEAR 0x17 /* Interrupt Clear Reg (RW) */ + + +/* Bit definitions for some of the registers above */ + +/* PHY Control Register (PHY_CTRL) */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ + +/* PHY Status Register (PHY_STATUS) */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register (PHY_AUTONEG_ADV) */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) (PHY_LP_ABILITY) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register (PHY_AUTONEG_EXP) */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */ + +/* Next Page TX Register (PHY_NEXT_PAGE_TX) */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* Link Partner Next Page Register (PHY_LP_NEXT_PAGE) */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* 1000BASE-T Control Register (PHY_1000T_CTRL) */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ +#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register (PHY_1000T_STATUS) */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 +#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100 + +/* Extended Status Register (PHY_EXT_STATUS) */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ + +#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ + /* (0=enable, 1=disable) */ + +/* Link and Auto Negotiation Status Reg (PHY_LINK_AN) [READ-ONLY] */ +#define LANSR_MASTER 0x0001 /* 1=PHY is currently in master mode */ +#define LANSR_FULL_DUPLEX 0x0002 /* 1=PHY is currently full duplex */ +#define LANSR_LINK_GOOD 0x0004 /* 1=a good link is established */ +#define LANSR_SPEED_MASK 0x0018 +#define LANSR_SPEED_10 0x0000 /* 10Mb/s */ +#define LANSR_SPEED_100 0x0008 /* 100Mb/s */ +#define LANSR_SPEED_1000 0x0010 /* 1000Mb/s */ +#define LANSR_SPEED_RSRVD 0x0018 /* reserved */ +#define LANSR_NON_COMP_MODE 0x0020 /* 1=detects only in non-compliant mode */ +#define LANSR_DEEP_LOOPBACK 0x0040 /* 1=the PHY operates in deep loopback mode */ +#define LANSR_SHALLOW_LOOPBACK 0x0080 /* 1=the PHY operates in shallow loopback mode */ +#define LANSR_RSRVD_8 0x0100 /* reserved */ +#define LANSR_FIFO_ERR 0x0200 /* 1=FIFO error occurred */ +#define LANSR_MDIX_XOVER 0x0400 /* 1=PHY's MDI is in cross-over mode */ +#define LANSR_RSRVD_11 0x0800 /* resevered */ +#define LANSR_TP_POLARITY_REV 0xf000 /* Twisted pair polarity status A:D([15:12]) 1=reversed */ + +/* Interrupt status, mask and clear regs (PHY_INT_{STATUS,MASK,CLEAR}) */ +#define PHY_INT_RSRVD_0 0x0001 /* reserved */ +#define PHY_INT_RSRVD_1 0x0002 /* reserved */ +#define PHY_INT_RSRVD_2 0x0004 /* reserved */ +#define PHY_INT_REM_FLT_CNG 0x0008 /* Remote Fault Changed */ +#define PHY_INT_AN_CMPL 0x0010 /* Auto-negotiation completion */ +#define PHY_INT_NXT_PG_RCVD 0x0020 /* Next Page Received */ +#define PHY_INT_JABBER_CNG 0x0040 /* Jabber Changed */ +#define PHY_INT_NO_LINK 0x0080 /* No link after auto-negotiation */ +#define PHY_INT_NO_HCD 0x0100 /* AN couldn't determine highest common denominator */ +#define PHY_INT_MAS_SLA_ERR 0x0200 /* Master / Slave Error: couldn't resolve */ +#define PHY_INT_PRL_DET_FLT 0x0400 /* Parallel detection fault */ +#define PHY_INT_POL_CNG 0x0800 /* Polarity of any channel changed */ +#define PHY_INT_MDIX_CNG 0x1000 /* MDIX changed. A pair swap occurred. */ +#define PHY_INT_DPLX_CNG 0x2000 /* Duplex changed */ +#define PHY_INT_LNK_CNG 0x4000 /* Link changed (asserted when a link is established or broken) */ +#define PHY_INT_SPD_CNG 0x8000 /* Speed changed */ + +#endif /* INCLUDED_ETH_PHY_H */ diff --git a/firmware/microblaze/usrp2/ethernet.c b/firmware/microblaze/usrp2/ethernet.c new file mode 100644 index 000000000..d60d7dc4c --- /dev/null +++ b/firmware/microblaze/usrp2/ethernet.c @@ -0,0 +1,389 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "ethernet.h" +#include "memory_map.h" +#include "eth_phy.h" +#include "eth_mac.h" +#include "eth_mac_regs.h" +#include "pic.h" +#include "hal_io.h" +#include "nonstdio.h" +#include +#include "i2c.h" +#include "usrp2/fw_common.h" + +#define VERBOSE 1 + +static ethernet_t ed_state; +static ethernet_link_changed_callback_t ed_callback = 0; + +void +ethernet_register_link_changed_callback(ethernet_link_changed_callback_t new_callback) +{ + ed_callback = new_callback; +} + + +static void +ed_set_mac_speed(int speed) +{ + printf("Speed set to %d\n",speed); + /* + switch(speed){ + case 10: + eth_mac->speed = 1; + break; + case 100: + eth_mac->speed = 2; + break; + case 1000: + eth_mac->speed = 4; + break; + default: + break; + } + */ +} + +static void +ed_link_up(int speed) +{ + // putstr("ed_link_up: "); puthex16_nl(speed); + + ed_set_mac_speed(speed); + + if (ed_callback) // fire link changed callback + (*ed_callback)(speed); +} + +static void +ed_link_down(void) +{ + // putstr("ed_link_down\n"); + + if (ed_callback) // fire link changed callback + (*ed_callback)(0); +} + + +static void +ed_link_speed_change(int speed) +{ + ed_link_down(); + ed_link_up(speed); +} + +static void +print_flow_control(int flow_control) +{ + static const char *flow_control_msg[4] = { + "NONE", "WE_TX", "WE_RX", "SYMMETRIC" + }; + putstr("ethernet flow control: "); + puts(flow_control_msg[flow_control & 0x3]); +} + +static void +check_flow_control_resolution(void) +{ + static const unsigned char table[16] = { + // index = {local_asm, local_pause, partner_asm, partner_pause} + FC_NONE, FC_NONE, FC_NONE, FC_NONE, + FC_NONE, FC_SYMM, FC_NONE, FC_SYMM, + FC_NONE, FC_NONE, FC_NONE, FC_WE_TX, + FC_NONE, FC_SYMM, FC_WE_RX, FC_SYMM + }; + + int us = eth_mac_miim_read(PHY_AUTONEG_ADV); + int lp = eth_mac_miim_read(PHY_LP_ABILITY); + int index = (((us >> 10) & 0x3) << 2) | ((lp >> 10) & 0x3); + ed_state.flow_control = table[index]; + + if (1) + print_flow_control(ed_state.flow_control); +} + +/* + * Read the PHY state register to determine link state and speed + */ +static void +ed_check_phy_state(void) +{ + int lansr = eth_mac_miim_read(PHY_LINK_AN); + eth_link_state_t new_state = LS_UNKNOWN; + int new_speed = S_UNKNOWN; + + if (VERBOSE){ + putstr("LANSR: "); + puthex16_nl(lansr); + } + + if (lansr & LANSR_LINK_GOOD){ // link's up + if (VERBOSE) + puts(" LINK_GOOD"); + + new_state = LS_UP; + switch (lansr & LANSR_SPEED_MASK){ + case LANSR_SPEED_10: + new_speed = 10; + break; + + case LANSR_SPEED_100: + new_speed = 100; + break; + + case LANSR_SPEED_1000: + new_speed = 1000; + break; + + default: + new_speed = S_UNKNOWN; + break; + } + + check_flow_control_resolution(); + } + else { // link's down + if (VERBOSE) + puts(" NOT LINK_GOOD"); + + new_state = LS_DOWN; + new_speed = S_UNKNOWN; + } + + if (new_state != ed_state.link_state){ + ed_state.link_state = new_state; // remember new state + if (new_state == LS_UP) + ed_link_up(new_speed); + else if (new_state == LS_DOWN) + ed_link_down(); + } + else if (new_state == LS_UP && new_speed != ed_state.link_speed){ + ed_state.link_speed = new_speed; // remember new speed + ed_link_speed_change(new_speed); + } +} + +/* + * This is fired when the ethernet PHY state changes + */ +static void +eth_phy_irq_handler(unsigned irq) +{ + ed_check_phy_state(); + eth_mac_miim_write(PHY_INT_CLEAR, ~0); // clear all ints +} + +void +ethernet_init(void) +{ + eth_mac_init(ethernet_mac_addr()); + + ed_state.link_state = LS_UNKNOWN; + ed_state.link_speed = S_UNKNOWN; + + // initialize MAC registers + // eth_mac->tx_hwmark = 0x1e; + //eth_mac->tx_lwmark = 0x19; + + //eth_mac->crc_chk_en = 1; + //eth_mac->rx_max_length = 2048; + + // configure PAUSE frame stuff + //eth_mac->tx_pause_en = 1; // pay attn to pause frames sent to us + + //eth_mac->pause_quanta_set = 38; // a bit more than 1 max frame 16kb/512 + fudge + //eth_mac->pause_frame_send_en = 1; // enable sending pause frames + + + // setup PHY to interrupt on changes + + unsigned mask = + (PHY_INT_AN_CMPL // auto-neg completed + | PHY_INT_NO_LINK // no link after auto-neg + | PHY_INT_NO_HCD // no highest common denominator + | PHY_INT_MAS_SLA_ERR // couldn't resolve master/slave + | PHY_INT_PRL_DET_FLT // parallel detection fault + | PHY_INT_LNK_CNG // link established or broken + | PHY_INT_SPD_CNG // speed changed + ); + + eth_mac_miim_write(PHY_INT_CLEAR, ~0); // clear all pending interrupts + eth_mac_miim_write(PHY_INT_MASK, mask); // enable the ones we want + + pic_register_handler(IRQ_PHY, eth_phy_irq_handler); + + // Advertise our flow control configuation. + // + // We and the link partner each specify two bits in the base page + // related to autoconfiguration: NWAY_AR_PAUSE and NWAY_AR_ASM_DIR. + // The bits say what a device is "willing" to do, not what may actually + // happen as a result of the negotiation. There are 4 cases: + // + // PAUSE ASM_DIR + // + // 0 0 I have no flow control capability. + // + // 1 0 I both assert and respond to flow control. + // + // 0 1 I assert flow control, but cannot respond. That is, + // I want to be able to send PAUSE frames, but will ignore any + // you send to me. (This is our configuration.) + // + // 1 1 I can both assert and respond to flow control AND I am willing + // to operate symmetrically OR asymmetrically in EITHER direction. + // (We hope the link partner advertises this, otherwise we don't + // get what we want.) + + int t = eth_mac_miim_read(PHY_AUTONEG_ADV); + t &= ~(NWAY_AR_PAUSE | NWAY_AR_ASM_DIR); + t |= NWAY_AR_ASM_DIR; + + // Say we can't to 10BASE-T or 100BASE-TX, half or full duplex + t &= ~(NWAY_AR_10T_HD_CAPS | NWAY_AR_10T_FD_CAPS | NWAY_AR_100TX_HD_CAPS | NWAY_AR_100TX_FD_CAPS); + + eth_mac_miim_write(PHY_AUTONEG_ADV, t); + int r = eth_mac_miim_read(PHY_AUTONEG_ADV); // DEBUG, read back + if (t != r){ + printf("PHY_AUTONEG_ADV: wrote 0x%x, got 0x%x\n", t, r); + } + + // Restart autonegotation. + // We want to ensure that we're advertising our PAUSE capabilities. + t = eth_mac_miim_read(PHY_CTRL); + eth_mac_miim_write(PHY_CTRL, t | MII_CR_RESTART_AUTO_NEG); +} + +static bool +unprogrammed(const void *t, size_t len) +{ + int i; + uint8_t *p = (uint8_t *)t; + bool all_zeros = true; + bool all_ones = true; + for (i = 0; i < len; i++){ + all_zeros &= p[i] == 0x00; + all_ones &= p[i] == 0xff; + } + return all_ones | all_zeros; +} + +//////////////////// MAC Addr Stuff /////////////////////// + +static int8_t src_mac_addr_initialized = false; +static eth_mac_addr_t src_mac_addr = {{ + 0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff + }}; + +const eth_mac_addr_t * +ethernet_mac_addr(void) +{ + if (!src_mac_addr_initialized){ // fetch from eeprom + src_mac_addr_initialized = true; + + // if we're simulating, don't read the EEPROM model, it's REALLY slow + if (hwconfig_simulation_p()) + return &src_mac_addr; + + eth_mac_addr_t tmp; + bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, &tmp, sizeof(tmp)); + if (!ok || unprogrammed(&tmp, sizeof(tmp))){ + // use the default + } + else + src_mac_addr = tmp; + } + + return &src_mac_addr; +} + +bool +ethernet_set_mac_addr(const eth_mac_addr_t *t) +{ + bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, t, sizeof(eth_mac_addr_t)); + if (ok){ + src_mac_addr = *t; + src_mac_addr_initialized = true; + //eth_mac_set_addr(t); //this breaks the link + } + + return ok; +} + +//////////////////// IP Addr Stuff /////////////////////// + +static int8_t src_ip_addr_initialized = false; +static struct ip_addr src_ip_addr = { + (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) +}; + + +const struct ip_addr *get_ip_addr(void) +{ + if (!src_ip_addr_initialized){ // fetch from eeprom + src_ip_addr_initialized = true; + + // if we're simulating, don't read the EEPROM model, it's REALLY slow + if (hwconfig_simulation_p()) + return &src_ip_addr; + + struct ip_addr tmp; + bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, &tmp, sizeof(tmp)); + if (!ok || unprogrammed(&tmp, sizeof(tmp))){ + // use the default + } + else + src_ip_addr = tmp; + } + + return &src_ip_addr; +} + +bool set_ip_addr(const struct ip_addr *t){ + bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, t, sizeof(struct ip_addr)); + if (ok){ + src_ip_addr = *t; + src_ip_addr_initialized = true; + } + + return ok; +} + +int +ethernet_check_errors(void) +{ + // these registers are reset when read + + int r = 0; + /* + if (eth_mac_read_rmon(0x05) != 0) + r |= RME_RX_CRC; + if (eth_mac_read_rmon(0x06) != 0) + r |= RME_RX_FIFO_FULL; + if (eth_mac_read_rmon(0x07) != 0) + r |= RME_RX_2SHORT_2LONG; + + if (eth_mac_read_rmon(0x25) != 0) + r |= RME_TX_JAM_DROP; + if (eth_mac_read_rmon(0x26) != 0) + r |= RME_TX_FIFO_UNDER; + if (eth_mac_read_rmon(0x27) != 0) + r |= RME_TX_FIFO_OVER; + */ + return r; +} diff --git a/firmware/microblaze/usrp2p/Makefile.am b/firmware/microblaze/usrp2p/Makefile.am index eff544294..86affc1f3 100644 --- a/firmware/microblaze/usrp2p/Makefile.am +++ b/firmware/microblaze/usrp2p/Makefile.am @@ -49,7 +49,8 @@ libusrp2p_a_SOURCES = \ spif.c \ spi_flash.c \ spi_flash_read.c \ - bootloader_utils.c + bootloader_utils.c \ + ethernet.c noinst_PROGRAMS = \ usrp2p_txrx_uhd.elf \ diff --git a/firmware/microblaze/usrp2p/eth_phy.h b/firmware/microblaze/usrp2p/eth_phy.h new file mode 100644 index 000000000..9328debee --- /dev/null +++ b/firmware/microblaze/usrp2p/eth_phy.h @@ -0,0 +1,210 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* Much of this was extracted from the Linux e1000_hw.h file */ + +#ifndef INCLUDED_ETH_PHY_H +#define INCLUDED_ETH_PHY_H + +/* PHY 1000 MII Register/Bit Definitions */ +/* PHY Registers defined by IEEE */ + +#define PHY_CTRL 0x00 /* Control Register */ +#define PHY_STATUS 0x01 /* Status Regiser */ +#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */ +#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */ +#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */ +#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */ +#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */ +#define PHY_NEXT_PAGE_TX 0x07 /* Next Page TX */ +#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */ +#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */ +#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ +#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ + +/* PHY 1000 MII Register additions in ET1011C */ +#define PHY_INT_MASK 24 +#define PHY_INT_STATUS 25 +#define PHY_PHY_STATUS 26 + +/* Bit definitions for some of the registers above */ + +/* PHY Control Register (PHY_CTRL) */ +#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */ +#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ +#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ +#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */ +#define MII_CR_POWER_DOWN 0x0800 /* Power down */ +#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */ +#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */ +#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ + +/* PHY Status Register (PHY_STATUS) */ +#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */ +#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */ +#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */ +#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */ +#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */ +#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */ +#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */ +#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */ +#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */ +#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */ +#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */ +#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */ +#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */ +#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */ +#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */ + +/* Autoneg Advertisement Register (PHY_AUTONEG_ADV) */ +#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */ +#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */ +#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */ +#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */ +#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */ +#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */ +#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */ +#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */ +#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */ +#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Link Partner Ability Register (Base Page) (PHY_LP_ABILITY) */ +#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */ +#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP is 10T Half Duplex Capable */ +#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP is 10T Full Duplex Capable */ +#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP is 100TX Half Duplex Capable */ +#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP is 100TX Full Duplex Capable */ +#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */ +#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */ +#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asymmetric Pause Direction bit */ +#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP has detected Remote Fault */ +#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP has rx'd link code word */ +#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */ + +/* Autoneg Expansion Register (PHY_AUTONEG_EXP) */ +#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */ +#define NWAY_ER_PAGE_RXD 0x0002 /* LP is 10T Half Duplex Capable */ +#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP is 10T Full Duplex Capable */ +#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP is 100TX Half Duplex Capable */ +#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP is 100TX Full Duplex Capable */ + +/* Next Page TX Register (PHY_NEXT_PAGE_TX) */ +#define NPTX_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define NPTX_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define NPTX_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define NPTX_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define NPTX_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* Link Partner Next Page Register (PHY_LP_NEXT_PAGE) */ +#define LP_RNPR_MSG_CODE_FIELD 0x0001 /* NP msg code or unformatted data */ +#define LP_RNPR_TOGGLE 0x0800 /* Toggles between exchanges + * of different NP + */ +#define LP_RNPR_ACKNOWLDGE2 0x1000 /* 1 = will comply with msg + * 0 = cannot comply with msg + */ +#define LP_RNPR_MSG_PAGE 0x2000 /* formatted(1)/unformatted(0) pg */ +#define LP_RNPR_ACKNOWLDGE 0x4000 /* 1 = ACK / 0 = NO ACK */ +#define LP_RNPR_NEXT_PAGE 0x8000 /* 1 = addition NP will follow + * 0 = sending last NP + */ + +/* 1000BASE-T Control Register (PHY_1000T_CTRL) */ +#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */ +#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */ +#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */ +#define CR_1000T_REPEATER_DTE 0x0400 /* 1=Repeater/switch device port */ + /* 0=DTE device */ +#define CR_1000T_MS_VALUE 0x0800 /* 1=Configure PHY as Master */ + /* 0=Configure PHY as Slave */ +#define CR_1000T_MS_ENABLE 0x1000 /* 1=Master/Slave manual config value */ + /* 0=Automatic Master/Slave config */ +#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */ +#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */ +#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */ +#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */ +#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */ + +/* 1000BASE-T Status Register (PHY_1000T_STATUS) */ +#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle errors since last read */ +#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asymmetric pause direction bit */ +#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */ +#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */ +#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */ +#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */ +#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local TX is Master, 0=Slave */ +#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */ +#define SR_1000T_REMOTE_RX_STATUS_SHIFT 12 +#define SR_1000T_LOCAL_RX_STATUS_SHIFT 13 +#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_20 20 +#define FFE_IDLE_ERR_COUNT_TIMEOUT_100 100 + +/* Extended Status Register (PHY_EXT_STATUS) */ +#define IEEE_ESR_1000T_HD_CAPS 0x1000 /* 1000T HD capable */ +#define IEEE_ESR_1000T_FD_CAPS 0x2000 /* 1000T FD capable */ +#define IEEE_ESR_1000X_HD_CAPS 0x4000 /* 1000X HD capable */ +#define IEEE_ESR_1000X_FD_CAPS 0x8000 /* 1000X FD capable */ + +#define PHY_TX_POLARITY_MASK 0x0100 /* register 10h bit 8 (polarity bit) */ +#define PHY_TX_NORMAL_POLARITY 0 /* register 10h bit 8 (normal polarity) */ + +#define AUTO_POLARITY_DISABLE 0x0010 /* register 11h bit 4 */ + /* (0=enable, 1=disable) */ + +/* PHY Status Register (PHY_PHY_STATUS) */ +#define PHYSTAT_ASYMMETRIC (1 << 0) +#define PHYSTAT_PAUSE (1 << 1) +#define PHYSTAT_AUTONEG_EN (1 << 2) +#define PHYSTAT_COLLISION (1 << 3) +#define PHYSTAT_RXSTAT (1 << 4) +#define PHYSTAT_TXSTAT (1 << 5) +#define PHYSTAT_LINK (1 << 6) +#define PHYSTAT_DUPLEX (1 << 7) +#define PHYSTAT_SPEED_MASK ((1 << 8) | (1 << 9)) +#define PHYSTAT_SPEED_1000 (1 << 9) +#define PHYSTAT_SPEED_100 (1 << 8) +#define PHYSTAT_SPEED_10 0 +#define PHYSTAT_POLARITY (1 << 10) +#define PHYSTAT_MDIX (1 << 11) +#define PHYSTAT_AUTONEG_STAT (1 << 12) +#define PHYSTAT_STANDBY (1 << 13) + +/* Interrupt status, mask and clear regs (PHY_INT_{STATUS,MASK,CLEAR}) */ +#define PHY_INT_ENABLE (1 << 0) +#define PHY_INT_DOWNSHIFT (1 << 1) +#define PHY_INT_LINK_STATUS_CHANGE (1 << 2) +#define PHY_INT_RX_STATUS_CHANGE (1 << 3) +#define PHY_INT_FIFO_ERROR (1 << 4) +#define PHY_INT_ERR_CTR_FULL (1 << 5) +#define PHY_INT_NEXT_PAGE_RX (1 << 6) +#define PHY_INT_CRC_ERROR (1 << 7) +#define PHY_INT_AUTONEG_STATUS_CHANGE (1 << 8) +#define PHY_INT_MDIO_SYNC_LOST (1 << 9) +#define PHY_INT_TDR_IP_PHONE (1 << 10) + +#endif /* INCLUDED_ETH_PHY_H */ diff --git a/firmware/microblaze/usrp2p/ethernet.c b/firmware/microblaze/usrp2p/ethernet.c new file mode 100644 index 000000000..134650326 --- /dev/null +++ b/firmware/microblaze/usrp2p/ethernet.c @@ -0,0 +1,388 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//Changes for USRP2P: status registers different (ethernet.h) + +#include "ethernet.h" +#include "memory_map.h" +#include "eth_phy.h" +#include +#include +#include +#include +#include +#include +#include +#include "usrp2/fw_common.h" + +#define VERBOSE 1 + +static ethernet_t ed_state; +static ethernet_link_changed_callback_t ed_callback = 0; + +void +ethernet_register_link_changed_callback(ethernet_link_changed_callback_t new_callback) +{ + ed_callback = new_callback; +} + + +static void +ed_set_mac_speed(int speed) +{ + printf("Speed set to %d\n",speed); + /* + switch(speed){ + case 10: + eth_mac->speed = 1; + break; + case 100: + eth_mac->speed = 2; + break; + case 1000: + eth_mac->speed = 4; + break; + default: + break; + } + */ +} + +static void +ed_link_up(int speed) +{ + // putstr("ed_link_up: "); puthex16_nl(speed); + + ed_set_mac_speed(speed); + + if (ed_callback) // fire link changed callback + (*ed_callback)(speed); +} + +static void +ed_link_down(void) +{ + // putstr("ed_link_down\n"); + + if (ed_callback) // fire link changed callback + (*ed_callback)(0); +} + + +static void +ed_link_speed_change(int speed) +{ + ed_link_down(); + ed_link_up(speed); +} + +static void +print_flow_control(int flow_control) +{ + static const char *flow_control_msg[4] = { + "NONE", "WE_TX", "WE_RX", "SYMMETRIC" + }; + putstr("ethernet flow control: "); + puts(flow_control_msg[flow_control & 0x3]); +} + +static void +check_flow_control_resolution(void) +{ + static const unsigned char table[16] = { + // index = {local_asm, local_pause, partner_asm, partner_pause} + FC_NONE, FC_NONE, FC_NONE, FC_NONE, + FC_NONE, FC_SYMM, FC_NONE, FC_SYMM, + FC_NONE, FC_NONE, FC_NONE, FC_WE_TX, + FC_NONE, FC_SYMM, FC_WE_RX, FC_SYMM + }; + + int us = eth_mac_miim_read(PHY_AUTONEG_ADV); + int lp = eth_mac_miim_read(PHY_LP_ABILITY); + int index = (((us >> 10) & 0x3) << 2) | ((lp >> 10) & 0x3); + ed_state.flow_control = table[index]; + + if (1) + print_flow_control(ed_state.flow_control); +} + +/* + * Read the PHY state register to determine link state and speed + */ +static void +ed_check_phy_state(void) +{ + int phystat = eth_mac_miim_read(PHY_PHY_STATUS); + eth_link_state_t new_state = LS_UNKNOWN; + int new_speed = S_UNKNOWN; + + if (VERBOSE){ + putstr("PHYSTAT: "); + puthex16_nl(phystat); + } + + if (phystat & PHYSTAT_LINK){ // link's up + if (VERBOSE) + puts(" LINK_GOOD"); + + new_state = LS_UP; + switch (phystat & PHYSTAT_SPEED_MASK){ + case PHYSTAT_SPEED_10: + new_speed = 10; + break; + + case PHYSTAT_SPEED_100: + new_speed = 100; + break; + + case PHYSTAT_SPEED_1000: + new_speed = 1000; + break; + + default: + new_speed = S_UNKNOWN; + break; + } + + check_flow_control_resolution(); + } + else { // link's down + if (VERBOSE) + puts(" NOT LINK_GOOD"); + + new_state = LS_DOWN; + new_speed = S_UNKNOWN; + } + + if (new_state != ed_state.link_state){ + ed_state.link_state = new_state; // remember new state + if (new_state == LS_UP) + ed_link_up(new_speed); + else if (new_state == LS_DOWN) + ed_link_down(); + } + else if (new_state == LS_UP && new_speed != ed_state.link_speed){ + ed_state.link_speed = new_speed; // remember new speed + ed_link_speed_change(new_speed); + } +} + +/* + * This is fired when the ethernet PHY state changes + */ +static void +eth_phy_irq_handler(unsigned irq) +{ + ed_check_phy_state(); + eth_mac_miim_read(PHY_INT_STATUS); +// eth_mac_miim_write(PHY_INT_CLEAR, ~0); // clear all ints +} + +void +ethernet_init(void) +{ + eth_mac_init(ethernet_mac_addr()); + + ed_state.link_state = LS_UNKNOWN; + ed_state.link_speed = S_UNKNOWN; + + // initialize MAC registers + // eth_mac->tx_hwmark = 0x1e; + //eth_mac->tx_lwmark = 0x19; + + //eth_mac->crc_chk_en = 1; + //eth_mac->rx_max_length = 2048; + + // configure PAUSE frame stuff + //eth_mac->tx_pause_en = 1; // pay attn to pause frames sent to us + + //eth_mac->pause_quanta_set = 38; // a bit more than 1 max frame 16kb/512 + fudge + //eth_mac->pause_frame_send_en = 1; // enable sending pause frames + + + // setup PHY to interrupt on changes + + unsigned mask = + (PHY_INT_ENABLE //master interrupt enable + | PHY_INT_LINK_STATUS_CHANGE + | PHY_INT_RX_STATUS_CHANGE + ); + + eth_mac_miim_read(PHY_INT_STATUS); + eth_mac_miim_write(PHY_INT_MASK, mask); // enable the ones we want + + pic_register_handler(IRQ_PHY, eth_phy_irq_handler); + + // Advertise our flow control configuation. + // + // We and the link partner each specify two bits in the base page + // related to autoconfiguration: NWAY_AR_PAUSE and NWAY_AR_ASM_DIR. + // The bits say what a device is "willing" to do, not what may actually + // happen as a result of the negotiation. There are 4 cases: + // + // PAUSE ASM_DIR + // + // 0 0 I have no flow control capability. + // + // 1 0 I both assert and respond to flow control. + // + // 0 1 I assert flow control, but cannot respond. That is, + // I want to be able to send PAUSE frames, but will ignore any + // you send to me. (This is our configuration.) + // + // 1 1 I can both assert and respond to flow control AND I am willing + // to operate symmetrically OR asymmetrically in EITHER direction. + // (We hope the link partner advertises this, otherwise we don't + // get what we want.) + + int t = eth_mac_miim_read(PHY_AUTONEG_ADV); + t &= ~(NWAY_AR_PAUSE | NWAY_AR_ASM_DIR); + t |= NWAY_AR_ASM_DIR; + + // Say we can't to 10BASE-T or 100BASE-TX, half or full duplex + t &= ~(NWAY_AR_10T_HD_CAPS | NWAY_AR_10T_FD_CAPS | NWAY_AR_100TX_HD_CAPS | NWAY_AR_100TX_FD_CAPS); + + eth_mac_miim_write(PHY_AUTONEG_ADV, t); + int r = eth_mac_miim_read(PHY_AUTONEG_ADV); // DEBUG, read back + if (t != r){ + printf("PHY_AUTONEG_ADV: wrote 0x%x, got 0x%x\n", t, r); + } + + // Restart autonegotation. + // We want to ensure that we're advertising our PAUSE capabilities. + t = eth_mac_miim_read(PHY_CTRL); + eth_mac_miim_write(PHY_CTRL, t | MII_CR_RESTART_AUTO_NEG); +} + +static bool +unprogrammed(const void *t, size_t len) +{ + int i; + uint8_t *p = (uint8_t *)t; + bool all_zeros = true; + bool all_ones = true; + for (i = 0; i < len; i++){ + all_zeros &= p[i] == 0x00; + all_ones &= p[i] == 0xff; + } + return all_ones | all_zeros; +} + +//////////////////// MAC Addr Stuff /////////////////////// + +static int8_t src_mac_addr_initialized = false; +static eth_mac_addr_t src_mac_addr = {{ + 0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff + }}; + +const eth_mac_addr_t * +ethernet_mac_addr(void) +{ + if (!src_mac_addr_initialized){ // fetch from eeprom + src_mac_addr_initialized = true; + + // if we're simulating, don't read the EEPROM model, it's REALLY slow + if (hwconfig_simulation_p()) + return &src_mac_addr; + + eth_mac_addr_t tmp; + bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, &tmp, sizeof(tmp)); + if (!ok || unprogrammed(&tmp, sizeof(tmp))){ + // use the default + } + else + src_mac_addr = tmp; + } + + return &src_mac_addr; +} + +bool +ethernet_set_mac_addr(const eth_mac_addr_t *t) +{ + bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_MAC_ADDR, t, sizeof(eth_mac_addr_t)); + if (ok){ + src_mac_addr = *t; + src_mac_addr_initialized = true; + //eth_mac_set_addr(t); //this breaks the link + } + + return ok; +} + +//////////////////// IP Addr Stuff /////////////////////// + +static int8_t src_ip_addr_initialized = false; +static struct ip_addr src_ip_addr = { + (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0) +}; + + +const struct ip_addr *get_ip_addr(void) +{ + if (!src_ip_addr_initialized){ // fetch from eeprom + src_ip_addr_initialized = true; + + // if we're simulating, don't read the EEPROM model, it's REALLY slow + if (hwconfig_simulation_p()) + return &src_ip_addr; + + struct ip_addr tmp; + bool ok = eeprom_read(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, &tmp, sizeof(tmp)); + if (!ok || unprogrammed(&tmp, sizeof(tmp))){ + // use the default + } + else + src_ip_addr = tmp; + } + + return &src_ip_addr; +} + +bool set_ip_addr(const struct ip_addr *t){ + bool ok = eeprom_write(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, t, sizeof(struct ip_addr)); + if (ok){ + src_ip_addr = *t; + src_ip_addr_initialized = true; + } + + return ok; +} + +int +ethernet_check_errors(void) +{ + // these registers are reset when read + + int r = 0; + /* + if (eth_mac_read_rmon(0x05) != 0) + r |= RME_RX_CRC; + if (eth_mac_read_rmon(0x06) != 0) + r |= RME_RX_FIFO_FULL; + if (eth_mac_read_rmon(0x07) != 0) + r |= RME_RX_2SHORT_2LONG; + + if (eth_mac_read_rmon(0x25) != 0) + r |= RME_TX_JAM_DROP; + if (eth_mac_read_rmon(0x26) != 0) + r |= RME_TX_FIFO_UNDER; + if (eth_mac_read_rmon(0x27) != 0) + r |= RME_TX_FIFO_OVER; + */ + return r; +} -- cgit v1.2.3 From 329415df45212206c44c9ae44851f972dce3ed53 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 28 Jul 2010 16:48:45 -0700 Subject: Stripped out all the clock functionality except for init'ing the FPGA. Clock smarts have been host-side for a while, so this is redundant code. --- firmware/microblaze/lib/Makefile.inc | 1 + firmware/microblaze/lib/clocks.c | 261 +++++++++++++++++++++++++++++++++ firmware/microblaze/lib/clocks.h | 8 +- firmware/microblaze/usrp2/Makefile.am | 1 - firmware/microblaze/usrp2/clocks.c | 239 ------------------------------ firmware/microblaze/usrp2p/Makefile.am | 1 - firmware/microblaze/usrp2p/clocks.c | 259 -------------------------------- 7 files changed, 266 insertions(+), 504 deletions(-) create mode 100644 firmware/microblaze/lib/clocks.c delete mode 100644 firmware/microblaze/usrp2/clocks.c delete mode 100644 firmware/microblaze/usrp2p/clocks.c (limited to 'firmware/microblaze/usrp2') diff --git a/firmware/microblaze/lib/Makefile.inc b/firmware/microblaze/lib/Makefile.inc index 02a267d35..ae123f531 100644 --- a/firmware/microblaze/lib/Makefile.inc +++ b/firmware/microblaze/lib/Makefile.inc @@ -23,6 +23,7 @@ COMMON_SRCS = \ $(top_srcdir)/lib/ad9510.c \ $(top_srcdir)/lib/bsm12.c \ $(top_srcdir)/lib/buffer_pool.c \ + $(top_srcdir)/lib/clocks.c \ $(top_srcdir)/lib/dbsm.c \ $(top_srcdir)/lib/eeprom.c \ $(top_srcdir)/lib/eth_mac.c \ diff --git a/firmware/microblaze/lib/clocks.c b/firmware/microblaze/lib/clocks.c new file mode 100644 index 000000000..08f15deb1 --- /dev/null +++ b/firmware/microblaze/lib/clocks.c @@ -0,0 +1,261 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +#include +#endif +#include + +#include "memory_map.h" +#include "ad9510.h" +#include "spi.h" +#include "u2_init.h" +#include "nonstdio.h" + +//USRP2PLUS clocks: +//Clock 0: testclk +//Clock 1: FPGA clk +//Clock 2: ADC clk +//Clock 3: DAC clk +//Clock 4: SER clk +//Clock 5: TX dboard clk +//Clock 6: EXP clk +//Clock 7: RX dboard clk + +//TODO: should have enough brains to init the FPGA clock for USRP2+. all others are suspect. +//note that without EEPROM support u2_hw_rev_major is going to be incorrect. + +void +clocks_init(void) +{ + // Set up basic clocking functions in AD9510 + ad9510_write_reg(0x45, 0x01); // CLK2 drives distribution + + clocks_enable_fpga_clk(true, 1); + + spi_wait(); + + // Set up PLL for 10 MHz reference + // Reg 4, A counter, Don't Care +// ad9510_write_reg(0x05, 0x00); // Reg 5, B counter MSBs, 0 +// ad9510_write_reg(0x06, 0x05); // Reg 6, B counter LSBs, 5 + // Reg 7, Loss of reference detect, doesn't work yet, 0 +// ad9510_write_reg(0x5A, 0x01); // Update Regs + + // Primary clock configuration +// clocks_mimo_config(MC_WE_DONT_LOCK); + + // Set up other clocks + //clocks_enable_test_clk(false, 0); + //clocks_enable_tx_dboard(false, 0); + //clocks_enable_rx_dboard(false, 0); +// clocks_enable_eth_phyclk(false, 0); //PHY clk is separate now (u2r4, u2p) + + // Enable clock to ADCs and DACs + //clocks_enable_dac_clk(true, 1); + //clocks_enable_adc_clk(true, 1); +} + +/* +void +clocks_mimo_config(int flags) +{ + if (flags & _MC_WE_LOCK){ + // Reg 8, Charge pump on, dig lock det, positive PFD, 47 + ad9510_write_reg(0x08, 0x47); + } + else { + // Reg 8, Charge pump off, dig lock det, positive PFD + ad9510_write_reg(0x08, 0x00); + } + + // Reg 9, Charge pump current, 0x40=3mA, 0x00=650uA + ad9510_write_reg(0x09, 0x00); + // Reg A, Prescaler of 2, everything normal 04 + ad9510_write_reg(0x0A, 0x04); + // Reg B, R Div MSBs, 0 + ad9510_write_reg(0x0B, 0x00); + // Reg C, R Div LSBs, 1 + ad9510_write_reg(0x0C, 0x01); + // Reg D, Antibacklash, Digital lock det, 0 + + ad9510_write_reg(0x5A, 0x01); // Update Regs + + spi_wait(); + + // Allow for clock switchover + + if (flags & _MC_WE_LOCK){ // WE LOCK + if (flags & _MC_MIMO_CLK_INPUT) { + // Turn on ref output and choose the MIMO connector + output_regs->clk_ctrl = 0x15; + } + else { + // turn on ref output and choose the SMA + output_regs->clk_ctrl = 0x1C; + } + } + else { // WE DONT LOCK + // Disable both ext clk inputs + output_regs->clk_ctrl = 0x10; + } + + // Do we drive a clock onto the MIMO connector? + if (flags & MC_PROVIDE_CLK_TO_MIMO) + clocks_enable_clkexp_out(true,10); + else + clocks_enable_clkexp_out(false,0); +} +*/ +/* +bool +clocks_lock_detect() +{ + if(pic_regs->pending & PIC_CLKSTATUS) + return true; + return false; +} +*/ +int inline +clocks_gen_div(int divisor) +{ + int L,H; + L = (divisor>>1)-1; + H = divisor-L-2; + return (L<<4)|H; +} + +#define CLOCK_OUT_EN 0x08 +#define CLOCK_OUT_DIS_CMOS 0x01 +#define CLOCK_OUT_DIS_PECL 0x02 +#define CLOCK_DIV_DIS 0x80 +#define CLOCK_DIV_EN 0x00 + +#define CLOCK_MODE_PECL 1 +#define CLOCK_MODE_LVDS 2 +#define CLOCK_MODE_CMOS 3 + +//CHANGED: set to PECL for default behavior +void +clocks_enable_XXX_clk(bool enable, int divisor, int reg_en, int reg_div, int mode) +{ + int enable_word, div_word, div_en_word; + + switch(mode) { + case CLOCK_MODE_LVDS : + enable_word = enable ? 0x02 : 0x03; + break; + case CLOCK_MODE_CMOS : + enable_word = enable ? 0x08 : 0x09; + break; + case CLOCK_MODE_PECL : + default: + enable_word = enable ? 0x08 : 0x0A; + break; + } + if(enable && (divisor>1)) { + div_word = clocks_gen_div(divisor); + div_en_word = CLOCK_DIV_EN; + } + else { + div_word = 0; + div_en_word = CLOCK_DIV_DIS; + } + + ad9510_write_reg(reg_en,enable_word); // Output en/dis + ad9510_write_reg(reg_div,div_word); // Set divisor + ad9510_write_reg(reg_div+1,div_en_word); // Enable or Bypass Divider + ad9510_write_reg(0x5A, 0x01); // Update Regs +} + +// Clock 0 +/*void +clocks_enable_test_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x3C,0x48,CLOCK_MODE_PECL); +}*/ + +// Clock 1 +void +clocks_enable_fpga_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x3D,0x4A,CLOCK_MODE_PECL); +} +/* +// Clock 2 on Rev 3, Clock 5 on Rev 4, Clock 6 on USRP2+ +void +clocks_enable_clkexp_out(bool enable, int divisor) +{ + if(u2_hw_rev_major == 3) + clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); + else if(u2_hw_rev_major == 4) { + ad9510_write_reg(0x34,0x00); // Turn on fine delay + ad9510_write_reg(0x35,0x00); // Set Full Scale to nearly 10ns + ad9510_write_reg(0x36,0x1c); // Set fine delay. 0x20 is midscale + clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); + } + else if(u2_hw_rev_major == 10) { + ad9510_write_reg(0x34, 0x00); + ad9510_write_reg(0x35, 0x00); + ad9510_write_reg(0x36, 0x1C); + clocks_enable_XXX_clk(enable, divisor, 0x42, 0x52, CLOCK_MODE_LVDS); + } + else + putstr("ERR (clocks_enable_clkexp_out): Invalid hw rev, don't know what to do!\n"); +} +*/ +/* +// Clock 5 on Rev 3, none (was 2) on Rev 4, none on USRP2+ +void +clocks_enable_eth_phyclk(bool enable, int divisor) +{ + if(u2_hw_rev_major == 3) + clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); + else if(u2_hw_rev_major == 4) + clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); + else + putstr("(clocks_enable_eth_phyclk): no eth PHY clock or invalid hw rev\n"); //not really an error +} +*/ +// Clock 3 +/*void +clocks_enable_dac_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x3F,0x4E,CLOCK_MODE_PECL); +}*/ + +// Clock 4 +/*void +clocks_enable_adc_clk(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x40,0x50,CLOCK_MODE_LVDS); +}*/ + +// Clock 6 +/*void +clocks_enable_tx_dboard(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x42,0x54,CLOCK_MODE_CMOS); +}*/ + +// Clock 7 +/*void +clocks_enable_rx_dboard(bool enable, int divisor) +{ + clocks_enable_XXX_clk(enable,divisor,0x43,0x56,CLOCK_MODE_CMOS); +}*/ diff --git a/firmware/microblaze/lib/clocks.h b/firmware/microblaze/lib/clocks.h index a285e03dd..2769fd434 100644 --- a/firmware/microblaze/lib/clocks.h +++ b/firmware/microblaze/lib/clocks.h @@ -43,12 +43,12 @@ void clocks_init(void); * Configure our master clock source, and whether or not we drive a * clock onto the mimo connector. See MC_flags in usrp2_mimo_config.h. */ -void clocks_mimo_config(int flags); +//void clocks_mimo_config(int flags); /*! * \brief Lock Detect -- Return True if our PLL is locked */ -bool clocks_lock_detect(); +//bool clocks_lock_detect(); /*! * \brief Enable or disable test clock (extra clock signal) @@ -63,12 +63,12 @@ void clocks_enable_fpga_clk(bool enable, int divisor); /*! * \brief Enable or disable clock output sent to MIMO connector */ -void clocks_enable_clkexp_out(bool enable, int divisor); +//void clocks_enable_clkexp_out(bool enable, int divisor); /*! * \brief Enable or disable ethernet phyclk, should always be disabled */ -void clocks_enable_eth_phyclk(bool enable, int divisor); +//void clocks_enable_eth_phyclk(bool enable, int divisor); /*! * \brief Enable or disable clock to DAC diff --git a/firmware/microblaze/usrp2/Makefile.am b/firmware/microblaze/usrp2/Makefile.am index b45d2481b..8da013980 100644 --- a/firmware/microblaze/usrp2/Makefile.am +++ b/firmware/microblaze/usrp2/Makefile.am @@ -33,7 +33,6 @@ noinst_LIBRARIES = libusrp2.a libusrp2_a_SOURCES = \ $(COMMON_SRCS) \ - clocks.c \ sd.c \ ethernet.c diff --git a/firmware/microblaze/usrp2/clocks.c b/firmware/microblaze/usrp2/clocks.c deleted file mode 100644 index ccc4a7cc7..000000000 --- a/firmware/microblaze/usrp2/clocks.c +++ /dev/null @@ -1,239 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2008 Free Software Foundation, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include - -#include "memory_map.h" -#include "ad9510.h" -#include "spi.h" -#include "u2_init.h" -#include "nonstdio.h" - -void -clocks_init(void) -{ - // Set up basic clocking functions in AD9510 - ad9510_write_reg(0x45, 0x00); // CLK2 drives distribution - - clocks_enable_fpga_clk(true, 1); - - spi_wait(); - - // Set up PLL for 10 MHz reference - // Reg 4, A counter, Don't Care - ad9510_write_reg(0x05, 0x00); // Reg 5, B counter MSBs, 0 - ad9510_write_reg(0x06, 0x05); // Reg 6, B counter LSBs, 5 - // Reg 7, Loss of reference detect, doesn't work yet, 0 - ad9510_write_reg(0x5A, 0x01); // Update Regs - - // Primary clock configuration - clocks_mimo_config(MC_WE_DONT_LOCK); - - // Set up other clocks - //clocks_enable_test_clk(false, 0); - //clocks_enable_tx_dboard(false, 0); - //clocks_enable_rx_dboard(false, 0); - clocks_enable_eth_phyclk(false, 0); - - // Enable clock to ADCs and DACs - //clocks_enable_dac_clk(true, 1); - //clocks_enable_adc_clk(true, 1); -} - - -void -clocks_mimo_config(int flags) -{ - if (flags & _MC_WE_LOCK){ - // Reg 8, Charge pump on, dig lock det, positive PFD, 47 - ad9510_write_reg(0x08, 0x47); - } - else { - // Reg 8, Charge pump off, dig lock det, positive PFD - ad9510_write_reg(0x08, 0x00); - } - - // Reg 9, Charge pump current, 0x40=3mA, 0x00=650uA - ad9510_write_reg(0x09, 0x00); - // Reg A, Prescaler of 2, everything normal 04 - ad9510_write_reg(0x0A, 0x04); - // Reg B, R Div MSBs, 0 - ad9510_write_reg(0x0B, 0x00); - // Reg C, R Div LSBs, 1 - ad9510_write_reg(0x0C, 0x01); - // Reg D, Antibacklash, Digital lock det, 0 - - ad9510_write_reg(0x5A, 0x01); // Update Regs - - spi_wait(); - - // Allow for clock switchover - - if (flags & _MC_WE_LOCK){ // WE LOCK - if (flags & _MC_MIMO_CLK_INPUT) { - // Turn on ref output and choose the MIMO connector - output_regs->clk_ctrl = 0x15; - } - else { - // turn on ref output and choose the SMA - output_regs->clk_ctrl = 0x1C; - } - } - else { // WE DONT LOCK - // Disable both ext clk inputs - output_regs->clk_ctrl = 0x10; - } - - // Do we drive a clock onto the MIMO connector? - if (flags & MC_PROVIDE_CLK_TO_MIMO) - clocks_enable_clkexp_out(true,10); - else - clocks_enable_clkexp_out(false,0); -} - -bool -clocks_lock_detect() -{ - if(pic_regs->pending & PIC_CLKSTATUS) - return true; - return false; -} - -int inline -clocks_gen_div(int divisor) -{ - int L,H; - L = (divisor>>1)-1; - H = divisor-L-2; - return (L<<4)|H; -} - -#define CLOCK_OUT_EN 0x08 -#define CLOCK_OUT_DIS_CMOS 0x01 -#define CLOCK_OUT_DIS_PECL 0x02 -#define CLOCK_DIV_DIS 0x80 -#define CLOCK_DIV_EN 0x00 - -#define CLOCK_MODE_PECL 1 -#define CLOCK_MODE_LVDS 2 -#define CLOCK_MODE_CMOS 3 - -void -clocks_enable_XXX_clk(bool enable, int divisor, int reg_en, int reg_div, int mode) -{ - int enable_word, div_word, div_en_word; - - switch(mode) { - case CLOCK_MODE_PECL : - enable_word = enable ? 0x08 : 0x0A; - break; - case CLOCK_MODE_LVDS : - enable_word = enable ? 0x02 : 0x03; - break; - case CLOCK_MODE_CMOS : - enable_word = enable ? 0x08 : 0x09; - break; - } - if(enable && (divisor>1)) { - div_word = clocks_gen_div(divisor); - div_en_word = CLOCK_DIV_EN; - } - else { - div_word = 0; - div_en_word = CLOCK_DIV_DIS; - } - - ad9510_write_reg(reg_en,enable_word); // Output en/dis - ad9510_write_reg(reg_div,div_word); // Set divisor - ad9510_write_reg(reg_div+1,div_en_word); // Enable or Bypass Divider - ad9510_write_reg(0x5A, 0x01); // Update Regs -} - -// Clock 0 -/*void -clocks_enable_test_clk(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x3C,0x48,CLOCK_MODE_PECL); -}*/ - -// Clock 1 -void -clocks_enable_fpga_clk(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x3D,0x4A,CLOCK_MODE_PECL); -} - -// Clock 2 on Rev 3, Clock 5 on Rev 4 -void -clocks_enable_clkexp_out(bool enable, int divisor) -{ - if(u2_hw_rev_major == 3) - clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); - else if(u2_hw_rev_major == 4) { - ad9510_write_reg(0x34,0x00); // Turn on fine delay - ad9510_write_reg(0x35,0x00); // Set Full Scale to nearly 10ns - ad9510_write_reg(0x36,0x1c); // Set fine delay. 0x20 is midscale - clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); - - } - else - putstr("ERR: Invalid Rev\n"); -} - -// Clock 5 on Rev 3, none (was 2) on Rev 4 -void -clocks_enable_eth_phyclk(bool enable, int divisor) -{ - if(u2_hw_rev_major == 3) - clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); - else if(u2_hw_rev_major == 4) - clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); - else - putstr("ERR: Invalid Rev\n"); -} - -// Clock 3 -/*void -clocks_enable_dac_clk(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x3F,0x4E,CLOCK_MODE_PECL); -}*/ - -// Clock 4 -/*void -clocks_enable_adc_clk(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x40,0x50,CLOCK_MODE_LVDS); -}*/ - -// Clock 6 -/*void -clocks_enable_tx_dboard(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x42,0x54,CLOCK_MODE_CMOS); -}*/ - -// Clock 7 -/*void -clocks_enable_rx_dboard(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x43,0x56,CLOCK_MODE_CMOS); -}*/ diff --git a/firmware/microblaze/usrp2p/Makefile.am b/firmware/microblaze/usrp2p/Makefile.am index 86affc1f3..ddc7006ca 100644 --- a/firmware/microblaze/usrp2p/Makefile.am +++ b/firmware/microblaze/usrp2p/Makefile.am @@ -45,7 +45,6 @@ noinst_LIBRARIES = libusrp2p.a libusrp2p_a_SOURCES = \ $(COMMON_SRCS) \ - clocks.c \ spif.c \ spi_flash.c \ spi_flash_read.c \ diff --git a/firmware/microblaze/usrp2p/clocks.c b/firmware/microblaze/usrp2p/clocks.c deleted file mode 100644 index 3488ec468..000000000 --- a/firmware/microblaze/usrp2p/clocks.c +++ /dev/null @@ -1,259 +0,0 @@ -/* -*- c++ -*- */ -/* - * Copyright 2008 Free Software Foundation, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include - -#include "memory_map.h" -#include "ad9510.h" -#include "spi.h" -#include "u2_init.h" -#include "nonstdio.h" - -//USRP2PLUS clocks: -//Clock 0: testclk -//Clock 1: FPGA clk -//Clock 2: ADC clk -//Clock 3: DAC clk -//Clock 4: SER clk -//Clock 5: TX dboard clk -//Clock 6: EXP clk -//Clock 7: RX dboard clk - -//TODO: should have enough brains to init the FPGA clock for USRP2+. all others are suspect. -//note that without EEPROM support u2_hw_rev_major is going to be incorrect. - -void -clocks_init(void) -{ - // Set up basic clocking functions in AD9510 - ad9510_write_reg(0x45, 0x01); // CLK2 drives distribution - - clocks_enable_fpga_clk(true, 1); - - spi_wait(); - - // Set up PLL for 10 MHz reference - // Reg 4, A counter, Don't Care - ad9510_write_reg(0x05, 0x00); // Reg 5, B counter MSBs, 0 - ad9510_write_reg(0x06, 0x05); // Reg 6, B counter LSBs, 5 - // Reg 7, Loss of reference detect, doesn't work yet, 0 - ad9510_write_reg(0x5A, 0x01); // Update Regs - - // Primary clock configuration - clocks_mimo_config(MC_WE_DONT_LOCK); - - // Set up other clocks - //clocks_enable_test_clk(false, 0); - //clocks_enable_tx_dboard(false, 0); - //clocks_enable_rx_dboard(false, 0); - clocks_enable_eth_phyclk(false, 0); //PHY clk is separate now (u2r4, u2p) - - // Enable clock to ADCs and DACs - //clocks_enable_dac_clk(true, 1); - //clocks_enable_adc_clk(true, 1); -} - - -void -clocks_mimo_config(int flags) -{ - if (flags & _MC_WE_LOCK){ - // Reg 8, Charge pump on, dig lock det, positive PFD, 47 - ad9510_write_reg(0x08, 0x47); - } - else { - // Reg 8, Charge pump off, dig lock det, positive PFD - ad9510_write_reg(0x08, 0x00); - } - - // Reg 9, Charge pump current, 0x40=3mA, 0x00=650uA - ad9510_write_reg(0x09, 0x00); - // Reg A, Prescaler of 2, everything normal 04 - ad9510_write_reg(0x0A, 0x04); - // Reg B, R Div MSBs, 0 - ad9510_write_reg(0x0B, 0x00); - // Reg C, R Div LSBs, 1 - ad9510_write_reg(0x0C, 0x01); - // Reg D, Antibacklash, Digital lock det, 0 - - ad9510_write_reg(0x5A, 0x01); // Update Regs - - spi_wait(); - - // Allow for clock switchover - - if (flags & _MC_WE_LOCK){ // WE LOCK - if (flags & _MC_MIMO_CLK_INPUT) { - // Turn on ref output and choose the MIMO connector - output_regs->clk_ctrl = 0x15; - } - else { - // turn on ref output and choose the SMA - output_regs->clk_ctrl = 0x1C; - } - } - else { // WE DONT LOCK - // Disable both ext clk inputs - output_regs->clk_ctrl = 0x10; - } - - // Do we drive a clock onto the MIMO connector? - if (flags & MC_PROVIDE_CLK_TO_MIMO) - clocks_enable_clkexp_out(true,10); - else - clocks_enable_clkexp_out(false,0); -} - -bool -clocks_lock_detect() -{ - if(pic_regs->pending & PIC_CLKSTATUS) - return true; - return false; -} - -int inline -clocks_gen_div(int divisor) -{ - int L,H; - L = (divisor>>1)-1; - H = divisor-L-2; - return (L<<4)|H; -} - -#define CLOCK_OUT_EN 0x08 -#define CLOCK_OUT_DIS_CMOS 0x01 -#define CLOCK_OUT_DIS_PECL 0x02 -#define CLOCK_DIV_DIS 0x80 -#define CLOCK_DIV_EN 0x00 - -#define CLOCK_MODE_PECL 1 -#define CLOCK_MODE_LVDS 2 -#define CLOCK_MODE_CMOS 3 - -//CHANGED: set to PECL for default behavior -void -clocks_enable_XXX_clk(bool enable, int divisor, int reg_en, int reg_div, int mode) -{ - int enable_word, div_word, div_en_word; - - switch(mode) { - case CLOCK_MODE_LVDS : - enable_word = enable ? 0x02 : 0x03; - break; - case CLOCK_MODE_CMOS : - enable_word = enable ? 0x08 : 0x09; - break; - case CLOCK_MODE_PECL : - default: - enable_word = enable ? 0x08 : 0x0A; - break; - } - if(enable && (divisor>1)) { - div_word = clocks_gen_div(divisor); - div_en_word = CLOCK_DIV_EN; - } - else { - div_word = 0; - div_en_word = CLOCK_DIV_DIS; - } - - ad9510_write_reg(reg_en,enable_word); // Output en/dis - ad9510_write_reg(reg_div,div_word); // Set divisor - ad9510_write_reg(reg_div+1,div_en_word); // Enable or Bypass Divider - ad9510_write_reg(0x5A, 0x01); // Update Regs -} - -// Clock 0 -/*void -clocks_enable_test_clk(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x3C,0x48,CLOCK_MODE_PECL); -}*/ - -// Clock 1 -void -clocks_enable_fpga_clk(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x3D,0x4A,CLOCK_MODE_PECL); -} - -// Clock 2 on Rev 3, Clock 5 on Rev 4, Clock 6 on USRP2+ -void -clocks_enable_clkexp_out(bool enable, int divisor) -{ - if(u2_hw_rev_major == 3) - clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); - else if(u2_hw_rev_major == 4) { - ad9510_write_reg(0x34,0x00); // Turn on fine delay - ad9510_write_reg(0x35,0x00); // Set Full Scale to nearly 10ns - ad9510_write_reg(0x36,0x1c); // Set fine delay. 0x20 is midscale - clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); - } - else if(u2_hw_rev_major == 10) { - ad9510_write_reg(0x34, 0x00); - ad9510_write_reg(0x35, 0x00); - ad9510_write_reg(0x36, 0x1C); - clocks_enable_XXX_clk(enable, divisor, 0x42, 0x52, CLOCK_MODE_LVDS); - } - else - putstr("ERR (clocks_enable_clkexp_out): Invalid hw rev, don't know what to do!\n"); -} - -// Clock 5 on Rev 3, none (was 2) on Rev 4, none on USRP2+ -void -clocks_enable_eth_phyclk(bool enable, int divisor) -{ - if(u2_hw_rev_major == 3) - clocks_enable_XXX_clk(enable,divisor,0x41,0x52,CLOCK_MODE_LVDS); - else if(u2_hw_rev_major == 4) - clocks_enable_XXX_clk(enable,divisor,0x3E,0x4C,CLOCK_MODE_PECL); - else - putstr("(clocks_enable_eth_phyclk): no eth PHY clock or invalid hw rev\n"); //not really an error -} - -// Clock 3 -/*void -clocks_enable_dac_clk(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x3F,0x4E,CLOCK_MODE_PECL); -}*/ - -// Clock 4 -/*void -clocks_enable_adc_clk(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x40,0x50,CLOCK_MODE_LVDS); -}*/ - -// Clock 6 -/*void -clocks_enable_tx_dboard(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x42,0x54,CLOCK_MODE_CMOS); -}*/ - -// Clock 7 -/*void -clocks_enable_rx_dboard(bool enable, int divisor) -{ - clocks_enable_XXX_clk(enable,divisor,0x43,0x56,CLOCK_MODE_CMOS); -}*/ -- cgit v1.2.3 From f39c4538a3ca25c79f6b793ee0b6448051dcd751 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Thu, 29 Jul 2010 10:12:45 -0700 Subject: Clock bugs, LED order. Figured out cold-start problem with Matt's help -- the DCM wasn't being reset. This also explains why USRP2 didn't like it when clocks_mimo_config was omitted -- it was sneakily resetting the DCM while enabling/disabling ref outputs. Also re-did USRP2P LED order and genericized the LED settings so LED_D is LED_D for both USRP2 and USRP2P. --- firmware/microblaze/lib/clocks.c | 23 ++++++++++++++--------- firmware/microblaze/lib/clocks.h | 4 ++-- firmware/microblaze/lib/u2_init.c | 3 ++- firmware/microblaze/usrp2/memory_map.h | 4 ++++ firmware/microblaze/usrp2p/ethernet.c | 4 ++-- firmware/microblaze/usrp2p/memory_map.h | 16 ++++++++++------ 6 files changed, 34 insertions(+), 20 deletions(-) (limited to 'firmware/microblaze/usrp2') diff --git a/firmware/microblaze/lib/clocks.c b/firmware/microblaze/lib/clocks.c index b9bd2e107..2b352a385 100644 --- a/firmware/microblaze/lib/clocks.c +++ b/firmware/microblaze/lib/clocks.c @@ -25,7 +25,6 @@ #include "ad9510.h" #include "spi.h" #include "u2_init.h" -#include "nonstdio.h" //USRP2PLUS clocks: //Clock 0: testclk @@ -46,6 +45,7 @@ clocks_init(void) // Set up basic clocking functions in AD9510 ad9510_write_reg(0x45, 0x01); // CLK2 drives distribution + //enable the 100MHz clock output to the FPGA for 50MHz CPU clock clocks_enable_fpga_clk(true, 1); spi_wait(); @@ -58,7 +58,14 @@ clocks_init(void) // ad9510_write_reg(0x5A, 0x01); // Update Regs // Primary clock configuration - clocks_mimo_config(MC_WE_DONT_LOCK); +// clocks_mimo_config(MC_WE_DONT_LOCK); + + + //wait for the clock to stabilize + while(!clocks_lock_detect()); + + //issue a reset to the DCM so it locks up to the new freq + output_regs->clk_ctrl |= CLK_RESET; // Set up other clocks //clocks_enable_test_clk(false, 0); @@ -71,7 +78,7 @@ clocks_init(void) //clocks_enable_adc_clk(true, 1); } - +/* void clocks_mimo_config(int flags) { @@ -99,7 +106,7 @@ clocks_mimo_config(int flags) spi_wait(); // Allow for clock switchover - + // The below masks include 0x10, which issues a reset to the DCM. if (flags & _MC_WE_LOCK){ // WE LOCK if (flags & _MC_MIMO_CLK_INPUT) { // Turn on ref output and choose the MIMO connector @@ -121,16 +128,14 @@ clocks_mimo_config(int flags) // else // clocks_enable_clkexp_out(false,0); } +*/ -/* bool clocks_lock_detect() { - if(pic_regs->pending & PIC_CLKSTATUS) - return true; - return false; + return (pic_regs->pending & PIC_CLKSTATUS); } -*/ + int inline clocks_gen_div(int divisor) { diff --git a/firmware/microblaze/lib/clocks.h b/firmware/microblaze/lib/clocks.h index 399127595..28d1d542f 100644 --- a/firmware/microblaze/lib/clocks.h +++ b/firmware/microblaze/lib/clocks.h @@ -43,12 +43,12 @@ void clocks_init(void); * Configure our master clock source, and whether or not we drive a * clock onto the mimo connector. See MC_flags in usrp2_mimo_config.h. */ -void clocks_mimo_config(int flags); +//void clocks_mimo_config(int flags); /*! * \brief Lock Detect -- Return True if our PLL is locked */ -//bool clocks_lock_detect(); +bool clocks_lock_detect(); /*! * \brief Enable or disable test clock (extra clock signal) diff --git a/firmware/microblaze/lib/u2_init.c b/firmware/microblaze/lib/u2_init.c index ff558d673..75bc40859 100644 --- a/firmware/microblaze/lib/u2_init.c +++ b/firmware/microblaze/lib/u2_init.c @@ -26,6 +26,7 @@ #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; @@ -66,7 +67,7 @@ u2_init(void) mdelay(100); hal_set_leds(0x1f, 0x1f); mdelay(100); - hal_set_leds(0x1, 0x1f); // Leave the first one on + hal_set_leds(LED_D, 0x1f); // Leave one on #if 0 // test register readback diff --git a/firmware/microblaze/usrp2/memory_map.h b/firmware/microblaze/usrp2/memory_map.h index 4c9ead615..41a2820bc 100644 --- a/firmware/microblaze/usrp2/memory_map.h +++ b/firmware/microblaze/usrp2/memory_map.h @@ -375,6 +375,10 @@ typedef struct { volatile uint32_t led_src; // HW or SW control for LEDs } output_regs_t; +#define CLK_RESET (1<<4) +#define CLK_ENABLE (1<<3) | (1<<2) +#define CLK_SEL (1<<1) | (1<<0) + #define SERDES_ENABLE 8 #define SERDES_PRBSEN 4 #define SERDES_LOOPEN 2 diff --git a/firmware/microblaze/usrp2p/ethernet.c b/firmware/microblaze/usrp2p/ethernet.c index 80c303153..660f28934 100644 --- a/firmware/microblaze/usrp2p/ethernet.c +++ b/firmware/microblaze/usrp2p/ethernet.c @@ -70,7 +70,7 @@ ed_link_up(int speed) ed_set_mac_speed(speed); //turn on link LED for USRP2P - hal_set_leds((1 << 0), (1 << 0)); + hal_set_leds(LED_RJ45, LED_RJ45); if (ed_callback) // fire link changed callback @@ -83,7 +83,7 @@ ed_link_down(void) // putstr("ed_link_down\n"); //turn off link LED for USRP2P - hal_set_leds(0, (1 << 0)); + hal_set_leds(0, LED_RJ45); if (ed_callback) // fire link changed callback (*ed_callback)(0); diff --git a/firmware/microblaze/usrp2p/memory_map.h b/firmware/microblaze/usrp2p/memory_map.h index fc0094e67..9c5b576d7 100644 --- a/firmware/microblaze/usrp2p/memory_map.h +++ b/firmware/microblaze/usrp2p/memory_map.h @@ -402,6 +402,10 @@ typedef struct { volatile uint32_t led_src; // HW or SW control for LEDs } output_regs_t; +#define CLK_RESET (1<<4) +#define CLK_ENABLE (1<<3) | (1<<2) +#define CLK_SEL (1<<1) | (1<<0) + #define SERDES_ENABLE 8 #define SERDES_PRBSEN 4 #define SERDES_LOOPEN 2 @@ -412,13 +416,13 @@ typedef struct { // crazy order that matches the labels on the case -#define LED_A (1 << 4) -#define LED_B (1 << 1) -#define LED_C (1 << 3) -#define LED_D (1 << 0) -#define LED_E (1 << 2) +#define LED_A (1 << 2) +#define LED_B (1 << 5) +#define LED_E (1 << 3) +#define LED_D (1 << 1) +#define LED_C (1 << 4) // LED_F // controlled by CPLD -#define LED_RJ45 (1 << 5) +#define LED_RJ45 (1 << 0) #define output_regs ((output_regs_t *) MISC_OUTPUT_BASE) -- cgit v1.2.3 From 9d10efa5f0f81e1a971e92b28ba7f38e0384fdab Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Tue, 17 Aug 2010 18:35:11 -0700 Subject: UDP firmware update support for USRP2P. The hooks are in there for USRP2, but without CPLD changes it won't support it. Added an app host/utils/usrp2p_fw_update.py to write to USRP2P over the wire. Lots of TODOs in that file. Caveat -- fw_common.h, bootloader_utils.h, and the .py app MUST ALL AGREE! --- firmware/microblaze/apps/txrx_uhd.c | 2 + firmware/microblaze/lib/loader_parser.c | 324 ---------------------------- firmware/microblaze/lib/loader_parser.h | 38 ---- firmware/microblaze/lib/udp_burner_packet.c | 38 ---- firmware/microblaze/lib/udp_burner_packet.h | 28 --- firmware/microblaze/usrp2/Makefile.am | 3 +- firmware/microblaze/usrp2/udp_fw_update.c | 34 +++ firmware/microblaze/usrp2/udp_fw_update.h | 22 ++ firmware/microblaze/usrp2p/Makefile.am | 4 +- firmware/microblaze/usrp2p/spi_flash.h | 7 + firmware/microblaze/usrp2p/spi_flash_read.c | 19 ++ firmware/microblaze/usrp2p/udp_fw_update.c | 116 ++++++++++ firmware/microblaze/usrp2p/udp_fw_update.h | 22 ++ host/lib/usrp/usrp2/fw_common.h | 50 ++++- host/utils/usrp2p_fw_update.py | 259 ++++++++++++++++++++++ 15 files changed, 535 insertions(+), 431 deletions(-) delete mode 100644 firmware/microblaze/lib/loader_parser.c delete mode 100644 firmware/microblaze/lib/loader_parser.h delete mode 100644 firmware/microblaze/lib/udp_burner_packet.c delete mode 100644 firmware/microblaze/lib/udp_burner_packet.h create mode 100644 firmware/microblaze/usrp2/udp_fw_update.c create mode 100644 firmware/microblaze/usrp2/udp_fw_update.h create mode 100644 firmware/microblaze/usrp2p/udp_fw_update.c create mode 100644 firmware/microblaze/usrp2p/udp_fw_update.h create mode 100755 host/utils/usrp2p_fw_update.py (limited to 'firmware/microblaze/usrp2') diff --git a/firmware/microblaze/apps/txrx_uhd.c b/firmware/microblaze/apps/txrx_uhd.c index 9cf4c163f..e637cb38c 100644 --- a/firmware/microblaze/apps/txrx_uhd.c +++ b/firmware/microblaze/apps/txrx_uhd.c @@ -45,6 +45,7 @@ #include #include #include +#include "udp_fw_update.h" /* * Full duplex Tx and Rx between ethernet and DSP pipelines @@ -474,6 +475,7 @@ main(void) register_udp_listener(USRP2_UDP_CTRL_PORT, handle_udp_ctrl_packet); register_udp_listener(USRP2_UDP_DATA_PORT, handle_udp_data_packet); + register_udp_listener(USRP2_UDP_UPDATE_PORT, handle_udp_fw_update_packet); // initialize double buffering state machine for ethernet -> DSP Tx diff --git a/firmware/microblaze/lib/loader_parser.c b/firmware/microblaze/lib/loader_parser.c deleted file mode 100644 index 96457a164..000000000 --- a/firmware/microblaze/lib/loader_parser.c +++ /dev/null @@ -1,324 +0,0 @@ -/* -*- 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 . - */ - -#include "loader_parser.h" -#include -#include -#include -#include -#include -//#include -#include -#include -#include -#include "ethernet.h" -#include "qr_settings.h" - -#define min(a,b) ((a) < (b) ? (a) : (b)) - -static spi_flash_async_state_t async_state; - - -static caldiv_eeprom_setter_t _caldiv_set_rev = NULL; -static caldiv_eeprom_setter_t _caldiv_set_ser = NULL; -static caldiv_eeprom_setter_t _caldiv_set_mod = NULL; - -void -register_caldiv_eeprom_setters(caldiv_eeprom_setter_t set_rev, - caldiv_eeprom_setter_t set_ser, - caldiv_eeprom_setter_t set_mod) -{ - _caldiv_set_rev = set_rev; - _caldiv_set_ser = set_ser; - _caldiv_set_mod = set_mod; -} - - -// big-endian -static uint32_t -get32(const unsigned char *s) -{ - return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; -} - -// big-endian -static unsigned char * -put32(unsigned char *s, uint32_t v) -{ - s[0] = (v >> 24) & 0xff; - s[1] = (v >> 16) & 0xff; - s[2] = (v >> 8) & 0xff; - s[3] = v & 0xff; - return s + 4; -} - -static bool -erased_p(uint32_t flash_addr, size_t nbytes) -{ - unsigned char buf[64]; - - size_t n; - for (size_t i = 0; i < nbytes; i += n, flash_addr += n){ - n = min(nbytes - i, sizeof(buf)); - spi_flash_read(flash_addr, n, buf); - for (size_t j = 0; j < n; j++) - if (buf[j] != 0xff) - return false; - } - return true; -} - -static bool -erase_flash(uint32_t addr, uint32_t len) -{ - if (addr % spi_flash_sector_size() != 0) - return false; - - if (len % spi_flash_sector_size() != 0) - return false; - - spi_flash_async_erase_start(&async_state, addr, len); - // FIXME? check to see if erase was successful - return true; -} - -static bool -map_slot(uint32_t slot, uint32_t *slot_start, uint32_t *slot_len, uint32_t *status) -{ - // This case doesn't require a valid flashdir, and in fact can be used as - // part of writing the intial flashdir. - if (QLD_SLOT_DOM(slot) == QLD_DOM_UNMAPPED){ - int flash_size = get_flash_size(); - if (flash_size == 0){ - *status = QLDS_FAILED; // Can't find the flash. most likely a h/w problem. - return false; - } - *slot_start = 0; - *slot_len = flash_size; - return true; - } - - const struct flashdir *fd = get_flashdir(); - if (fd == 0) - return false; - - uint32_t slot_num = QLD_SLOT_NUM(slot); - - switch(QLD_SLOT_DOM(slot)){ - case QLD_DOM_FPGA: - if (slot_num >= fd->fpga_nslots){ - *status = QLDS_INVALID_ARG; - return false; - } - *slot_start = fd->slot[slot_num + fd->fpga_slot0].start << spi_flash_log2_sector_size(); - *slot_len = fd->slot[slot_num + fd->fpga_slot0].len << spi_flash_log2_sector_size(); - return true; - - case QLD_DOM_FW: - if (slot_num >= fd->fw_nslots){ - *status = QLDS_INVALID_ARG; - return false; - } - *slot_start = fd->slot[slot_num + fd->fw_slot0].start << spi_flash_log2_sector_size(); - *slot_len = fd->slot[slot_num + fd->fw_slot0].len << spi_flash_log2_sector_size(); - return true; - - default: - *status = QLDS_INVALID_ARG; - return false; - } -} - - -static bool -check_flashdir(void) -{ - return get_flashdir() != 0; -} - -void -loader_parser(const unsigned char *input, size_t ilen, - unsigned char *output, size_t max_olen, size_t *actual_olen) -{ - //assert (max_olen >= 8); - if (!(max_olen >= 8)) - abort(); - - *actual_olen = 0; - uint32_t status = QLDS_BAD_PKT; - - uint32_t cmd = get32(input); - uint32_t nonce = get32(input+4); - uint32_t slot = 0; - uint32_t addr = 0; - uint32_t len = 0; - - if (ilen < 8){ - nonce = -1; - goto done; - } - - uint32_t slot_start; // offset in flash - uint32_t slot_len; // length in bytes - - if (ilen >= 5 * sizeof(uint32_t)){ - slot = get32(input+8); - addr = get32(input+12); - len = get32(input+16); - } - - switch (cmd){ - case QLD_FLASH_ERASE_START: - // - if (ilen != 5 * sizeof(uint32_t)) - goto done; - - if (!check_flashdir()){ - status = QLDS_BAD_FLASHDIR; - goto done; - } - if (!map_slot(slot, &slot_start, &slot_len, &status)) - goto done; - - if (QLD_SLOT_DOM(slot) != QLD_DOM_UNMAPPED){ - addr = slot_start; - len = slot_len; - } - //printf("flash_erase: addr = 0x%x, len=0x%x\n", addr, len); - - if (0 && erased_p(addr, len)){ // already erased? - async_state.first = async_state.last = async_state.current = 0; - goto ok; - } - - if (erase_flash(addr, len)) - goto ok; - - status = QLDS_FAILED; - goto done; - - - case QLD_FLASH_ERASE_POLL: - // - if (ilen != 2 * sizeof(uint32_t)) - goto done; - - if (spi_flash_async_erase_poll(&async_state)) - goto ok; - - status = QLDS_BUSY; - goto done; - - - case QLD_FLASH_WRITE: - // - if (ilen < 5 * sizeof(uint32_t)) - goto done; - - if (ilen != 5 * sizeof(uint32_t) + len) - goto done; - - if (!check_flashdir()){ - status = QLDS_BAD_FLASHDIR; - goto done; - } - if (!map_slot(slot, &slot_start, &slot_len, &status)) - goto done; - - addr += slot_start; - len = min(len, slot_len); - - if (spi_flash_program(addr, len, &input[5*sizeof(uint32_t)])) - goto ok; - - status = QLDS_FAILED; - goto done; - - - case QLD_FLASH_READ: - case QLD_MEM_READ: - case QLD_MEM_WRITE: - case QLD_GOTO: - status = QLDS_NOTIMPLEMENTED; - goto done; - - case QLD_PING: - // - if (ilen != 2 * sizeof(uint32_t)) - goto done; - goto ok; - -#if 0 - case QLD_EEPROM_SET_XXX: - // - { - uint32_t arg = get32(input+2*sizeof(uint32_t)); - uint32_t idlen = get32(input+3*sizeof(uint32_t)); - uint8_t *idstr = (uint8_t*)input+4*sizeof(uint32_t); - uint8_t *data_p = idstr+idlen; - - //handle the ethernet cases - if (strncmp((char*)idstr, "ip", idlen) == 0){ - struct ip_addr addr = {get32(data_p)}; - ethernet_set_ip_addr(arg, addr); - } - else if (strncmp((char*)idstr, "mac", idlen) == 0){ - eth_mac_addr_t addr; - memcpy(&addr, data_p, sizeof(addr)); - ethernet_set_mac_addr(arg, &addr); - } - //handle the main board eeprom - else if (strncmp((char*)idstr, "qrrev", idlen) == 0){ - qr_set_revision(get32(data_p)); - } - else if (strncmp((char*)idstr, "qrser", idlen) == 0){ - qr_set_serial(get32(data_p)); - } - else if (strncmp((char*)idstr, "qrmod", idlen) == 0){ - qr_set_model(get32(data_p)); - } - //handle the caldiv eeprom - else if (strncmp((char*)idstr, "cdrev", idlen) == 0){ - if (_caldiv_set_rev) _caldiv_set_rev(get32(data_p)); - } - else if (strncmp((char*)idstr, "cdser", idlen) == 0){ - if (_caldiv_set_ser) _caldiv_set_ser(get32(data_p)); - } - else if (strncmp((char*)idstr, "cdmod", idlen) == 0){ - if (_caldiv_set_ser) _caldiv_set_mod(get32(data_p)); - } - else { - goto done; - } - } - goto ok; -#endif - - default: - status = QLDS_UNKNOWN_CMD; - goto done; - } - - ok: - status = QLDS_OK; - - done: - put32(output, nonce); - put32(output+4, status); - *actual_olen = 2*sizeof(uint32_t); -} diff --git a/firmware/microblaze/lib/loader_parser.h b/firmware/microblaze/lib/loader_parser.h deleted file mode 100644 index 365317bd7..000000000 --- a/firmware/microblaze/lib/loader_parser.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 . - */ - -#include -#include - -/* - * max_olen must be at least 8 bytes. 1KB is recommended. - */ -void -loader_parser(const unsigned char *input, size_t ilen, - unsigned char *output, size_t max_olen, size_t *actual_olen); - -/* - * Major kludge-master altert! - * This function registers functions for setting caldiv eeprom stuff. - * This way, the parser does not depend on the qpn apps at compile time. - */ -typedef void(*caldiv_eeprom_setter_t)(uint32_t); -void register_caldiv_eeprom_setters( - caldiv_eeprom_setter_t set_rev, - caldiv_eeprom_setter_t set_ser, - caldiv_eeprom_setter_t set_mod); diff --git a/firmware/microblaze/lib/udp_burner_packet.c b/firmware/microblaze/lib/udp_burner_packet.c deleted file mode 100644 index d86a4cf4a..000000000 --- a/firmware/microblaze/lib/udp_burner_packet.c +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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 . - */ - -#ifdef HAVE_CONFIG_H -#include -#endif -#include "udp_burner_packet.h" -#include "net_common.h" -#include "loader_parser.h" -#include -#include -#include - - -void -handle_udp_burner_packet(struct socket_address src, struct socket_address dst, - unsigned char *payload, int payload_len) -{ - unsigned char reply[128] _AL4; - size_t actual_reply_len; - loader_parser(payload, payload_len, reply, sizeof(reply), &actual_reply_len); - send_udp_pkt(dst.port, src, reply, actual_reply_len); -} diff --git a/firmware/microblaze/lib/udp_burner_packet.h b/firmware/microblaze/lib/udp_burner_packet.h deleted file mode 100644 index 0f4025712..000000000 --- a/firmware/microblaze/lib/udp_burner_packet.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -*- 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 . - */ -#ifndef INCLUDED_UDP_BURNER_PACKET_H -#define INCLUDED_UDP_BURNER_PACKET_H - -#include - -void -handle_udp_burner_packet(struct socket_address src, struct socket_address dst, - unsigned char *payload, int payload_len); - - -#endif /* INCLUDED_UDP_BURNER_PACKET_H */ diff --git a/firmware/microblaze/usrp2/Makefile.am b/firmware/microblaze/usrp2/Makefile.am index 8da013980..17f7a4744 100644 --- a/firmware/microblaze/usrp2/Makefile.am +++ b/firmware/microblaze/usrp2/Makefile.am @@ -34,7 +34,8 @@ noinst_LIBRARIES = libusrp2.a libusrp2_a_SOURCES = \ $(COMMON_SRCS) \ sd.c \ - ethernet.c + ethernet.c \ + udp_fw_update.c noinst_PROGRAMS = \ usrp2_txrx_uhd.elf diff --git a/firmware/microblaze/usrp2/udp_fw_update.c b/firmware/microblaze/usrp2/udp_fw_update.c new file mode 100644 index 000000000..bd62aed85 --- /dev/null +++ b/firmware/microblaze/usrp2/udp_fw_update.c @@ -0,0 +1,34 @@ +/* -*- 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 . + */ + +//Routines to handle updating the SPI Flash firmware via UDP + +#include "net_common.h" +#include "usrp2/fw_common.h" +#include "udp_fw_update.h" +#include + +//Firmware update packet handler +void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, + unsigned char *payload, int payload_len) { + + udp_fw_update_data_t update_data_out; + update_data_out.id = USRP2_FW_UPDATE_ID_WAT; + + send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out)); +} diff --git a/firmware/microblaze/usrp2/udp_fw_update.h b/firmware/microblaze/usrp2/udp_fw_update.h new file mode 100644 index 000000000..dcf44bc42 --- /dev/null +++ b/firmware/microblaze/usrp2/udp_fw_update.h @@ -0,0 +1,22 @@ +/* -*- 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 . + */ + +#include "net_common.h" + +void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, + unsigned char *payload, int payload_len); diff --git a/firmware/microblaze/usrp2p/Makefile.am b/firmware/microblaze/usrp2p/Makefile.am index ddc7006ca..c9ed3b99b 100644 --- a/firmware/microblaze/usrp2p/Makefile.am +++ b/firmware/microblaze/usrp2p/Makefile.am @@ -32,6 +32,7 @@ COMMON_IHX_ARGS = \ --change-section-address .vectors.hw_exception+0x8000 \ --change-section-address .vectors.interrupt+0x8000 \ --change-section-address .vectors.reset+0x8000 + # $(MB_OBJCOPY) -O ihex $< $@ # the below would work if objcopy weren't written by apes # $(MB_OBJCOPY) -O ihex -w --change-section-address .vectors*+0x8000 $< $@ @@ -49,7 +50,8 @@ libusrp2p_a_SOURCES = \ spi_flash.c \ spi_flash_read.c \ bootloader_utils.c \ - ethernet.c + ethernet.c \ + udp_fw_update.c noinst_PROGRAMS = \ usrp2p_txrx_uhd.elf \ diff --git a/firmware/microblaze/usrp2p/spi_flash.h b/firmware/microblaze/usrp2p/spi_flash.h index f65f28477..bbe7b650d 100644 --- a/firmware/microblaze/usrp2p/spi_flash.h +++ b/firmware/microblaze/usrp2p/spi_flash.h @@ -31,6 +31,7 @@ uint32_t spi_flash_rdid(void); /* Read ID */ uint32_t spi_flash_rdsr(void); /* Read Status Register */ size_t spi_flash_log2_sector_size(void) __attribute__((pure)); /* either 16 or 18 */ +size_t spi_flash_log2_memory_size(void); static inline size_t spi_flash_sector_size(void) @@ -38,6 +39,12 @@ spi_flash_sector_size(void) return ((size_t) 1) << spi_flash_log2_sector_size(); } +static inline size_t +spi_flash_memory_size(void) +{ + return ((size_t) 1) << spi_flash_log2_memory_size(); +} + void spi_flash_read(uint32_t flash_addr, size_t nbytes, void *buf); /* diff --git a/firmware/microblaze/usrp2p/spi_flash_read.c b/firmware/microblaze/usrp2p/spi_flash_read.c index 1c65350f7..4682c5fe6 100644 --- a/firmware/microblaze/usrp2p/spi_flash_read.c +++ b/firmware/microblaze/usrp2p/spi_flash_read.c @@ -20,6 +20,8 @@ #include "spi_flash_private.h" #include // abort +static size_t _spi_flash_log2_memory_size; + uint32_t spi_flash_rdid(void) { @@ -48,9 +50,26 @@ spi_flash_log2_sector_size(void) }; _spi_flash_log2_sector_size = log2_sector_size[size - 22]; + _spi_flash_log2_memory_size = size; //while we're at it return _spi_flash_log2_sector_size; } +size_t +spi_flash_log2_memory_size(void) +{ + if (_spi_flash_log2_memory_size != 0) + return _spi_flash_log2_memory_size; + + uint32_t id = spi_flash_rdid(); + int type = (id >> 8) & 0xff; + int size = id & 0xff; + if (type != 0x20 || size < 22 || size > 24) + abort(); + + _spi_flash_log2_memory_size = size; + return _spi_flash_log2_memory_size; +} + void spi_flash_read(uint32_t flash_addr, size_t nbytes, void *buf) { diff --git a/firmware/microblaze/usrp2p/udp_fw_update.c b/firmware/microblaze/usrp2p/udp_fw_update.c new file mode 100644 index 000000000..6b860007a --- /dev/null +++ b/firmware/microblaze/usrp2p/udp_fw_update.c @@ -0,0 +1,116 @@ +/* -*- 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 . + */ + +//Routines to handle updating the SPI Flash firmware via UDP + +#include +#include "usrp2/fw_common.h" +#include "spi.h" +#include "spi_flash.h" +#include "udp_fw_update.h" +#include +#include +#include "ethernet.h" + +//Firmware update packet handler +void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, + unsigned char *payload, int payload_len) { +//okay, we can do this ANY NUMBER of ways. we have up to ~1500 bytes to play with -- we don't handle +//fragmentation, so limit it to id; + + //ensure that the protocol versions match + if (payload_len >= sizeof(uint32_t) && update_data_in->proto_ver != USRP2_FW_COMPAT_NUM){ + printf("!Error in update packet handler: Expected compatibility number %d, but got %d\n", + USRP2_FW_COMPAT_NUM, update_data_in->proto_ver + ); + update_data_in_id = USRP2_FW_UPDATE_ID_OHAI_LOL; //so we can respond + } + + //ensure that this is not a short packet + if (payload_len < sizeof(udp_fw_update_data_t)){ + printf("!Error in update packet handler: Expected payload length %d, but got %d\n", + (int)sizeof(udp_fw_update_data_t), payload_len + ); + update_data_in_id = USRP2_FW_UPDATE_ID_WAT; + } + + spi_flash_async_state_t spi_flash_async_state; + + switch(update_data_in_id) { + case USRP2_FW_UPDATE_ID_OHAI_LOL: //why hello there you handsome devil + update_data_out.id = USRP2_FW_UPDATE_ID_OHAI_OMG; + memcpy(&update_data_out.data.ip_addr, (void *)get_ip_addr(), sizeof(struct ip_addr)); + break; + + case USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL: //query sector size, memory size so the host can mind the boundaries + update_data_out.data.flash_info_args.sector_size_bytes = spi_flash_sector_size(); + update_data_out.data.flash_info_args.memory_size_bytes = spi_flash_memory_size(); + update_data_out.id = USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG; + break; + + case USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL: //out with the old + spi_flash_async_erase_start(&spi_flash_async_state, update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length); + update_data_out.id = USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG; + break; + + case USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL: + //poll for done, set something in the reply packet + //spi_flash_async_erase_poll() also advances the state machine, so you should call it reasonably often to get things done quicker + if(spi_flash_async_erase_poll(&spi_flash_async_state)) update_data_out.id = USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG; + else update_data_out.id = USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG; + break; + + case USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL: //and in with the new + //spi_flash_program() goes pretty quick compared to page erases, so we don't bother polling -- it'll come back in some milliseconds + //if it doesn't come back fast enough, we'll just write smaller packets at a time until it does + spi_flash_program(update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length, update_data_in->data.flash_args.data); + update_data_out.id = USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG; + break; + + case USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL: //for verify + spi_flash_read(update_data_in->data.flash_args.flash_addr, update_data_in->data.flash_args.length, update_data_out.data.flash_args.data); + update_data_out.id = USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG; + break; + + case USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL: //for if we ever get the ICAP working + //should reset via icap_reload_fpga(uint32_t flash_address); + update_data_out.id = USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG; + //you should note that if you get a reply packet to this the reset has obviously failed + break; + +// case USRP2_FW_UPDATE_ID_KTHXBAI: //see ya +// break; + + default: //uhhhh + update_data_out.id = USRP2_FW_UPDATE_ID_WAT; + } + send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out)); +} diff --git a/firmware/microblaze/usrp2p/udp_fw_update.h b/firmware/microblaze/usrp2p/udp_fw_update.h new file mode 100644 index 000000000..dcf44bc42 --- /dev/null +++ b/firmware/microblaze/usrp2p/udp_fw_update.h @@ -0,0 +1,22 @@ +/* -*- 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 . + */ + +#include "net_common.h" + +void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, + unsigned char *payload, int payload_len); diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index fc1e6f2a7..320ed7d77 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -34,7 +34,7 @@ extern "C" { //fpga and firmware compatibility numbers #define USRP2_FPGA_COMPAT_NUM 1 -#define USRP2_FW_COMPAT_NUM 5 +#define USRP2_FW_COMPAT_NUM 6 //used to differentiate control packets over data port #define USRP2_INVALID_VRT_HEADER 0 @@ -43,6 +43,7 @@ extern "C" { // Dynamic and/or private ports: 49152-65535 #define USRP2_UDP_CTRL_PORT 49152 #define USRP2_UDP_DATA_PORT 49153 +#define USRP2_UDP_UPDATE_PORT 49154 //for firmware upgrade commands //////////////////////////////////////////////////////////////////////// // I2C addresses @@ -93,6 +94,35 @@ typedef enum{ } usrp2_ctrl_id_t; +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 enum{ USRP2_DIR_RX = 'r', USRP2_DIR_TX = 't' @@ -137,6 +167,24 @@ typedef struct{ } data; } usrp2_ctrl_data_t; +typedef struct { + __stdint(uint32_t) proto_ver; + __stdint(uint32_t) id; + __stdint(uint32_t) seq; + union { + __stdint(uint32_t) ip_addr; + struct { + __stdint(uint32_t) flash_addr; + __stdint(uint32_t) length; + __stdint(uint8_t) data[256]; + } flash_args; + struct { + __stdint(uint32_t) sector_size_bytes; + __stdint(uint32_t) memory_size_bytes; + } flash_info_args; + } data; +} udp_fw_update_data_t; + #undef __stdint #ifdef __cplusplus } diff --git a/host/utils/usrp2p_fw_update.py b/host/utils/usrp2p_fw_update.py new file mode 100755 index 000000000..dce9c82ef --- /dev/null +++ b/host/utils/usrp2p_fw_update.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python +# +# 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 . +# + +# TODO: make it work +# TODO: make it autodetect UHD devices +# TODO: you should probably watch sequence numbers + +import optparse +import math +import os +import re +import struct +import socket +import sys + +######################################################################## +# constants +######################################################################## +UDP_FW_UPDATE_PORT = 49154 +UDP_MAX_XFER_BYTES = 1024 +UDP_TIMEOUT = 3 +UDP_POLL_INTERVAL = 0.10 #in seconds + +USRP2_FW_PROTO_VERSION = 5 + +#from bootloader_utils.h +PROD_FPGA_IMAGE_LOCATION_ADDR = 0x00200000 +PROD_FW_IMAGE_LOCATION_ADDR = 0x00400000 +SAFE_FW_IMAGE_LOCATION_ADDR = 0x007F0000 +SAFE_FPGA_IMAGE_LOCATION_ADDR = 0x00000000 +FPGA_IMAGE_SIZE_BYTES = 2097152 +FW_IMAGE_SIZE_BYTES = 31744 + +FLASH_DATA_PACKET_SIZE = 256 + +#see fw_common.h +FLASH_ARGS_FMT = '!LLLLL256s' +FLASH_INFO_FMT = '!LLLLL256x' +FLASH_IP_FMT = '!LLLL260x' + +class update_id_t: + USRP2_FW_UPDATE_ID_WAT = ord(' ') + USRP2_FW_UPDATE_ID_OHAI_LOL = ord('a') + USRP2_FW_UPDATE_ID_OHAI_OMG = ord('A') + USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL = ord('f') + USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG = ord('F') + USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL = ord('e') + USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG = ord('E') + USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL = ord('d') + USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG = ord('D') + USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG = ord('B') + USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL = ord('w') + USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG = ord('W') + USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL = ord('r') + USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG = ord('R') + USRP2_FW_UPDATE_ID_RESET_MAH_COMPUTORZ_LOL = ord('s') + USRP2_FW_UPDATE_ID_RESETTIN_TEH_COMPUTORZ_OMG = ord('S') + USRP2_FW_UPDATE_ID_KTHXBAI = ord('~') + +_seq = -1 +def seq(): + global _seq + _seq = _seq+1 + return _seq + +######################################################################## +# helper functions +######################################################################## +def unpack_flash_args_fmt(s): + return struct.unpack(FLASH_ARGS_FMT, s) #(proto_ver, pktid, seq, flash_addr, length, data) + +def unpack_flash_info_fmt(s): + return struct.unpack(FLASH_INFO_FMT, s) #(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) + +def unpack_flash_ip_fmt(s): + return struct.unpack(FLASH_IP_FMT, s) #(proto_ver, pktid, seq, ip_addr) + +def pack_flash_args_fmt(proto_ver, pktid, seq, flash_addr, length, data): + return struct.pack(FLASH_ARGS_FMT, proto_ver, pktid, seq, flash_addr, length, data) + +def pack_flash_info_fmt(proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes): + return struct.pack(FLASH_INFO_FMT, proto_ver, pktid, seq, sector_size_bytes, memory_size_bytes) + +def send_and_recv(pkt, ip): + update_socket = create_socket() + + try: + update_socket.sendto(pkt, (ip, UDP_FW_UPDATE_PORT)) + except Exception, e: + print e + sys.exit(1) + + try: + (recv_pkt, recv_addr) = update_socket.recvfrom(UDP_MAX_XFER_BYTES) + except Exception, e: + print e + sys.exit(1) + + if recv_addr != (options.ip, UDP_FW_UPDATE_PORT): + raise Exception, "Packet received from invalid IP %s, expected %s" % (recv_addr, options.ip) + + return recv_pkt + +def create_socket(): + socket.setdefaulttimeout(UDP_TIMEOUT) + update_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + return update_socket + +#just here to validate comms +def init_update(ip): + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_OHAI_LOL, seq(), 0, 0, "") + in_pkt = send_and_recv(out_pkt, ip) + (proto_ver, pktid, rxseq, ip_addr) = unpack_flash_ip_fmt(in_pkt) + if pktid == update_id_t.USRP2_FW_UPDATE_ID_OHAI_OMG: + print "USRP2P found." + else: + raise Exception, "Invalid reply received from device." + +# print "Incoming:\n\tVer: %i\n\tID: %c\n\tSeq: %i\n\tIP: %i\n" % (proto_ver, chr(pktid), rxseq, ip_addr) + +def get_flash_info(ip): + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WATS_TEH_FLASH_INFO_LOL, seq(), 0, 0, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, sector_size_bytes, memory_size_bytes) = unpack_flash_info_fmt(in_pkt) + + if pktid != update_id_t.USRP2_FW_UPDATE_ID_HERES_TEH_FLASH_INFO_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + + return (memory_size_bytes, sector_size_bytes) + +def burn_fw(ip, fw, fpga): + init_update(ip) + (flash_size, sector_size) = get_flash_info(ip) + + print "Flash size: %i\nSector size: %i" % (flash_size, sector_size) + + if fpga: + fpga_file = open(fpga, 'rb') + fpga_image = fpga_file.read() + erase_image(ip, SAFE_FPGA_IMAGE_LOCATION_ADDR, FPGA_IMAGE_SIZE_BYTES) + write_image(ip, fpga_image, SAFE_FPGA_IMAGE_LOCATION_ADDR) #TODO: DO NOT WRITE SAFE IMAGE! this is only here because the bootloader isn't finished yet + verify_image(ip, fpga_image, SAFE_FPGA_IMAGE_LOCATION_ADDR) + + if fw: + fw_file = open(fw, 'rb') + fw_image = fw_file.read() + erase_image(ip, PROD_FW_IMAGE_LOCATION_ADDR, FW_IMAGE_SIZE_BYTES) + write_image(ip, fw_image, PROD_FW_IMAGE_LOCATION_ADDR) + verify_image(ip, fw_image, PROD_FW_IMAGE_LOCATION_ADDR) + +def write_image(ip, image, addr): +#we split the image into smaller (256B) bits and send them down the wire + while image: + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_WRITE_TEH_FLASHES_LOL, seq(), addr, FLASH_DATA_PACKET_SIZE, image[:FLASH_DATA_PACKET_SIZE]) + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + + if pktid != update_id_t.USRP2_FW_UPDATE_ID_WROTE_TEH_FLASHES_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + image = image[FLASH_DATA_PACKET_SIZE:] + addr += FLASH_DATA_PACKET_SIZE + +def verify_image(ip, image, addr): + readsize = len(image) + readdata = str() + while readsize > 0: + if readsize < FLASH_DATA_PACKET_SIZE: thisreadsize = readsize + else: thisreadsize = FLASH_DATA_PACKET_SIZE + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_READ_TEH_FLASHES_LOL, seq(), addr, thisreadsize, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + + if pktid != update_id_t.USRP2_FW_UPDATE_ID_KK_READ_TEH_FLASHES_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + readdata += data[:thisreadsize] + readsize -= FLASH_DATA_PACKET_SIZE + addr += FLASH_DATA_PACKET_SIZE + + print "Read back %i bytes" % len(readdata) + print readdata + +# for i in range(256, 512): +# print "out: %i in: %i" % (ord(image[i]), ord(readdata[i])) + + if readdata != image: + print "Verify failed. Image did not write correctly." + else: + print "Success." + +def erase_image(ip, addr, length): + #get flash info first + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_ERASE_TEH_FLASHES_LOL, seq(), addr, length, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + + if pktid != update_id_t.USRP2_FW_UPDATE_ID_ERASING_TEH_FLASHES_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + print "Erasing %i bytes at %i" % (length, addr) + + #now wait for it to finish + while(1): + out_pkt = pack_flash_args_fmt(USRP2_FW_PROTO_VERSION, update_id_t.USRP2_FW_UPDATE_ID_R_U_DONE_ERASING_LOL, seq(), 0, 0, "") + in_pkt = send_and_recv(out_pkt, ip) + + (proto_ver, pktid, rxseq, flash_addr, rxlength, data) = unpack_flash_args_fmt(in_pkt) + + if pktid == update_id_t.USRP2_FW_UPDATE_ID_IM_DONE_ERASING_OMG: break + elif pktid != update_id_t.USRP2_FW_UPDATE_ID_NOPE_NOT_DONE_ERASING_OMG: + raise Exception, "Invalid reply %c from device." % (chr(pktid)) + + print "\tFinished." + +#def verify_image(ip, image, addr): + + +######################################################################## +# command line options +######################################################################## +def get_options(): + parser = optparse.OptionParser() + parser.add_option("--ip", type="string", help="USRP2P firmware address", default='') + parser.add_option("--fw", type="string", help="firmware image path (optional)", default='') + parser.add_option("--fpga", type="string", help="fpga image path (optional)", default='') + (options, args) = parser.parse_args() + + return options + +######################################################################## +# main +######################################################################## +if __name__=='__main__': + options = get_options() + if not options.ip: raise Exception, 'no ip address specified' + + if not options.fpga and not options.fw: raise Exception, 'Must specify either a firmware image or FPGA image.' + burn_fw(ip=options.ip, fw=options.fw, fpga=options.fpga) -- cgit v1.2.3 From 42547a0be8e52758d340fd1eba51a2dd5274a652 Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Wed, 18 Aug 2010 14:38:50 -0700 Subject: Moved UDP firmware update stuff out of fw_common.h into udp_fw_update.h. This stuff should only go back into fw_common.h if we decide to integrate firmware update into the UHD code. Right now it's a separate Python script. Also moved udp_fw_update.h to lib/ because it's the same between USRP2 and USRP2P. --- firmware/microblaze/Makefile.common | 9 +++- firmware/microblaze/lib/udp_fw_update.h | 71 ++++++++++++++++++++++++++++++ firmware/microblaze/usrp2/udp_fw_update.c | 4 +- firmware/microblaze/usrp2/udp_fw_update.h | 22 --------- firmware/microblaze/usrp2p/Makefile.am | 2 +- firmware/microblaze/usrp2p/udp_fw_update.c | 20 +++------ firmware/microblaze/usrp2p/udp_fw_update.h | 22 --------- host/lib/usrp/usrp2/fw_common.h | 48 -------------------- host/utils/usrp2p_fw_update.py | 2 +- 9 files changed, 87 insertions(+), 113 deletions(-) create mode 100644 firmware/microblaze/lib/udp_fw_update.h delete mode 100644 firmware/microblaze/usrp2/udp_fw_update.h delete mode 100644 firmware/microblaze/usrp2p/udp_fw_update.h (limited to 'firmware/microblaze/usrp2') diff --git a/firmware/microblaze/Makefile.common b/firmware/microblaze/Makefile.common index ceb6a553a..4e726edab 100644 --- a/firmware/microblaze/Makefile.common +++ b/firmware/microblaze/Makefile.common @@ -59,8 +59,13 @@ COMMON_LFLAGS = \ ######################################################################## # Common stuff for building top level microblaze images ######################################################################## +#we use COMMON_IHX_ARGS to relocate the reset and interrupt vectors to +#just below the start of code. upon creating the BIN, any leading padding +#is thrown out, so the .bin file is valid for uploading to USRP2P. this +#does not affect USRP2 because the USRP2 already starts at 0x0000, and +#because the relocate_args are not defined for USRP2's Makefile.am. .elf.bin: - $(MB_OBJCOPY) -O binary $< $@ + $(MB_OBJCOPY) -O binary $(RELOCATE_ARGS) $< $@ .elf.dump: $(MB_OBJDUMP) -DSC $< > $@ @@ -69,7 +74,7 @@ COMMON_LFLAGS = \ $(HEXDUMP) -v -e'1/1 "%.2X\n"' $< > $@ .elf.ihx: - $(MB_OBJCOPY) -O ihex $(COMMON_IHX_ARGS) $< $@ + $(MB_OBJCOPY) -O ihex $(RELOCATE_ARGS) $< $@ _generated_from_elf = \ $(noinst_PROGRAMS:.elf=.map) \ 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 . + */ + +#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); diff --git a/firmware/microblaze/usrp2/udp_fw_update.c b/firmware/microblaze/usrp2/udp_fw_update.c index bd62aed85..14eb0b1ee 100644 --- a/firmware/microblaze/usrp2/udp_fw_update.c +++ b/firmware/microblaze/usrp2/udp_fw_update.c @@ -20,14 +20,14 @@ #include "net_common.h" #include "usrp2/fw_common.h" -#include "udp_fw_update.h" #include +#include "udp_fw_update.h" //Firmware update packet handler void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, unsigned char *payload, int payload_len) { - udp_fw_update_data_t update_data_out; + usrp2_fw_update_data_t update_data_out; update_data_out.id = USRP2_FW_UPDATE_ID_WAT; send_udp_pkt(USRP2_UDP_UPDATE_PORT, src, &update_data_out, sizeof(update_data_out)); diff --git a/firmware/microblaze/usrp2/udp_fw_update.h b/firmware/microblaze/usrp2/udp_fw_update.h deleted file mode 100644 index dcf44bc42..000000000 --- a/firmware/microblaze/usrp2/udp_fw_update.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- 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 . - */ - -#include "net_common.h" - -void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, - unsigned char *payload, int payload_len); diff --git a/firmware/microblaze/usrp2p/Makefile.am b/firmware/microblaze/usrp2p/Makefile.am index c9ed3b99b..e6bff2dbc 100644 --- a/firmware/microblaze/usrp2p/Makefile.am +++ b/firmware/microblaze/usrp2p/Makefile.am @@ -27,7 +27,7 @@ AM_LDFLAGS = \ -Wl,-defsym -Wl,_STACK_SIZE=3072 #all of this here is to relocate the hardware vectors to somewhere normal. -COMMON_IHX_ARGS = \ +RELOCATE_ARGS = \ --change-section-address .vectors.sw_exception+0x8000 \ --change-section-address .vectors.hw_exception+0x8000 \ --change-section-address .vectors.interrupt+0x8000 \ diff --git a/firmware/microblaze/usrp2p/udp_fw_update.c b/firmware/microblaze/usrp2p/udp_fw_update.c index 6b860007a..9242242e7 100644 --- a/firmware/microblaze/usrp2p/udp_fw_update.c +++ b/firmware/microblaze/usrp2p/udp_fw_update.c @@ -22,28 +22,18 @@ #include "usrp2/fw_common.h" #include "spi.h" #include "spi_flash.h" -#include "udp_fw_update.h" #include #include #include "ethernet.h" +#include "udp_fw_update.h" //Firmware update packet handler void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, unsigned char *payload, int payload_len) { -//okay, we can do this ANY NUMBER of ways. we have up to ~1500 bytes to play with -- we don't handle -//fragmentation, so limit it to id; //ensure that the protocol versions match @@ -55,9 +45,9 @@ void handle_udp_fw_update_packet(struct socket_address src, struct socket_addres } //ensure that this is not a short packet - if (payload_len < sizeof(udp_fw_update_data_t)){ + if (payload_len < sizeof(usrp2_fw_update_data_t)){ printf("!Error in update packet handler: Expected payload length %d, but got %d\n", - (int)sizeof(udp_fw_update_data_t), payload_len + (int)sizeof(usrp2_fw_update_data_t), payload_len ); update_data_in_id = USRP2_FW_UPDATE_ID_WAT; } diff --git a/firmware/microblaze/usrp2p/udp_fw_update.h b/firmware/microblaze/usrp2p/udp_fw_update.h deleted file mode 100644 index dcf44bc42..000000000 --- a/firmware/microblaze/usrp2p/udp_fw_update.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -*- 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 . - */ - -#include "net_common.h" - -void handle_udp_fw_update_packet(struct socket_address src, struct socket_address dst, - unsigned char *payload, int payload_len); diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 320ed7d77..4d3b62d6b 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -43,7 +43,6 @@ extern "C" { // Dynamic and/or private ports: 49152-65535 #define USRP2_UDP_CTRL_PORT 49152 #define USRP2_UDP_DATA_PORT 49153 -#define USRP2_UDP_UPDATE_PORT 49154 //for firmware upgrade commands //////////////////////////////////////////////////////////////////////// // I2C addresses @@ -94,35 +93,6 @@ typedef enum{ } usrp2_ctrl_id_t; -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 enum{ USRP2_DIR_RX = 'r', USRP2_DIR_TX = 't' @@ -167,24 +137,6 @@ typedef struct{ } data; } usrp2_ctrl_data_t; -typedef struct { - __stdint(uint32_t) proto_ver; - __stdint(uint32_t) id; - __stdint(uint32_t) seq; - union { - __stdint(uint32_t) ip_addr; - struct { - __stdint(uint32_t) flash_addr; - __stdint(uint32_t) length; - __stdint(uint8_t) data[256]; - } flash_args; - struct { - __stdint(uint32_t) sector_size_bytes; - __stdint(uint32_t) memory_size_bytes; - } flash_info_args; - } data; -} udp_fw_update_data_t; - #undef __stdint #ifdef __cplusplus } diff --git a/host/utils/usrp2p_fw_update.py b/host/utils/usrp2p_fw_update.py index b15542933..5eff83c07 100755 --- a/host/utils/usrp2p_fw_update.py +++ b/host/utils/usrp2p_fw_update.py @@ -36,7 +36,7 @@ UDP_MAX_XFER_BYTES = 1024 UDP_TIMEOUT = 3 UDP_POLL_INTERVAL = 0.10 #in seconds -USRP2_FW_PROTO_VERSION = 5 +USRP2_FW_PROTO_VERSION = 6 #from bootloader_utils.h PROD_FPGA_IMAGE_LOCATION_ADDR = 0x00200000 -- cgit v1.2.3 From 5982ec4ee2c6f9efca1c5068e42dc38b822df7dc Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Tue, 12 Oct 2010 14:46:41 -0700 Subject: USRP2P: This is surprisingly involved. Adding a consistent interface to deal with hardware revisions. --- firmware/microblaze/usrp2/Makefile.am | 3 +- firmware/microblaze/usrp2p/Makefile.am | 3 +- host/include/uhd/usrp/mboard_rev.hpp | 90 ++++++++++++++++++++++++++++++++++ host/lib/usrp/mboard_rev.cpp | 68 +++++++++++++++++++++++++ host/lib/usrp/usrp2/clock_ctrl.cpp | 36 +++++++------- host/lib/usrp/usrp2/codec_impl.cpp | 4 +- host/lib/usrp/usrp2/fw_common.h | 3 +- host/lib/usrp/usrp2/mboard_impl.cpp | 30 ++++++++---- host/lib/usrp/usrp2/usrp2_clk_regs.hpp | 15 ++++-- host/lib/usrp/usrp2/usrp2_iface.cpp | 4 +- host/lib/usrp/usrp2/usrp2_iface.hpp | 6 +-- host/lib/usrp/usrp2/usrp2_impl.hpp | 1 - host/lib/usrp/usrp2/usrp2_regs.hpp | 2 - 13 files changed, 219 insertions(+), 46 deletions(-) create mode 100644 host/include/uhd/usrp/mboard_rev.hpp create mode 100644 host/lib/usrp/mboard_rev.cpp (limited to 'firmware/microblaze/usrp2') diff --git a/firmware/microblaze/usrp2/Makefile.am b/firmware/microblaze/usrp2/Makefile.am index 17f7a4744..e3f57728a 100644 --- a/firmware/microblaze/usrp2/Makefile.am +++ b/firmware/microblaze/usrp2/Makefile.am @@ -22,9 +22,10 @@ AM_CFLAGS = \ AM_LDFLAGS = \ $(COMMON_LFLAGS) \ - libusrp2.a \ -Wl,-defsym -Wl,_TEXT_START_ADDR=0x0050 \ -Wl,-defsym -Wl,_STACK_SIZE=3072 + +LDADD = libusrp2.a ######################################################################## # USRP2 specific library and programs diff --git a/firmware/microblaze/usrp2p/Makefile.am b/firmware/microblaze/usrp2p/Makefile.am index 0d0b60437..a5df3ff08 100644 --- a/firmware/microblaze/usrp2p/Makefile.am +++ b/firmware/microblaze/usrp2p/Makefile.am @@ -22,10 +22,11 @@ AM_CFLAGS = \ AM_LDFLAGS = \ $(COMMON_LFLAGS) \ - libusrp2p.a \ -Wl,-defsym -Wl,_TEXT_START_ADDR=0x8050 \ -Wl,-defsym -Wl,_STACK_SIZE=3072 +LDADD = libusrp2p.a + #all of this here is to relocate the hardware vectors to somewhere normal. RELOCATE_ARGS = \ --change-section-address .vectors.sw_exception+0x8000 \ diff --git a/host/include/uhd/usrp/mboard_rev.hpp b/host/include/uhd/usrp/mboard_rev.hpp new file mode 100644 index 000000000..5307d80c1 --- /dev/null +++ b/host/include/uhd/usrp/mboard_rev.hpp @@ -0,0 +1,90 @@ +// +// 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 . +// + +#ifndef INCLUDED_UHD_USRP_MBOARD_REV_HPP +#define INCLUDED_UHD_USRP_MBOARD_REV_HPP + +#include +#include +#include +#include + +namespace uhd{ namespace usrp{ + + class UHD_API mboard_rev_t : boost::equality_comparable{ + public: + /*! + * Create a mboard rev from an integer. + * \param rev the integer representation + */ + mboard_rev_t(boost::uint16_t rev = 0xffff); + + /*! + * Obtain a mboard rev that represents an invalid/uninit mboard ID + * \return the mboard rev with the 0xffff rev. + */ + static mboard_rev_t none(void); + + /*! + * Create a new mboard rev from an integer representation. + * \param uint16 an unsigned 16 bit integer + * \return a new mboard rev containing the integer + */ + static mboard_rev_t from_uint16(boost::uint16_t uint16); + + /*! + * Get the mboard rev represented as an integer. + * \return an unsigned 16 bit integer representation + */ + boost::uint16_t to_uint16(void) const; + + /*! + * Create a new mboard rev from a string representation. + * If the string has a 0x prefix, it will be parsed as hex. + * \param string a numeric string, possibly hex + * \return a new dboard id containing the integer + */ + static mboard_rev_t from_string(const std::string &string); + + /*! + * Get the mboard rev represented as an integer. + * \return a hex string representation with 0x prefix + */ + std::string to_string(void) const; + + /*! + * Get the pretty print representation of this mboard rev. + * \return a string with the mboard name and rev number + */ + std::string to_pp_string(void) const; + + private: + boost::uint16_t _rev; //internal representation + }; + + /*! + * Comparator operator overloaded for mboard rev. + * The boost::equality_comparable provides the !=. + * \param lhs the dboard id to the left of the operator + * \param rhs the dboard id to the right of the operator + * \return true when the mboard revs are equal + */ + UHD_API bool operator==(const mboard_rev_t &lhs, const mboard_rev_t &rhs); + +}} //namespace + +#endif /* INCLUDED_UHD_USRP_MBOARD_REV_HPP */ diff --git a/host/lib/usrp/mboard_rev.cpp b/host/lib/usrp/mboard_rev.cpp new file mode 100644 index 000000000..8206d12af --- /dev/null +++ b/host/lib/usrp/mboard_rev.cpp @@ -0,0 +1,68 @@ +// +// 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 . +// + +#include +#include +#include +#include +#include + +using namespace uhd::usrp; + +mboard_rev_t::mboard_rev_t(boost::uint16_t id){ + _rev = rev; +} + +mboard_rev_t mboard_rev_t::none(void){ + return mboard_rev_t(); +} + +mboard_rev_t mboard_rev_t::from_uint16(boost::uint16_t uint16){ + return mboard_rev_t(uint16); +} + +boost::uint16_t mboard_rev_t::to_uint16(void) const{ + return _rev; +} + +//used with lexical cast to parse a hex string +template struct to_hex{ + T value; + operator T() const {return value;} + friend std::istream& operator>>(std::istream& in, to_hex& out){ + in >> std::hex >> out.value; + return in; + } +}; + +mboard_rev_t mboard_rev_t::from_string(const std::string &string){ + if (string.substr(0, 2) == "0x"){ + return mboard_rev_t::from_uint16(boost::lexical_cast >(string)); + } + return mboard_rev_t::from_uint16(boost::lexical_cast(string)); +} + +std::string mboard_rev_t::to_string(void) const{ + return str(boost::format("0x%04x") % this->to_uint16()); +} + +//Note: to_pp_string is implemented in the dboard manager +//because it needs access to the dboard registration table + +bool uhd::usrp::operator==(const mboard_rev_t &lhs, const mboard_rev_t &rhs){ + return lhs.to_uint16() == rhs.to_uint16(); +} diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index c3dc4917e..8887faf12 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -17,6 +17,7 @@ #include "clock_ctrl.hpp" #include "ad9510_regs.hpp" +#include #include "usrp2_regs.hpp" //spi slave constants #include "usrp2_clk_regs.hpp" #include @@ -31,9 +32,7 @@ using namespace uhd; class usrp2_clock_ctrl_impl : public usrp2_clock_ctrl{ public: usrp2_clock_ctrl_impl(usrp2_iface::sptr iface){ - _iface = iface; //_iface has get_hw_rev(), which lets us know if it's a USRP2+ (>=0x80) or USRP2 (<0x80). - - clk_regs = usrp2_clk_regs_t(_iface->get_hw_rev()); + clk_regs = usrp2_clk_regs_t(iface->get_hw_rev()); _ad9510_regs.cp_current_setting = ad9510_regs_t::CP_CURRENT_SETTING_3_0MA; this->write_reg(clk_regs.pll_3); @@ -86,16 +85,13 @@ public: } void enable_mimo_clock_out(bool enb){ - //FIXME TODO put this revision read in a common place - boost::uint8_t rev_hi = _iface->read_eeprom(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV_MSB, 1).at(0); - //calculate the low and high dividers size_t divider = size_t(this->get_master_clock_rate()/10e6); size_t high = divider/2; size_t low = divider - high; - switch(rev_hi){ - case 3: //clock 2 + switch(clk_regs.exp){ + case 2: //U2 rev 3 _ad9510_regs.power_down_lvpecl_out2 = enb? ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_NORMAL : ad9510_regs_t::POWER_DOWN_LVPECL_OUT2_SAFE_PD; @@ -104,11 +100,9 @@ public: _ad9510_regs.divider_low_cycles_out2 = low - 1; _ad9510_regs.divider_high_cycles_out2 = high - 1; _ad9510_regs.bypass_divider_out2 = 0; - this->write_reg(0x3e); - this->write_reg(0x4c); break; - case 4: //clock 5 + case 5: //U2 rev 4 _ad9510_regs.power_down_lvds_cmos_out5 = enb? 0 : 1; _ad9510_regs.lvds_cmos_select_out5 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT5_LVDS; _ad9510_regs.output_level_lvds_out5 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT5_1_75MA; @@ -116,19 +110,26 @@ public: _ad9510_regs.divider_low_cycles_out5 = low - 1; _ad9510_regs.divider_high_cycles_out5 = high - 1; _ad9510_regs.bypass_divider_out5 = 0; - this->write_reg(0x41); - this->write_reg(0x52); + break; + + case 6: //U2+ + _ad9510_regs.power_down_lvds_cmos_out6 = enb? 0 : 1; + _ad9510_regs.lvds_cmos_select_out6 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT6_LVDS; + _ad9510_regs.output_level_lvds_out6 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT6_1_75MA; + //set the registers (divider - 1) + _ad9510_regs.divider_low_cycles_out6 = low - 1; + _ad9510_regs.divider_high_cycles_out6 = high - 1; + _ad9510_regs.bypass_divider_out5 = 0; break; - //TODO FIXME do i want to throw, what about uninitialized boards? - //default: throw std::runtime_error("unknown rev hi in mboard eeprom"); - default: std::cerr << "unknown rev hi: " << rev_hi << std::endl; + default: } + this->write_reg(clk_regs.output(clk_regs.exp)); + this->write_reg(clk_regs.div_lo(clk_regs.exp)); this->update_regs(); } //uses output clock 7 (cmos) - //this clock is the same between USRP2 and USRP2+ and so this fn does not get a switch statement void enable_rx_dboard_clock(bool enb){ _ad9510_regs.power_down_lvds_cmos_out7 = enb? 0 : 1; _ad9510_regs.lvds_cmos_select_out7 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT7_CMOS; @@ -297,7 +298,6 @@ private: } usrp2_clk_regs_t clk_regs; - usrp2_iface::sptr _iface; ad9510_regs_t _ad9510_regs; }; diff --git a/host/lib/usrp/usrp2/codec_impl.cpp b/host/lib/usrp/usrp2/codec_impl.cpp index db98e50ab..04a11d922 100644 --- a/host/lib/usrp/usrp2/codec_impl.cpp +++ b/host/lib/usrp/usrp2/codec_impl.cpp @@ -67,7 +67,7 @@ void usrp2_mboard_impl::rx_codec_get(const wax::obj &key_, wax::obj &val){ return; case CODEC_PROP_GAIN_NAMES: - if(_iface->get_hw_rev() >= USRP2P_FIRST_HW_REV) { + if(_iface->get_hw_rev().to_uint16() >= USRP2P_FIRST_HW_REV) { val = prop_names_t(codec_rx_gain_ranges.keys()); } else val = prop_names_t(); return; @@ -94,7 +94,7 @@ void usrp2_mboard_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val){ switch(key.as()) { case CODEC_PROP_GAIN_I: case CODEC_PROP_GAIN_Q: - if(_iface->get_hw_rev() < USRP2P_FIRST_HW_REV) UHD_THROW_PROP_SET_ERROR();//this capability is only found in USRP2P + if(_iface->get_hw_rev().to_uint16() < USRP2P_FIRST_HW_REV) UHD_THROW_PROP_SET_ERROR();//this capability is only found in USRP2P gain = val.as(); this->rx_codec_set_gain(gain, key.name); diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 67955c831..6c9596092 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -55,8 +55,7 @@ extern "C" { //////////////////////////////////////////////////////////////////////// // EEPROM Layout //////////////////////////////////////////////////////////////////////// -#define USRP2_EE_MBOARD_REV_LSB 0x00 //1 byte -#define USRP2_EE_MBOARD_REV_MSB 0x01 //1 byte +#define USRP2_EE_MBOARD_REV 0x00 //2 bytes, little-endian (historic, don't blame me) #define USRP2_EE_MBOARD_MAC_ADDR 0x02 //6 bytes #define USRP2_EE_MBOARD_IP_ADDR 0x0C //uint32, big-endian #define USRP2_EE_MBOARD_BOOTLOADER_FLAGS 0xF7 diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index e0e0d726e..6ee6d03a1 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -49,12 +50,9 @@ usrp2_mboard_impl::usrp2_mboard_impl( _iface = usrp2_iface::make(ctrl_transport); //extract the mboard rev numbers - _rev_lo = _iface->read_eeprom(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV_LSB, 1).at(0); - _rev_hi = _iface->read_eeprom(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV_MSB, 1).at(0); - - //set the device revision (USRP2 or USRP2+) based on the above - _iface->set_hw_rev((_rev_hi << 8) | _rev_lo); - + byte_vector_t rev_bytes = _iface->read_eeprom(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV, 2); + _iface->set_hw_rev(mboard_rev_t::from_uint16(rev_bytes.at(0) | (revbytes.at(1) << 8))); + //contruct the interfaces to mboard perifs _clock_ctrl = usrp2_clock_ctrl::make(_iface); _codec_ctrl = usrp2_codec_ctrl::make(_iface); @@ -224,12 +222,17 @@ void usrp2_mboard_impl::get(const wax::obj &key_, wax::obj &val){ val = boost::asio::ip::address_v4(bytes).to_string(); return; } - } + + if (key.as() == "hw-rev"){ + //extract the mboard rev numbers + val = _iface->get_hw_rev().to_string(); + return; + } //handle the get request conditioned on the key switch(key.as()){ case MBOARD_PROP_NAME: - val = str(boost::format("usrp2 mboard%d - rev %d:%d") % _index % _rev_hi % _rev_lo); + val = str(boost::format("usrp2 mboard%d - rev %s") % _index % _iface->get_hw_rev().to_string()); return; case MBOARD_PROP_OTHERS:{ @@ -321,9 +324,18 @@ void usrp2_mboard_impl::set(const wax::obj &key, const wax::obj &val){ _iface->write_eeprom(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_IP_ADDR, bytes); return; } + + if (key.as() == "hw-rev"){ + mboard_rev_t rev = mboard_rev_t::from_string(val.as()); + byte_vector_t revbytes(2); + revbytes(1) = rev.to_uint16() >> 8; + revbytes(0) = rev.to_uint16() & 0xff; + _iface->write_eeprom(USRP2_I2C_ADDR_MBOARD, USRP2_EE_MBOARD_REV, revbytes); + _iface->set_hw_rev(rev); //so the iface knows what rev it is + return; } - //handle the get request conditioned on the key + //handle the set request conditioned on the key switch(key.as()){ case MBOARD_PROP_CLOCK_CONFIG: diff --git a/host/lib/usrp/usrp2/usrp2_clk_regs.hpp b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp index d057fb342..edbdfa15d 100644 --- a/host/lib/usrp/usrp2/usrp2_clk_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_clk_regs.hpp @@ -18,19 +18,24 @@ #ifndef INCLUDED_USRP2_CLK_REGS_HPP #define INCLUDED_USRP2_CLK_REGS_HPP +#include #include "usrp2_regs.hpp" class usrp2_clk_regs_t { public: usrp2_clk_regs_t(void) { ; } - usrp2_clk_regs_t(int hw_rev) { + usrp2_clk_regs_t(uhd::usrp::mboard_rev_t hw_rev) { test = 0; fpga = 1; - adc = (hw_rev>=USRP2P_FIRST_HW_REV) ? 2 : 4; + adc = (hw_rev.is_usrp2p()) ? 2 : 4; dac = 3; - serdes = (hw_rev>=USRP2P_FIRST_HW_REV) ? 4 : 2; //only used by usrp2+ - tx_db = (hw_rev>=USRP2P_FIRST_HW_REV) ? 5 : 6; - exp = (hw_rev>=USRP2P_FIRST_HW_REV) ? 6 : 5; + serdes = (hw_rev.is_usrp2p()) ? 4 : 2; //only used by usrp2+ + tx_db = (hw_rev.is_usrp2p()) ? 5 : 6; + + if(hw_rev.major() == 3) exp = 2; + else if(hw_rev.major() >= 4) exp = 5; + else if(hw_rev.major() > 10) exp = 6; + rx_db = 7; } diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 0771c4945..7ce5cc936 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -260,12 +260,12 @@ public: /*********************************************************************** * Get/set hardware revision **********************************************************************/ - void set_hw_rev(int rev) { + void set_hw_rev(mboard_rev_t rev) { hw_rev = rev; regs = usrp2_get_regs(rev); //might be a better place to do this } - int get_hw_rev(void) { + mboard_rev_t get_hw_rev(void) { return hw_rev; } diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp index 55fbfa7e4..fe2687d02 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.hpp +++ b/host/lib/usrp/usrp2/usrp2_iface.hpp @@ -112,12 +112,12 @@ public: * Set the hardware revision number. Also selects the proper register set for the device. * \param rev the 16-bit revision */ - virtual void set_hw_rev(int rev) = 0; + virtual void set_hw_rev(uhd::usrp::mboard_rev_t rev) = 0; /*! Return the hardware revision number * \return hardware revision */ - virtual int get_hw_rev(void) = 0; + virtual uhd::usrp::mboard_rev_t get_hw_rev(void) = 0; /*! * Register map selected from USRP2/USRP2+. @@ -126,7 +126,7 @@ public: /*! * Hardware revision as returned by the device. */ - int hw_rev; + uhd::usrp::mboard_rev_t hw_rev; }; #endif /* INCLUDED_USRP2_IFACE_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index b81711e2b..1ebd90ca4 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -95,7 +95,6 @@ public: private: size_t _index; - int _rev_hi, _rev_lo; const size_t _recv_frame_size; //properties for this mboard diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index f74c78b7b..8dae843ab 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -18,8 +18,6 @@ #ifndef INCLUDED_USRP2_REGS_HPP #define INCLUDED_USRP2_REGS_HPP -//these are used to set the - #define USRP2_MISC_OUTPUT_BASE 0xD400 #define USRP2_GPIO_BASE 0xC800 #define USRP2_ATR_BASE 0xE400 -- cgit v1.2.3