diff options
author | jcorgan | 2008-09-08 01:00:12 +0000 |
---|---|---|
committer | jcorgan | 2008-09-08 01:00:12 +0000 |
commit | e0fcbaee124d3e8c4c11bdda662f88e082352058 (patch) | |
tree | a51ef1c8b949681f45e5664478e8515065cfff5b /usrp2/firmware/lib | |
parent | c86f6c23c6883f73d953d64c28ab42cedb77e4d7 (diff) | |
download | gnuradio-e0fcbaee124d3e8c4c11bdda662f88e082352058.tar.gz gnuradio-e0fcbaee124d3e8c4c11bdda662f88e082352058.tar.bz2 gnuradio-e0fcbaee124d3e8c4c11bdda662f88e082352058.zip |
Merged r9433:9527 from features/gr-usrp2 into trunk. Adds usrp2 and gr-usrp2 top-level components. Trunk passes distcheck with mb-gcc installed, but currently not without them. The key issue is that when mb-gcc is not installed, the build system skips over the usrp2/firmware directory, and the firmware include files don't get put into the dist tarball. But we can't do the usual DIST_SUBDIRS method as the firmware is a subpackage.
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@9528 221aa14e-8319-0410-a670-987f0aec2ac5
Diffstat (limited to 'usrp2/firmware/lib')
63 files changed, 6771 insertions, 0 deletions
diff --git a/usrp2/firmware/lib/Makefile.am b/usrp2/firmware/lib/Makefile.am new file mode 100644 index 000000000..83dd915c9 --- /dev/null +++ b/usrp2/firmware/lib/Makefile.am @@ -0,0 +1,90 @@ +# +# Copyright 2007 Free Software Foundation, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +include $(top_srcdir)/Makefile.common + +noinst_LIBRARIES = \ + libu2fw.a + + +libu2fw_a_SOURCES = \ + ad9510.c \ + ad9777.c \ + buffer_pool.c \ + clocks.c \ + db_basic.c \ + db_init.c \ + db_rfx.c \ + db_tvrx.c \ + dbsm.c \ + eeprom.c \ + ethernet.c \ + eth_mac.c \ + hal_io.c \ + hal_uart.c \ + i2c.c \ + lsadc.c \ + lsdac.c \ + mdelay.c \ + memcpy_wa.c \ + memset_wa.c \ + nonstdio.c \ + pic.c \ + print_mac_addr.c \ + print_rmon_regs.c \ + print_fxpt.c \ + print_buffer.c \ + printf.c \ + spi.c \ + u2_init.c + + +noinst_HEADERS = \ + ad9510.h \ + ad9777.h \ + ad9777_regs.h \ + bool.h \ + buffer_pool.h \ + clocks.h \ + db.h \ + db_base.h \ + dbsm.h \ + eth_mac.h \ + eth_mac_regs.h \ + eth_phy.h \ + ethernet.h \ + hal_io.h \ + hal_uart.h \ + i2c.h \ + lsadc.h \ + lsdac.h \ + mdelay.h \ + memcpy_wa.h \ + memory_map.h \ + memset_wa.h \ + nonstdio.h \ + pic.h \ + print_rmon_regs.h \ + spi.h \ + stdint.h \ + stdio.h \ + u2_init.h \ + usrp2_bytesex.h \ + wb16550.h + +EXTRA_DIST = \ + microblaze.ld diff --git a/usrp2/firmware/lib/ad9510.c b/usrp2/firmware/lib/ad9510.c new file mode 100644 index 000000000..4d3acb65d --- /dev/null +++ b/usrp2/firmware/lib/ad9510.c @@ -0,0 +1,42 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ad9510.h" +#include "spi.h" +#include <memory_map.h> + +#define RD (1 << 15) +#define WR (0 << 15) + +void +ad9510_write_reg(int regno, uint8_t value) +{ + uint32_t inst = WR | (regno & 0xff); + uint32_t v = (inst << 8) | (value & 0xff); + spi_transact(SPI_TXONLY, SPI_SS_AD9510, v, 24, SPIF_PUSH_FALL); +} + +int +ad9510_read_reg(int regno) +{ + uint32_t inst = RD | (regno & 0xff); + uint32_t v = (inst << 8) | 0; + uint32_t r = spi_transact(SPI_TXRX, SPI_SS_AD9510, v, 24, + SPIF_PUSH_FALL | SPIF_LATCH_FALL); + return r & 0xff; +} diff --git a/usrp2/firmware/lib/ad9510.h b/usrp2/firmware/lib/ad9510.h new file mode 100644 index 000000000..a395e5223 --- /dev/null +++ b/usrp2/firmware/lib/ad9510.h @@ -0,0 +1,30 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_AD9510_H +#define INCLUDED_AD9510_H + +#include <stdint.h> + +/* + * Analog Device AD9510 1.2 GHz Clock Distribution IC w/ PLL + */ + +void ad9510_write_reg(int regno, uint8_t value); +int ad9510_read_reg(int regno); + +#endif /* INCLUDED_AD9510_H */ diff --git a/usrp2/firmware/lib/ad9777.c b/usrp2/firmware/lib/ad9777.c new file mode 100644 index 000000000..734ccd7e5 --- /dev/null +++ b/usrp2/firmware/lib/ad9777.c @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ad9777.h" +#include "memory_map.h" +#include "spi.h" + +#define IB_RD 0x80 +#define IB_WR 0x00 +#define IB_XFER_1 0x00 +#define IB_XFER_2 0x20 +#define IB_XFER_3 0x40 +#define IB_XFER_4 0x60 +#define IB_ADDR_MASK 0x1f + +void +ad9777_write_reg(int regno, uint8_t value) +{ + uint8_t instr = IB_WR | IB_XFER_1 | (regno & IB_ADDR_MASK); + spi_transact(SPI_TXONLY, SPI_SS_AD9777, + (instr << 8) | (value & 0xff), 16, SPIF_PUSH_FALL); +} + +int +ad9777_read_reg(int regno) +{ + uint8_t instr = IB_RD | IB_XFER_1 | (regno & IB_ADDR_MASK); + uint32_t r = spi_transact(SPI_TXRX, SPI_SS_AD9777, + (instr << 8) | 0, 16, + SPIF_PUSH_FALL | SPIF_LATCH_RISE); + return r & 0xff; +} diff --git a/usrp2/firmware/lib/ad9777.h b/usrp2/firmware/lib/ad9777.h new file mode 100644 index 000000000..d4d104910 --- /dev/null +++ b/usrp2/firmware/lib/ad9777.h @@ -0,0 +1,31 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_AD9777_H +#define INCLUDED_AD9777_H + +#include <stdint.h> +#include "ad9777_regs.h" + +/* + * Analog Devices AD9777 16-bit, 160 MS/s, Dual Interpolating TxDAC + */ + +void ad9777_write_reg(int regno, uint8_t value); +int ad9777_read_reg(int regno); + +#endif /* INCLUDED_AD9777_H */ diff --git a/usrp2/firmware/lib/ad9777_regs.h b/usrp2/firmware/lib/ad9777_regs.h new file mode 100644 index 000000000..de2936c15 --- /dev/null +++ b/usrp2/firmware/lib/ad9777_regs.h @@ -0,0 +1,71 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_AD9777_REGS_H +#define INCLUDED_AD9777_REGS_H + +#define R0_SW_RESET (1 << 5) +#define R0_SLEEP (1 << 4) +#define R0_POWER_DN (1 << 3) +#define R0_1R (1 << 2) +#define R0_2R (0 << 2) +#define R0_PLL_LOCKED (1 << 1) + +#define R1_INTERP_1X 0x00 +#define R1_INTERP_2X 0x40 +#define R1_INTERP_4X 0x80 +#define R1_INTERP_8X 0xC0 +#define R1_MOD_NONE 0x00 +#define R1_MOD_FS_2 0x10 // Fs/2 +#define R1_MOD_FS_4 0x20 // Fs/4 +#define R1_MOD_FS_8 0x30 // Fs/8 +#define R1_ZERO_STUFF (1 << 3) // N.B., doubles output rate +#define R1_REAL_MIX (1 << 2) +#define R1_CMPLX_MIX (0 << 2) +#define R1_POS_EXP (1 << 1) // exp(+jwt) +#define R1_NEG_EXP (0 << 1) // exp(-jwt) +#define R1_DATACLK_OUT (1 << 0) + +#define R2_2S_COMP (0 << 7) +#define R2_2PORT_MODE (0 << 6) +#define R2_1PORT_MODE (1 << 6) + +#define R3_PLL_DIV_1 0x00 +#define R3_PLL_DIV_2 0x01 +#define R3_PLL_DIV_4 0x02 +#define R3_PLL_DIV_8 0x03 + +#define R4_PLL_ON (1 << 7) +#define R4_CP_MANUAL (1 << 6) +#define R4_CP_AUTO (0 << 6) +#define R4_CP_50uA (0x00 | R4_CP_MANUAL) +#define R4_CP_100uA (0x01 | R4_CP_MANUAL) +#define R4_CP_200uA (0x02 | R4_CP_MANUAL) +#define R4_CP_400uA (0x03 | R4_CP_MANUAL) +#define R4_CP_800uA (0x07 | R4_CP_MANUAL) + +#define R5_I_FINE_GAIN(g) (g) // 8-bits +#define R6_I_COARSE_GAIN(g) ((g) & 0xf) // low 4-bits + +#define R9_Q_FINE_GAIN(g) (g) // 8-bits +#define R10_Q_COARSE_GAIN(g) ((g) & 0xf) // low 4-bits + + +// FIXME more registers for offset and gain control... + + +#endif /* INCLUDED_AD9777_REGS_H */ diff --git a/usrp2/firmware/lib/bool.h b/usrp2/firmware/lib/bool.h new file mode 100644 index 000000000..36f472d40 --- /dev/null +++ b/usrp2/firmware/lib/bool.h @@ -0,0 +1,26 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_BOOL_H +#define INCLUDED_BOOL_H + +typedef int bool; +#define true 1 +#define false 0 + +#endif /* INCLUDED_BOOL_H */ diff --git a/usrp2/firmware/lib/buffer_pool.c b/usrp2/firmware/lib/buffer_pool.c new file mode 100644 index 000000000..77e7c5213 --- /dev/null +++ b/usrp2/firmware/lib/buffer_pool.c @@ -0,0 +1,72 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "memory_map.h" +#include "buffer_pool.h" +#include "hal_io.h" + +void +bp_init(void) +{ + int i; + bp_disable_port(PORT_SERDES); + bp_disable_port(PORT_DSP); + bp_disable_port(PORT_ETH); + bp_disable_port(PORT_RAM); + + for (i = 0; i < NBUFFERS; i++) + bp_clear_buf(i); +} + +#ifndef INLINE_BUFFER_POOL + +void +bp_clear_buf(int bufnum) +{ + buffer_pool_ctrl->ctrl = BPC_BUFFER(bufnum) | BPC_PORT_NIL | BPC_CLR; +} + +void +bp_disable_port(int portnum) +{ + // disable buffer connections to this port + buffer_pool_ctrl->ctrl = BPC_BUFFER_NIL | BPC_PORT(portnum); +} + +void +bp_receive_to_buf(int bufnum, int port, int step, int fl, int ll) +{ + buffer_pool_ctrl->ctrl = (BPC_READ + | BPC_BUFFER(bufnum) + | BPC_PORT(port) + | BPC_STEP(step) + | BPC_FIRST_LINE(fl) + | BPC_LAST_LINE(ll)); +} + +void +bp_send_from_buf(int bufnum, int port, int step, int fl, int ll) +{ + buffer_pool_ctrl->ctrl = (BPC_WRITE + | BPC_BUFFER(bufnum) + | BPC_PORT(port) + | BPC_STEP(step) + | BPC_FIRST_LINE(fl) + | BPC_LAST_LINE(ll)); +} + +#endif diff --git a/usrp2/firmware/lib/buffer_pool.h b/usrp2/firmware/lib/buffer_pool.h new file mode 100644 index 000000000..145b20f8d --- /dev/null +++ b/usrp2/firmware/lib/buffer_pool.h @@ -0,0 +1,75 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_BUFFER_POOL_H +#define INCLUDED_BUFFER_POOL_H + +#include "memory_map.h" + +// Buffer Pool Management + + +// define to have common buffer operations inlined +#define INLINE_BUFFER_POOL 1 + +void bp_init(void); + +#ifndef INLINE_BUFFER_POOL + +void bp_clear_buf(int bufnum); +void bp_disable_port(int portnum); +void bp_receive_to_buf(int bufnum, int port, int step, int fl, int ll); +void bp_send_from_buf(int bufnum, int port, int step, int fl, int ll); + +#else + +static inline void +bp_clear_buf(int bufnum) +{ + buffer_pool_ctrl->ctrl = BPC_BUFFER(bufnum) | BPC_PORT_NIL | BPC_CLR; +} + +static inline void +bp_disable_port(int portnum) +{ + // disable buffer connections to this port + buffer_pool_ctrl->ctrl = BPC_BUFFER_NIL | BPC_PORT(portnum); +} + +static inline void +bp_receive_to_buf(int bufnum, int port, int step, int fl, int ll) +{ + buffer_pool_ctrl->ctrl = (BPC_READ + | BPC_BUFFER(bufnum) + | BPC_PORT(port) + | BPC_STEP(step) + | BPC_FIRST_LINE(fl) + | BPC_LAST_LINE(ll)); +} + +static inline void +bp_send_from_buf(int bufnum, int port, int step, int fl, int ll) +{ + buffer_pool_ctrl->ctrl = (BPC_WRITE + | BPC_BUFFER(bufnum) + | BPC_PORT(port) + | BPC_STEP(step) + | BPC_FIRST_LINE(fl) + | BPC_LAST_LINE(ll)); +} +#endif +#endif diff --git a/usrp2/firmware/lib/clocks.c b/usrp2/firmware/lib/clocks.c new file mode 100644 index 000000000..bfcaa7347 --- /dev/null +++ b/usrp2/firmware/lib/clocks.c @@ -0,0 +1,182 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <clocks.h> + +#include "memory_map.h" +#include "ad9510.h" +#include "spi.h" + +void +clocks_init(void) +{ + // Set up basic clocking functions in AD9510 + ad9510_write_reg(0x45, 0x00); // CLK2 drives distribution + ad9510_write_reg(0x3D, 0x00); // Turn on output 1 (FPGA CLK), normal levels + ad9510_write_reg(0x4B, 0x80); // Bypass divider 1 + ad9510_write_reg(0x5A, 0x01); // Update Regs + + 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 + + + // FIXME, probably need interface to this... + timesync_regs->tick_control = 4; + + // Primary clock configuration + clocks_mimo_config(MC_WE_DONT_LOCK); + + // Set up other clocks + + clocks_enable_test_clk(false); + clocks_enable_tx_dboard(false, 0); + clocks_enable_rx_dboard(false, 0); + + // ETH phy clock + ad9510_write_reg(0x41, 0x01); // Turn off output 5 (phy_clk) + ad9510_write_reg(0x53, 0x80); // Bypass divider + ad9510_write_reg(0x5A, 0x01); // Update Regs + + // Enable clock to ADCs and DACs + ad9510_write_reg(0x3F, 0x00); // Turn on output 3 (DAC CLK), normal levels + ad9510_write_reg(0x4F, 0x80); // Bypass Div #3 + + ad9510_write_reg(0x40, 0x02); // Turn on out 4 (ADC clk), LVDS + ad9510_write_reg(0x51, 0x80); // Bypass Div #4 + + ad9510_write_reg(0x5A, 0x01); // Update Regs +} + + +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) { + ad9510_write_reg(0x3E, 0x00); // Turn on output 2 (clk_exp_out), normal levels + ad9510_write_reg(0x4D, 0x00); // Turn on Div2 + ad9510_write_reg(0x4C, 0x44); // Set Div2 = 10, output a 10 MHz clock + } + else { + ad9510_write_reg(0x3E, 0x02); // Turn off output 2 (clk_exp_out) + ad9510_write_reg(0x4D, 0x80); // Bypass divider 2 + } + ad9510_write_reg(0x5A, 0x01); // Update Regs +} + +void +clocks_enable_test_clk(bool enable) +{ + if (enable){ + ad9510_write_reg(0x3C, 0x08); // Turn on output 0 -- Test output + ad9510_write_reg(0x49, 0x80); // Bypass divider 0 + } + else { + ad9510_write_reg(0x3C, 0x02); // Turn off output 0 + } + ad9510_write_reg(0x5A, 0x01); // Update Regs +} + + +void +clocks_enable_rx_dboard(bool enable, int divisor) +{ + if (enable){ + ad9510_write_reg(0x43, 0x08); // enable output 7 (db_rx_clk), CMOS + + if (divisor == 0){ + ad9510_write_reg(0x57, 0x80); // Bypass Div #7, 100 MHz clock + } + else { + // FIXME Matt, do something with divisor... + } + } + else { + ad9510_write_reg(0x43, 0x01); // Turn off output 7 (db_rx_clk) + } + ad9510_write_reg(0x5A, 0x01); // Update Regs +} + + +void +clocks_enable_tx_dboard(bool enable, int divisor) +{ + if (enable){ + ad9510_write_reg(0x42, 0x08); // enable output 6 (db_tx_clk), CMOS + if (divisor == 0) { + ad9510_write_reg(0x55, 0x80); // Bypass Div #6, 100 MHz clock + } + else { + // FIXME Matt, do something with divisor + } + } + else { + ad9510_write_reg(0x42, 0x01); // Turn off output 6 (db_tx_clk) + } + ad9510_write_reg(0x5A, 0x01); // Update Regs +} diff --git a/usrp2/firmware/lib/clocks.h b/usrp2/firmware/lib/clocks.h new file mode 100644 index 000000000..c43cfcb14 --- /dev/null +++ b/usrp2/firmware/lib/clocks.h @@ -0,0 +1,63 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_CLOCKS_H +#define INCLUDED_CLOCKS_H + +/* + * Routines to configure our multitude of clocks + */ + +#include <bool.h> +#include <usrp2_mimo_config.h> + + +/*! + * One time call to initialize all clocks to a reasonable state. We + * come out of here using our free running 100MHz oscilator and not + * providing a clock to the MIMO connector (CMC_WE_DONT_LOCK) + */ +void clocks_init(void); + + +/*! + * \brief MIMO clock configuration. + * + * 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); + +/*! + * \brief Enable or disable test clock (extra clock signal) + */ +void clocks_enable_test_clk(bool enable); + +/*! + * \brief Enable or disable clock to Rx daughterboard + */ +void clocks_enable_rx_dboard(bool enable, int divisor); + + +/*! + * \brief Enable or disable clock to Tx daughterboard + */ +void clocks_enable_tx_dboard(bool enable, int divisor); + + +#endif /* INCLUDED_CLOCKS_H */ diff --git a/usrp2/firmware/lib/db.h b/usrp2/firmware/lib/db.h new file mode 100644 index 000000000..5828fb00d --- /dev/null +++ b/usrp2/firmware/lib/db.h @@ -0,0 +1,94 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Interface to daughterboard code + */ + +#ifndef INCLUDED_DB_H +#define INCLUDED_DB_H + +#include <usrp2_types.h> + + +struct db_base; + +/* pointers to daughterboard structures */ +extern struct db_base *tx_dboard; +extern struct db_base *rx_dboard; + + +//! Intermediate tuning information + +struct tune_result +{ + //! The RF frequency that corresponds to DC in the IF from the daughterboard + u2_fxpt_freq_t baseband_freq; + + //! The DDC/DUC frequency used to down/up convert to/from the target frequency + u2_fxpt_freq_t dxc_freq; + + //! Any differerence btwn target and actual (typically < 0.01 Hz) + u2_fxpt_freq_t residual_freq; + + //! Is the complex baseband spectrum inverted + bool inverted; +}; + + +/*! + * \brief One-time init at powerup + * + * Sets rx_dboard, tx_dboard and initializes daughterboards. + */ +void +db_init(void); + +/*! + * \brief Two stage tuning. Given target_freq, tune LO and DDC/DUC + * + * \param[in] db is the daughterboard instance + * \param[in] target_freq is the freq to translate the complex baseband to/from. + * \param[out] result provides details of the resulting configuration. + * + */ +bool +db_tune(struct db_base *db, u2_fxpt_freq_t target_freq, struct tune_result *result); + + +/* + * Set only the DDC frequency + */ +bool +db_set_ddc_freq(u2_fxpt_freq_t dxc_freq, u2_fxpt_freq_t *actual_dxc_freq); + +/* + * Set only the DUC frequency + */ +bool +db_set_duc_freq(u2_fxpt_freq_t dxc_freq, u2_fxpt_freq_t *actual_dxc_freq); + + +/*! + * \brief Set gain + */ +bool +db_set_gain(struct db_base *db, u2_fxpt_gain_t gain); + + +#endif /* INCLUDED_DB_H */ diff --git a/usrp2/firmware/lib/db_base.h b/usrp2/firmware/lib/db_base.h new file mode 100644 index 000000000..9b5ce051f --- /dev/null +++ b/usrp2/firmware/lib/db_base.h @@ -0,0 +1,68 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_DB_BASE_H +#define INCLUDED_DB_BASE_H + +#include <usrp2_types.h> +#include <bool.h> + +/*! + * \brief "base class" for firmware version of daughterboard code + */ +struct db_base { + uint16_t dbid; //< daughterboard ID + + uint16_t is_tx : 1; //< is this a transmit db? + uint16_t is_quadrature : 1; + uint16_t i_and_q_swapped : 1; + uint16_t spectrum_inverted : 1; + + uint16_t output_enables; //< bitmask of which pins should be outputs from FPGA + uint16_t used_pins; //< bitmask of pins used by the daughterboard + + u2_fxpt_freq_t freq_min; //< min freq that can be set (Hz) + u2_fxpt_freq_t freq_max; //< max freq that can be set (Hz) + + u2_fxpt_gain_t gain_min; //< min gain that can be set (dB) + u2_fxpt_gain_t gain_max; //< max gain that can be set (dB) + u2_fxpt_gain_t gain_step_size; //< (dB) + + // u2_fxpt_freq_t lo_offset; + + /* + * Auto T/R control values + */ + uint32_t atr_mask; //< which bits to control + uint32_t atr_txval; //< value to use when transmitting + uint32_t atr_rxval; //< value to use when receiving + + //! delay in clk ticks from when Tx fifo gets data to when T/R switches + // uint32_t atr_tx_delay; + + //! delay in clk ticks from when Tx fifo goes empty to when T/R switches + // uint32_t atr_rx_delay; + + bool (*init)(struct db_base *); + bool (*set_freq)(struct db_base *, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc); + bool (*set_gain)(struct db_base *, u2_fxpt_gain_t gain); + bool (*set_tx_enable)(struct db_base *, bool on); +}; + + +#endif /* INCLUDED_DB_BASE_H */ diff --git a/usrp2/firmware/lib/db_basic.c b/usrp2/firmware/lib/db_basic.c new file mode 100644 index 000000000..0f1ad6504 --- /dev/null +++ b/usrp2/firmware/lib/db_basic.c @@ -0,0 +1,161 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include <db_base.h> + +bool db_basic_init(struct db_base *db); +bool db_basic_set_freq(struct db_base *db, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc); +bool db_basic_set_gain(struct db_base *db, u2_fxpt_gain_t gain); +bool db_basic_set_tx_enable(struct db_base *, bool on); + +struct db_basic { + struct db_base base; +}; + + +struct db_basic db_basic_tx = { + .base.dbid = 0x0000, + .base.is_tx = true, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(-90e9), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(90e9), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.atr_mask = 0, + .base.atr_txval = 0, + .base.atr_rxval = 0, + //.base.atr_tx_delay = 0, + //.base.atr_rx_delay = 0, + + .base.init = db_basic_init, + .base.set_freq = db_basic_set_freq, + .base.set_gain = db_basic_set_gain, + .base.set_tx_enable = db_basic_set_tx_enable, +}; + +struct db_basic db_basic_rx = { + .base.dbid = 0x0001, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(-90e9), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(90e9), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.is_quadrature = false, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.atr_mask = 0, + .base.atr_txval = 0, + .base.atr_rxval = 0, + //.base.atr_tx_delay = 0, + //.base.atr_rx_delay = 0, + + .base.init = db_basic_init, + .base.set_freq = db_basic_set_freq, + .base.set_gain = db_basic_set_gain, + .base.set_tx_enable = db_basic_set_tx_enable, +}; + +struct db_basic db_lf_tx = { + .base.dbid = 0x000e, + .base.is_tx = true, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(-32e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(32e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.atr_mask = 0, + .base.atr_txval = 0, + .base.atr_rxval = 0, + //.base.atr_tx_delay = 0, + //.base.atr_rx_delay = 0, + + .base.init = db_basic_init, + .base.set_freq = db_basic_set_freq, + .base.set_gain = db_basic_set_gain, + .base.set_tx_enable = db_basic_set_tx_enable, +}; + +struct db_basic db_lf_rx = { + .base.dbid = 0x000f, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(32e6), + .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(0), + .base.is_quadrature = false, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0), + .base.atr_mask = 0, + .base.atr_txval = 0, + .base.atr_rxval = 0, + //.base.atr_tx_delay = 0, + //.base.atr_rx_delay = 0, + + .base.init = db_basic_init, + .base.set_freq = db_basic_set_freq, + .base.set_gain = db_basic_set_gain, + .base.set_tx_enable = db_basic_set_tx_enable, +}; + + +bool +db_basic_init(struct db_base *db) +{ + return true; +} + +bool +db_basic_set_freq(struct db_base *db, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc) +{ + *dc = 0; + return true; +} + +bool +db_basic_set_gain(struct db_base *db, u2_fxpt_gain_t gain) +{ + return true; +} + +bool +db_basic_set_tx_enable(struct db_base *db, bool on) +{ + return true; +} + diff --git a/usrp2/firmware/lib/db_init.c b/usrp2/firmware/lib/db_init.c new file mode 100644 index 000000000..d47beb11d --- /dev/null +++ b/usrp2/firmware/lib/db_init.c @@ -0,0 +1,340 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include <memory_map.h> +#include <i2c.h> +#include <usrp2_i2c_addr.h> +#include <string.h> +#include <stdio.h> +#include <db.h> +#include <db_base.h> +#include <hal_io.h> +#include <nonstdio.h> + + +struct db_base *tx_dboard; // the tx daughterboard that's installed +struct db_base *rx_dboard; // the rx daughterboard that's installed + +extern struct db_base db_basic_tx; +extern struct db_base db_basic_rx; +extern struct db_base db_lf_tx; +extern struct db_base db_lf_rx; +extern struct db_base db_rfx_400_tx; +extern struct db_base db_rfx_400_rx; +extern struct db_base db_tvrx1; +extern struct db_base db_tvrx2; +extern struct db_base db_tvrx3; + +struct db_base *all_dboards[] = { + &db_basic_tx, + &db_basic_rx, + &db_lf_tx, + &db_lf_rx, + &db_rfx_400_tx, + &db_rfx_400_rx, + &db_tvrx1, + &db_tvrx2, + &db_tvrx3, + 0 +}; + + +typedef enum { UDBE_OK, UDBE_NO_EEPROM, UDBE_INVALID_EEPROM } usrp_dbeeprom_status_t; + +static usrp_dbeeprom_status_t +read_raw_dboard_eeprom (unsigned char *buf, int i2c_addr) +{ + if (!eeprom_read (i2c_addr, 0, buf, DB_EEPROM_CLEN)) + return UDBE_NO_EEPROM; + + if (buf[DB_EEPROM_MAGIC] != DB_EEPROM_MAGIC_VALUE) + return UDBE_INVALID_EEPROM; + + int sum = 0; + unsigned int i; + for (i = 0; i < DB_EEPROM_CLEN; i++) + sum += buf[i]; + + if ((sum & 0xff) != 0) + return UDBE_INVALID_EEPROM; + + return UDBE_OK; +} + + +/* + * Return DBID, -1 <none> or -2 <invalid eeprom contents> + */ +static int +read_dboard_eeprom(int i2c_addr) +{ + unsigned char buf[DB_EEPROM_CLEN]; + + usrp_dbeeprom_status_t s = read_raw_dboard_eeprom (buf, i2c_addr); + + //printf("\nread_raw_dboard_eeprom: %d\n", s); + + switch (s){ + case UDBE_OK: + return (buf[DB_EEPROM_ID_MSB] << 8) | buf[DB_EEPROM_ID_LSB]; + + case UDBE_NO_EEPROM: + default: + return -1; + + case UDBE_INVALID_EEPROM: + return -2; + } +} + + +static struct db_base * +lookup_dbid(int dbid) +{ + if (dbid < 0) + return 0; + + int i; + for (i = 0; all_dboards[i]; i++) + if (all_dboards[i]->dbid == dbid) + return all_dboards[i]; + + return 0; +} + +static struct db_base * +lookup_dboard(int i2c_addr, struct db_base *default_db, char *msg) +{ + struct db_base *db; + int dbid = read_dboard_eeprom(i2c_addr); + printf("%s dbid: 0x%x\n", msg, dbid); + + if (dbid < 0){ // there was some kind of problem. Treat as Basic Tx + return default_db; + } + else if ((db = lookup_dbid(dbid)) == 0){ + return default_db; + printf("No daugherboard code for dbid = 0x%x\n", dbid); + } + return db; +} + +static void +set_atr_regs(int bank, struct db_base *db) +{ + uint32_t val[4]; + int shift; + int mask; + int i; + + val[ATR_IDLE] = db->atr_rxval; + val[ATR_RX] = db->atr_rxval; + val[ATR_TX] = db->atr_txval; + val[ATR_FULL] = db->atr_txval; + + if (bank == GPIO_TX_BANK){ + mask = 0xffff0000; + shift = 16; + } + else { + mask = 0x0000ffff; + shift = 0; + } + + for (i = 0; i < 4; i++){ + int t = (atr_regs->v[i] & ~mask) | ((val[i] << shift) & mask); + //printf("atr_regs[%d] = 0x%x\n", i, t); + atr_regs->v[i] = t; + } +} + +static void +set_gpio_mode(int bank, struct db_base *db) +{ + int i; + + hal_gpio_set_ddr(bank, db->output_enables, 0xffff); + set_atr_regs(bank, db); + + for (i = 0; i < 16; i++){ + if (db->used_pins & (1 << i)){ + // set to either GPIO_SEL_SW or GPIO_SEL_ATR + hal_gpio_set_sel(bank, i, (db->atr_mask & (1 << i)) ? 'a' : 's'); + } + } +} + +void +db_init(void) +{ + + tx_dboard = lookup_dboard(I2C_ADDR_TX_A, &db_basic_tx, "Tx"); + //printf("db_init: tx dbid = 0x%x\n", tx_dboard->dbid); + set_gpio_mode(GPIO_TX_BANK, tx_dboard); + tx_dboard->init(tx_dboard); + + rx_dboard = lookup_dboard(I2C_ADDR_RX_A, &db_basic_rx, "Rx"); + //printf("db_init: rx dbid = 0x%x\n", rx_dboard->dbid); + set_gpio_mode(GPIO_RX_BANK, rx_dboard); + rx_dboard->init(rx_dboard); +} + +/*! + * Calculate the frequency to use for setting the digital down converter. + * + * \param[in] target_freq desired RF frequency (Hz) + * \param[in] baseband_freq the RF frequency that corresponds to DC in the IF. + * + * \param[out] dxc_freq is the value for the ddc + * \param[out] inverted is true if we're operating in an inverted Nyquist zone. +*/ +void +calc_dxc_freq(u2_fxpt_freq_t target_freq, u2_fxpt_freq_t baseband_freq, + u2_fxpt_freq_t *dxc_freq, bool *inverted) +{ + u2_fxpt_freq_t fs = U2_DOUBLE_TO_FXPT_FREQ(100e6); // converter sample rate + u2_fxpt_freq_t delta = target_freq - baseband_freq; + + printf("calc_dxc_freq\n"); + printf(" fs = "); print_fxpt_freq(fs); newline(); + printf(" target = "); print_fxpt_freq(target_freq); newline(); + printf(" baseband = "); print_fxpt_freq(baseband_freq); newline(); + printf(" delta = "); print_fxpt_freq(delta); newline(); + +#if 0 + printf("--- printed as uint64_t ---\n"); + printf(" fs = "); print_uint64(fs); newline(); + printf(" target = "); print_uint64(target_freq); newline(); + printf(" baseband = "); print_uint64(baseband_freq); newline(); + printf(" delta = "); print_uint64(delta); newline(); +#endif + + if (delta >= 0){ + while (delta > fs) + delta -= fs; + if (delta <= fs/2){ // non-inverted region + *dxc_freq = -delta; + *inverted = false; + } + else { // inverted region + *dxc_freq = delta - fs; + *inverted = true; + } + } + else { + while (delta < -fs){ + delta += fs; + if (delta >= -fs/2){ // non-inverted region + *dxc_freq = -delta; + *inverted = false; + } + else { // inverted region + *dxc_freq = delta + fs; + *inverted = true; + } + } + } +} + + +bool +db_tune(struct db_base *db, u2_fxpt_freq_t target_freq, struct tune_result *result) +{ + memset(result, 0, sizeof(*result)); + bool inverted = false; + u2_fxpt_freq_t dxc_freq; + u2_fxpt_freq_t actual_dxc_freq; + + // Ask the d'board to tune as closely as it can to target_freq + bool ok = db->set_freq(db, target_freq, &result->baseband_freq); + + // Calculate the DDC setting that will downconvert the baseband from the + // daughterboard to our target frequency. + calc_dxc_freq(target_freq, result->baseband_freq, &dxc_freq, &inverted); + + // If the spectrum is inverted, and the daughterboard doesn't do + // quadrature downconversion, we can fix the inversion by flipping the + // sign of the dxc_freq... (This only happens using the basic_rx board) + + if (db->spectrum_inverted) + inverted = !inverted; + + if (inverted && !db->is_quadrature){ + dxc_freq = -dxc_freq; + inverted = !inverted; + } + + if (db->is_tx){ + dxc_freq = -dxc_freq; // down conversion versus up conversion + ok &= db_set_duc_freq(dxc_freq, &actual_dxc_freq); + } + else { + ok &= db_set_ddc_freq(dxc_freq, &actual_dxc_freq); + } + + result->dxc_freq = dxc_freq; + result->residual_freq = dxc_freq - actual_dxc_freq; + result->inverted = inverted; + return ok; +} + +static int32_t +compute_freq_control_word(u2_fxpt_freq_t target_freq, u2_fxpt_freq_t *actual_freq) +{ + // If we were using floating point, we'd calculate + // master = 100e6; + // v = (int) rint(target_freq / master_freq) * pow(2.0, 32.0); + + printf("compute_freq_control_word\n"); + printf(" target_freq = "); print_fxpt_freq(target_freq); newline(); + + int32_t master_freq = 100000000; // 100M + + int32_t v = ((target_freq << 12)) / master_freq; + printf(" fcw = %d\n", v); + + *actual_freq = (v * (int64_t) master_freq) >> 12; + + printf(" actual = "); print_fxpt_freq(*actual_freq); newline(); + + return v; +} + + +bool +db_set_ddc_freq(u2_fxpt_freq_t dxc_freq, u2_fxpt_freq_t *actual_dxc_freq) +{ + int32_t v = compute_freq_control_word(dxc_freq, actual_dxc_freq); + dsp_rx_regs->freq = v; + return true; +} + +bool +db_set_duc_freq(u2_fxpt_freq_t dxc_freq, u2_fxpt_freq_t *actual_dxc_freq) +{ + int32_t v = compute_freq_control_word(dxc_freq, actual_dxc_freq); + dsp_tx_regs->freq = v; + return true; +} + +bool +db_set_gain(struct db_base *db, u2_fxpt_gain_t gain) +{ + return db->set_gain(db, gain); +} diff --git a/usrp2/firmware/lib/db_rfx.c b/usrp2/firmware/lib/db_rfx.c new file mode 100644 index 000000000..d56112929 --- /dev/null +++ b/usrp2/firmware/lib/db_rfx.c @@ -0,0 +1,609 @@ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <spi.h> +#include <memory_map.h> +#include <db_base.h> +#include <hal_io.h> +#include <ad9510.h> +#include <stdio.h> +#include <mdelay.h> +#include <lsdac.h> +#include <clocks.h> + + +bool rfx_init_rx(struct db_base *db); +bool rfx_init_tx(struct db_base *db); +bool rfx_set_freq(struct db_base *db, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc); +bool rfx_set_gain_rx(struct db_base *db, u2_fxpt_gain_t gain); +bool rfx_set_gain_tx(struct db_base *db, u2_fxpt_gain_t gain); +bool rfx_set_tx_enable(struct db_base *, bool on); + +// Control Latch Defines +#define P 0 // Prescalar value for setting in regs, must match the next line... +#define PRESCALER 8 // Presacalar value for computations +#define PD 0 // Power down, 0 = normal operation +#define PL 0 // PLL power output +#define MTLD 1 // Mute till lock detect +#define CPGAIN 0 // Charge pump gain, use setting 1, also in N-reg +#define CP3S 0 // Charge pump tri-state, 0 = normal operation +#define PDP 1 // Phase detector polarity +#define MUXOUT 1 // Digital lock detect, active high +#define CR 0 // normal operation +#define PC 1 // core power + +// N Latch Defines +#define DIVSEL 0 // N Counter always operates on full rate +#define N_RSV 0 + +// R Latch Defines +#define R_RSV 0 +#define R_BSC 3 +#define R_TMB 0 +#define R_LDP 1 +#define R_ABP 0 +#define R_DIV 16 + +#define phdet_freq (U2_DOUBLE_TO_FXPT_FREQ(100e6/R_DIV)) + +// IO Pin functions +#define POWER_UP (1 << 7) // Low enables power supply +#define ANT_SW (1 << 6) // On TX DB, 0 = TX, 1 = RX, on RX DB 0 = main ant, 1 = RX2 +#define MIX_EN (1 << 5) // Enable appropriate mixer +#define LOCKDET_MASK (1 << 2) // Input pin + +struct db_rfx_common { + // RFX common stuff + unsigned char DIV2; + unsigned char CP1; + unsigned char CP2; + int freq_mult; + int spi_mask; +}; + +struct db_rfx_dummy { + struct db_base base; + struct db_rfx_common common; +}; + + +struct db_rfx_400_rx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_400_tx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_900_rx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_900_tx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_1200_rx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_1200_tx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_1800_rx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_1800_tx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_2400_rx { + struct db_base base; + struct db_rfx_common common; +}; + +struct db_rfx_2400_tx { + struct db_base base; + struct db_rfx_common common; +}; + + +/* + * The class instances + */ +struct db_rfx_400_rx db_rfx_400_rx = { + .base.dbid = 0x0024, + .base.is_tx = false, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + //.base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = rfx_init_rx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_rx, + .base.set_tx_enable = 0, + .base.atr_mask = 0x00E0, + .base.atr_txval = POWER_UP, + .base.atr_rxval = POWER_UP|MIX_EN, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 0, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_RX_DB, + .common.freq_mult = 2 +}; + + +struct db_rfx_400_tx db_rfx_400_tx = { + .base.dbid = 0x0028, + .base.is_tx = true, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + //.base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = rfx_init_tx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_tx, + .base.set_tx_enable = rfx_set_tx_enable, + .base.atr_mask = 0x00E0, + .base.atr_txval = POWER_UP|MIX_EN, + .base.atr_rxval = POWER_UP|ANT_SW, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 1, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_TX_DB, + .common.freq_mult = 2 +}; + +struct db_rfx_900_rx db_rfx_900_rx = { + .base.dbid = 0x0025, + .base.is_tx = false, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + //.base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = rfx_init_rx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_rx, + .base.set_tx_enable = 0, + .base.atr_mask = 0x00E0, + .base.atr_txval = 0, + .base.atr_rxval = MIX_EN, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 1, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_RX_DB, + .common.freq_mult = 2 +}; + + +struct db_rfx_900_tx db_rfx_900_tx = { + .base.dbid = 0x0029, + .base.is_tx = true, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + //.base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = rfx_init_tx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_tx, + .base.set_tx_enable = rfx_set_tx_enable, + .base.atr_mask = 0x00E0, + .base.atr_txval = MIX_EN, + .base.atr_rxval = ANT_SW, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 1, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_TX_DB, + .common.freq_mult = 2 +}; + +struct db_rfx_1200_rx db_rfx_1200_rx = { + .base.dbid = 0x0026, + .base.is_tx = false, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + //.base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = rfx_init_rx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_rx, + .base.set_tx_enable = 0, + .base.atr_mask = 0x00E0, + .base.atr_txval = 0, + .base.atr_rxval = MIX_EN, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 1, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_RX_DB, + .common.freq_mult = 2 +}; + + +struct db_rfx_1200_tx db_rfx_1200_tx = { + .base.dbid = 0x002a, + .base.is_tx = true, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + //.base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = rfx_init_tx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_tx, + .base.set_tx_enable = rfx_set_tx_enable, + .base.atr_mask = 0x00E0, + .base.atr_txval = MIX_EN, + .base.atr_rxval = ANT_SW, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 1, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_TX_DB, + .common.freq_mult = 2 +}; + +struct db_rfx_1800_rx db_rfx_1800_rx = { + .base.dbid = 0x0034, + .base.is_tx = false, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + //.base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = rfx_init_rx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_rx, + .base.set_tx_enable = 0, + .base.atr_mask = 0x00E0, + .base.atr_txval = 0, + .base.atr_rxval = MIX_EN, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 0, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_RX_DB, + .common.freq_mult = 1 +}; + + +struct db_rfx_1800_tx db_rfx_1800_tx = { + .base.dbid = 0x0035, + .base.is_tx = true, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + //.base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = rfx_init_tx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_tx, + .base.set_tx_enable = rfx_set_tx_enable, + .base.atr_mask = 0x00E0, + .base.atr_txval = MIX_EN, + .base.atr_rxval = ANT_SW, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 0, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_TX_DB, + .common.freq_mult = 1 +}; + + +struct db_rfx_2400_rx db_rfx_2400_rx = { + .base.dbid = 0x0027, + .base.is_tx = false, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + //.base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = rfx_init_rx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_rx, + .base.set_tx_enable = 0, + .base.atr_mask = 0x00E0, + .base.atr_txval = 0, + .base.atr_rxval = MIX_EN, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 0, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_RX_DB, + .common.freq_mult = 1 +}; + + +struct db_rfx_2400_tx db_rfx_2400_tx = { + .base.dbid = 0x002b, + .base.is_tx = true, + .base.output_enables = 0x00E0, + .base.used_pins = 0x00FF, + //.base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(xxx), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = true, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = rfx_init_tx, + .base.set_freq = rfx_set_freq, + .base.set_gain = rfx_set_gain_tx, + .base.set_tx_enable = rfx_set_tx_enable, + .base.atr_mask = 0x00E0, + .base.atr_txval = MIX_EN, + .base.atr_rxval = ANT_SW, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.DIV2 = 0, + .common.CP1 = 7, + .common.CP2 = 7, + .common.spi_mask = SPI_SS_TX_DB, + .common.freq_mult = 1 +}; + + +bool +rfx_init_tx(struct db_base *dbb) +{ + //struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + clocks_enable_tx_dboard(true, 0); + return true; +} + +bool +rfx_init_rx(struct db_base *dbb) +{ + //struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + clocks_enable_rx_dboard(true, 0); + + // test gain + dbb->set_gain(dbb,U2_DOUBLE_TO_FXPT_GAIN(45.0)); + printf("set the gain\n"); + return true; +} + +bool +rfx_set_freq(struct db_base *dbb, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc) +{ + *dc = 0; + struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + //u2_fxpt_freq_t desired_n = db->common.freq_mult*freq/phdet_freq; + //int N_DIV = u2_fxpt_freq_round_to_int(desired_n); + u2_fxpt_freq_t desired_n = ((1LL<<20) * db->common.freq_mult*freq)/phdet_freq; + int N_DIV = u2_fxpt_freq_round_to_int(desired_n); + int B = N_DIV/PRESCALER; + int A = N_DIV - PRESCALER*B; + + if(B<A) + return false; + + int R = (R_RSV<<22)|(R_BSC<<20)|(R_TMB<<19)|(R_LDP<<18)|(R_ABP<<16)|(R_DIV<<2)|1; + int N = (DIVSEL<<23)|(db->common.DIV2<<22)|(CPGAIN<<21)|(B<<8)|(N_RSV<<7)|(A<<2)|2; + int C = (P<<22)|(PD<<20)|(db->common.CP2<<17)|(db->common.CP1<<14)|(PL<<12)| + (MTLD<<11)|(CPGAIN<<10)|(CP3S<<9)|(PDP<<8)|(MUXOUT<<5)|(CR<<4)|(PC<<2)|0; + + spi_transact(SPI_TXONLY,db->common.spi_mask,R,24,SPIF_PUSH_FALL); + spi_transact(SPI_TXONLY,db->common.spi_mask,C,24,SPIF_PUSH_FALL); + mdelay(10); + spi_transact(SPI_TXONLY,db->common.spi_mask,N,24,SPIF_PUSH_FALL); + + printf("A = %d, B = %d, N_DIV = %d\n",A, B, N_DIV); + *dc = (N_DIV * phdet_freq) / db->common.freq_mult; + return true; +} + +bool +rfx_set_gain_tx(struct db_base *dbb, u2_fxpt_gain_t gain) +{ + // There is no analog gain control on TX + return true; +} + +bool +rfx_set_gain_rx(struct db_base *dbb, u2_fxpt_gain_t gain) +{ + struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + + u2_fxpt_gain_t MAXGAIN = U2_DOUBLE_TO_FXPT_GAIN(70.0); + + int offset_q8 = (int)(1.2/3.3*4096*(1<<15)); + int slope_q8 = (int)(-1.0/45.0*4096/3.3*256); + int dacword = ((slope_q8 * gain) + offset_q8)>>15; + printf("DACWORD %d\n",dacword); + lsdac_write_rx(1,dacword); + return true; + /* + def set_gain(self, gain): + """ + Set the gain. + + @param gain: gain in decibels + @returns True/False + """ + maxgain = self.gain_range()[1] - self._u.pga_max() + mingain = self.gain_range()[0] + if gain > maxgain: + pga_gain = gain-maxgain + assert pga_gain <= self._u.pga_max() + agc_gain = maxgain + else: + pga_gain = 0 + agc_gain = gain + V_maxgain = .2 + V_mingain = 1.2 + V_fullscale = 3.3 + dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale + assert dac_value>=0 and dac_value<4096 + return self._u.write_aux_dac(self._which, 0, int(dac_value)) and \ + self._set_pga(int(pga_gain)) + + def gain_range(self): + return (self._u.pga_min(), self._u.pga_max() + 70, 0.05) -- For 900-2400 + return (self._u.pga_min(), self._u.pga_max() + 45, 0.035) -- For 400 + */ +} + + +bool +rfx_set_tx_enable(struct db_base *dbb, bool on) +{ + struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + + // FIXME + + return false; +} + +bool +rfx_lock_detect(struct db_base *dbb) +{ + struct db_rfx_dummy *db = (struct db_rfx_dummy *) dbb; + int pins; + pins = hal_gpio_read( db->base.is_tx ? GPIO_TX_BANK : GPIO_RX_BANK ); + if(pins & LOCKDET_MASK) + return true; + return false; +} + +/* + def select_rx_antenna(self, which_antenna): + """ + Specify which antenna port to use for reception. + @param which_antenna: either 'TX/RX' or 'RX2' + """ + if which_antenna in (0, 'TX/RX'): + self._u.write_io(self._which, 0, RX2_RX1N) + elif which_antenna in (1, 'RX2'): + self._u.write_io(self._which, RX2_RX1N, RX2_RX1N) + else: + raise ValueError, "which_antenna must be either 'TX/RX' or 'RX2'" + + def set_gain(self, gain): + """ + Set the gain. + + @param gain: gain in decibels + @returns True/False + """ + maxgain = self.gain_range()[1] - self._u.pga_max() + mingain = self.gain_range()[0] + if gain > maxgain: + pga_gain = gain-maxgain + assert pga_gain <= self._u.pga_max() + agc_gain = maxgain + else: + pga_gain = 0 + agc_gain = gain + V_maxgain = .2 + V_mingain = 1.2 + V_fullscale = 3.3 + dac_value = (agc_gain*(V_maxgain-V_mingain)/(maxgain-mingain) + V_mingain)*4096/V_fullscale + assert dac_value>=0 and dac_value<4096 + return self._u.write_aux_dac(self._which, 0, int(dac_value)) and \ + self._set_pga(int(pga_gain)) + + def gain_range(self): + return (self._u.pga_min(), self._u.pga_max() + 70, 0.05) -- For 900-2400 + return (self._u.pga_min(), self._u.pga_max() + 45, 0.035) -- For 400 + +*/ diff --git a/usrp2/firmware/lib/db_tvrx.c b/usrp2/firmware/lib/db_tvrx.c new file mode 100644 index 000000000..062d71895 --- /dev/null +++ b/usrp2/firmware/lib/db_tvrx.c @@ -0,0 +1,240 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <i2c.h> +#include <lsdac.h> +#include <memory_map.h> +#include <db_base.h> +#include <hal_io.h> +#include <ad9510.h> +#include <stdio.h> +#include <mdelay.h> + +bool tvrx_init(struct db_base *db); +bool tvrx_set_freq(struct db_base *db, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc); +bool tvrx_set_gain(struct db_base *db, u2_fxpt_gain_t gain); + +#define I2C_ADDR 0x60 +#define ref_freq (U2_DOUBLE_TO_FXPT_FREQ(4e6)/640*8) + +#define ref_div 640 /* choices are 640, 512, 1024 */ + +#if (ref_div == 640) +#define ref_div_byte 0 +#else +#if (ref_div == 512) +#define ref_div_byte 0x6 +#else +#define ref_div_byte 0x2 +#endif +#endif + +#define fast_tuning 0x40 + +#define control_byte_1 (0x88|fast_tuning|ref_div_byte) + + +struct db_tvrx_common { + // TVRX common stuff + u2_fxpt_freq_t first_if; + u2_fxpt_freq_t second_if; +}; + +struct db_tvrx_dummy { + struct db_base base; + struct db_tvrx_common common; +}; + +struct db_tvrx1 { + struct db_base base; + struct db_tvrx_common common; +}; + +struct db_tvrx2 { + struct db_base base; + struct db_tvrx_common common; +}; + +struct db_tvrx3 { + struct db_base base; + struct db_tvrx_common common; +}; + +/* The class instances */ +struct db_tvrx1 db_tvrx1 = { + .base.dbid = 0x0003, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(50e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(860e6), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = false, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = tvrx_init, + .base.set_freq = tvrx_set_freq, + .base.set_gain = tvrx_set_gain, + .base.set_tx_enable = 0, + .base.atr_mask = 0x0000, + .base.atr_txval = 0, + .base.atr_rxval = 0, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.first_if = U2_DOUBLE_TO_FXPT_FREQ(43.75e6), + .common.second_if = U2_DOUBLE_TO_FXPT_FREQ(5.75e6), +}; + +struct db_tvrx2 db_tvrx2 = { + .base.dbid = 0x000c, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(50e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(860e6), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = false, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = tvrx_init, + .base.set_freq = tvrx_set_freq, + .base.set_gain = tvrx_set_gain, + .base.set_tx_enable = 0, + .base.atr_mask = 0x0000, + .base.atr_txval = 0, + .base.atr_rxval = 0, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.first_if = U2_DOUBLE_TO_FXPT_FREQ(44e6), + .common.second_if = U2_DOUBLE_TO_FXPT_FREQ(44e6), +}; + +struct db_tvrx3 db_tvrx3 = { + .base.dbid = 0x0040, + .base.is_tx = false, + .base.output_enables = 0x0000, + .base.used_pins = 0x0000, + .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(50e6), + .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(860e6), + //.base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(xxx), + //.base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(xxx), + .base.is_quadrature = false, + .base.i_and_q_swapped = false, + .base.spectrum_inverted = false, + //.base.lo_offset = U2_DOUBLE_TO_FXPT_FREQ(4e6), + .base.init = tvrx_init, + .base.set_freq = tvrx_set_freq, + .base.set_gain = tvrx_set_gain, + .base.set_tx_enable = 0, + .base.atr_mask = 0x0000, + .base.atr_txval = 0, + .base.atr_rxval = 0, + // .base.atr_tx_delay = + // .base.atr_rx_delay = + .common.first_if = U2_DOUBLE_TO_FXPT_FREQ(44e6), + .common.second_if = U2_DOUBLE_TO_FXPT_FREQ(44e6), +}; + +bool +tvrx_init(struct db_base *dbb) +{ + struct db_tvrx_dummy *db = (struct db_tvrx_dummy *) dbb; + db->base.set_gain(dbb,U2_DOUBLE_TO_FXPT_GAIN(94.0)); + return true; +} + +bool +tvrx_set_freq(struct db_base *dbb, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc) +{ + *dc = 0; + if (freq < dbb->freq_min || freq > dbb->freq_max) + return false; + + struct db_tvrx_dummy *db = (struct db_tvrx_dummy *) dbb; + + u2_fxpt_freq_t target_lo_freq = freq + db->common.first_if; + int N_DIV = u2_fxpt_freq_round_to_int(((1LL<<20) * target_lo_freq)/ref_freq); + + u2_fxpt_freq_t actual_lo_freq = ref_freq * N_DIV; + u2_fxpt_freq_t actual_freq = actual_lo_freq - db->common.first_if; + if(N_DIV > 32767) + return false; + + printf("N_DIV = %d, actual_freq = %d, actual_lo_freq = %d\n", + N_DIV, u2_fxpt_freq_round_to_int(actual_freq),u2_fxpt_freq_round_to_int(actual_freq)); + + char buf[4]; + buf[0] = (N_DIV>>8) & 0xff; + buf[1] = N_DIV & 0xff; + buf[2] = control_byte_1; + buf[3] = (freq < U2_DOUBLE_TO_FXPT_FREQ(158e6)) ? 0xa8 : // VHF LOW + (freq < U2_DOUBLE_TO_FXPT_FREQ(464e6)) ? 0x98 : // VHF HIGH + 0x38; // UHF + + *dc = actual_freq - db->common.second_if; + return i2c_write(I2C_ADDR,buf,4); +} + +bool +tvrx_set_gain(struct db_base *dbb, u2_fxpt_gain_t gain) +{ + struct db_tvrx_dummy *db = (struct db_tvrx_dummy *) dbb; + int rfgain; + int ifgain; + if(gain>U2_DOUBLE_TO_FXPT_GAIN(95.0)) + return false; + if(gain<0) + return false; + + if(gain>U2_DOUBLE_TO_FXPT_GAIN(60.0)) { + rfgain = U2_DOUBLE_TO_FXPT_GAIN(60.0); + ifgain = gain-U2_DOUBLE_TO_FXPT_GAIN(60.0); + } else { + rfgain = gain; + ifgain = 0; + } + + int rf_slope_q8 = 256 * 4096 * 2.5 / 60.0 / 1.22 / 3.3; + int rf_offset_q8 = 128 * 256 * 4096 * 1.25 / 1.22 / 3.3; + int if_slope_q8 = 256 * 4096 * 2.25 / 35.0 / 1.22 / 3.3; + int if_offset_q8 = 128 * 256 * 4096 * 1.4 / 1.22 / 3.3; + + + int rfdac = (rfgain*rf_slope_q8 + rf_offset_q8)>>15; + int ifdac = (ifgain*if_slope_q8 + if_offset_q8)>>15; + lsdac_write_rx(0,rfdac); + lsdac_write_rx(1,ifdac); + + printf("Setting gain %d, rf %d, if %d\n",gain,rfdac,ifdac); + return true; +} + + +bool +tvrx_lock_detect(struct db_base *dbb) +{ + struct db_tvrx_dummy *db = (struct db_tvrx_dummy *) dbb; + return true; +} diff --git a/usrp2/firmware/lib/dbsm.c b/usrp2/firmware/lib/dbsm.c new file mode 100644 index 000000000..5e2042bed --- /dev/null +++ b/usrp2/firmware/lib/dbsm.c @@ -0,0 +1,298 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Double Buffering State Machine + */ + +#include "dbsm.h" +#include "memory_map.h" +#include "buffer_pool.h" +#include "bool.h" +#include "nonstdio.h" +#include <stdlib.h> + +typedef enum { + BS_EMPTY, + BS_FILLING, + BS_FULL, + BS_EMPTYING, +} buffer_state_t; + +buffer_state_t buffer_state[NBUFFERS]; + +bool +dbsm_nop_inspector(dbsm_t *sm, int buf_this) +{ + return false; +} + +void +dbsm_init(dbsm_t *sm, int buf0, + const buf_cmd_args_t *recv, const buf_cmd_args_t *send, + inspector_t inspect) +{ + if (buf0 & 0x1) // must be even + abort(); + + sm->buf0 = buf0; + sm->running = false; + sm->recv_args = *recv; + sm->send_args = *send; + + sm->rx_idle = true; + sm->tx_idle = true; + + sm->inspect = inspect; + + // How much to adjust the last_line register. + // It's 1 for everything but the ethernet. + sm->last_line_adj = recv->port == PORT_ETH ? 3 : 1; + + buffer_state[sm->buf0] = BS_EMPTY; + buffer_state[sm->buf0 ^ 1] = BS_EMPTY; + + sm->precomputed_receive_to_buf_ctrl_word[0] = + (BPC_READ + | BPC_BUFFER(sm->buf0) + | BPC_PORT(sm->recv_args.port) + | BPC_STEP(1) + | BPC_FIRST_LINE(sm->recv_args.first_line) + | BPC_LAST_LINE(sm->recv_args.last_line)); + + sm->precomputed_receive_to_buf_ctrl_word[1] = + (BPC_READ + | BPC_BUFFER(sm->buf0 ^ 1) + | BPC_PORT(sm->recv_args.port) + | BPC_STEP(1) + | BPC_FIRST_LINE(sm->recv_args.first_line) + | BPC_LAST_LINE(sm->recv_args.last_line)); + + sm->precomputed_send_from_buf_ctrl_word[0] = + (BPC_WRITE + | BPC_BUFFER(sm->buf0) + | BPC_PORT(sm->send_args.port) + | BPC_STEP(1) + | BPC_FIRST_LINE(sm->send_args.first_line) + | BPC_LAST_LINE(0)); // last line filled in at runtime + + sm->precomputed_send_from_buf_ctrl_word[1] = + (BPC_WRITE + | BPC_BUFFER(sm->buf0 ^ 1) + | BPC_PORT(sm->send_args.port) + | BPC_STEP(1) + | BPC_FIRST_LINE(sm->send_args.first_line) + | BPC_LAST_LINE(0)); // last line filled in at runtime + +} + +static inline void +dbsm_receive_to_buf(dbsm_t *sm, int bufno) +{ + buffer_pool_ctrl->ctrl = sm->precomputed_receive_to_buf_ctrl_word[bufno & 1]; +} + +static inline void +dbsm_send_from_buf(dbsm_t *sm, int bufno) +{ + buffer_pool_ctrl->ctrl = + (sm->precomputed_send_from_buf_ctrl_word[bufno & 1] + | BPC_LAST_LINE(buffer_pool_status->last_line[bufno] - sm->last_line_adj)); +} + +void +dbsm_start(dbsm_t *sm) +{ + // printf("dbsm_start: buf0 = %d, recv_port = %d\n", sm->buf0, sm->recv_args.port); + + sm->running = true; + + buffer_state[sm->buf0] = BS_EMPTY; + buffer_state[sm->buf0 ^ 1] = BS_EMPTY; + + bp_clear_buf(sm->buf0); + bp_clear_buf(sm->buf0 ^ 1); + + sm->tx_idle = true; + sm->rx_idle = false; + dbsm_receive_to_buf(sm, sm->buf0); + buffer_state[sm->buf0] = BS_FILLING; + +} + + +void +dbsm_stop(dbsm_t *sm) +{ + sm->running = false; + bp_clear_buf(sm->buf0); + bp_clear_buf(sm->buf0 ^ 1); + buffer_state[sm->buf0] = BS_EMPTY; + buffer_state[sm->buf0 ^ 1] = BS_EMPTY; +} + +static void dbsm_process_helper(dbsm_t *sm, int buf_this); +static void dbsm_error_helper(dbsm_t *sm, int buf_this); + +void +dbsm_process_status(dbsm_t *sm, uint32_t status) +{ + if (!sm->running) + return; + + if (status & (BPS_ERROR(sm->buf0) | BPS_ERROR(sm->buf0 ^ 1))){ + putchar('E'); + // Most likely an ethernet Rx error. We just restart the transfer. + if (status & (BPS_ERROR(sm->buf0))) + dbsm_error_helper(sm, sm->buf0); + + if (status & (BPS_ERROR(sm->buf0 ^ 1))) + dbsm_error_helper(sm, sm->buf0 ^ 1); + } + + if (status & BPS_DONE(sm->buf0)) + dbsm_process_helper(sm, sm->buf0); + + if (status & BPS_DONE(sm->buf0 ^ 1)) + dbsm_process_helper(sm, sm->buf0 ^ 1); +} + +static void +dbsm_process_helper(dbsm_t *sm, int buf_this) +{ + int buf_other = buf_this ^ 1; + + if (1){ + bp_clear_buf(buf_this); + + if (buffer_state[buf_this] == BS_FILLING){ + buffer_state[buf_this] = BS_FULL; + // + // does s/w handle this packet? + // + if (sm->inspect(sm, buf_this)){ + // s/w handled the packet; refill the buffer + dbsm_receive_to_buf(sm, buf_this); + buffer_state[buf_this] = BS_FILLING; + } + + else { // s/w didn't handle this; pass it on + + if(buffer_state[buf_other] == BS_EMPTY){ + dbsm_receive_to_buf(sm, buf_other); + buffer_state[buf_other] = BS_FILLING; + } + else + sm->rx_idle = true; + + if (sm->tx_idle){ + sm->tx_idle = false; + dbsm_send_from_buf(sm, buf_this); + buffer_state[buf_this] = BS_EMPTYING; + } + } + } + else { // buffer was emptying + buffer_state[buf_this] = BS_EMPTY; + if (sm->rx_idle){ + sm->rx_idle = false; + dbsm_receive_to_buf(sm, buf_this); + buffer_state[buf_this] = BS_FILLING; + } + if (buffer_state[buf_other] == BS_FULL){ + dbsm_send_from_buf(sm, buf_other); + buffer_state[buf_other] = BS_EMPTYING; + } + else + sm->tx_idle = true; + } + } +} + +static void +dbsm_error_helper(dbsm_t *sm, int buf_this) +{ + bp_clear_buf(buf_this); // clears ERROR flag + + if (buffer_state[buf_this] == BS_FILLING){ + dbsm_receive_to_buf(sm, buf_this); // restart the xfer + } + else { // buffer was emptying + dbsm_send_from_buf(sm, buf_this); // restart the xfer + } +} + +/* + * Handle DSP Tx underrun + */ +void +dbsm_handle_tx_underrun(dbsm_t *sm) +{ + // clear the DSP Tx state machine + dsp_tx_regs->clear_state = 1; + + // If there's a buffer that's empyting, clear it & flush xfer + + if (buffer_state[sm->buf0] == BS_EMPTYING){ + bp_clear_buf(sm->buf0); + dsp_tx_regs->clear_state = 1; // flush partial packet + // drop frame in progress on ground. Pretend it finished + dbsm_process_helper(sm, sm->buf0); + } + else if (buffer_state[sm->buf0 ^ 1] == BS_EMPTYING){ + bp_clear_buf(sm->buf0 ^ 1); + dsp_tx_regs->clear_state = 1; // flush partial packet + // drop frame in progress on ground. Pretend it finished + dbsm_process_helper(sm, sm->buf0 ^ 1); + } +} + +/* + * Handle DSP Rx overrun + */ +void +dbsm_handle_rx_overrun(dbsm_t *sm) +{ + dsp_rx_regs->clear_state = 1; + + // If there's a buffer that's filling, clear it. + // Any restart will be the job of the caller. + + if (buffer_state[sm->buf0] == BS_FILLING) + bp_clear_buf(sm->buf0); + + if (buffer_state[sm->buf0 ^1] == BS_FILLING) + bp_clear_buf(sm->buf0 ^ 1); +} + +void +dbsm_wait_for_opening(dbsm_t *sm) +{ + if (buffer_state[sm->buf0] == BS_EMPTYING){ + // wait for xfer to complete + int mask = BPS_DONE(sm->buf0) | BPS_ERROR(sm->buf0) | BPS_IDLE(sm->buf0); + while ((buffer_pool_status->status & mask) == 0) + ; + } + else if (buffer_state[sm->buf0 ^ 1] == BS_EMPTYING){ + // wait for xfer to complete + int mask = BPS_DONE(sm->buf0 ^ 1) | BPS_ERROR(sm->buf0 ^ 1) | BPS_IDLE(sm->buf0 ^ 1); + while ((buffer_pool_status->status & mask) == 0) + ; + } +} diff --git a/usrp2/firmware/lib/dbsm.h b/usrp2/firmware/lib/dbsm.h new file mode 100644 index 000000000..3a64bf0dc --- /dev/null +++ b/usrp2/firmware/lib/dbsm.h @@ -0,0 +1,90 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_DBSM_H +#define INCLUDED_DBSM_H + +/* + * Double Buffering State Machine + */ + +#include <stdint.h> +#include "bool.h" + +struct _dbsm; +typedef struct _dbsm dbsm_t; + +/* + * pointer to function that does packet inspection. + * + * If one of these returns true, it means that the s/w + * handled that packet, and that it should NOT be passed + * on to the normal destination port. + */ +typedef bool (*inspector_t)(dbsm_t *sm, int buf_this); + +bool dbsm_nop_inspector(dbsm_t *sm, int buf_this); // returns false + + +typedef struct +{ + uint16_t port; + uint16_t first_line; + uint16_t last_line; +} buf_cmd_args_t; + +/*! + * double buffer state machine + */ +struct _dbsm +{ + uint8_t buf0; // Must be even. This machine uses buf0 and buf0+1 + uint8_t running; + uint8_t rx_idle; + uint8_t tx_idle; + buf_cmd_args_t recv_args; + buf_cmd_args_t send_args; + inspector_t inspect; + uint32_t precomputed_receive_to_buf_ctrl_word[2]; + uint32_t precomputed_send_from_buf_ctrl_word[2]; + int last_line_adj; +}; + +void dbsm_init(dbsm_t *sm, int buf0, + const buf_cmd_args_t *recv, const buf_cmd_args_t *send, + inspector_t inspect); + +void dbsm_start(dbsm_t *sm); +void dbsm_stop(dbsm_t *sm); +void dbsm_process_status(dbsm_t *sm, uint32_t status); +void dbsm_handle_tx_underrun(dbsm_t *sm); +void dbsm_handle_rx_overrun(dbsm_t *sm); + +/* + * The cpu calls this when it want to ensure that it can send a buffer + * to the same destination being used by this state machine. + * + * If neither buffer is EMPTYING it returns immediately. If a buffer + * is EMPYTING, it waits for the h/w to transition to the DONE or + * ERROR state. + * + * When this function returns, the caller queues it's buffer and busy + * waits for it to complete. + */ +void dbsm_wait_for_opening(dbsm_t *sm); + +#endif /* INCLUDED_DBSM_H */ diff --git a/usrp2/firmware/lib/eeprom.c b/usrp2/firmware/lib/eeprom.c new file mode 100644 index 000000000..b12ffe082 --- /dev/null +++ b/usrp2/firmware/lib/eeprom.c @@ -0,0 +1,69 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "i2c.h" +#include "mdelay.h" + +static const int EEPROM_PAGESIZE = 16; + +bool +eeprom_write (int i2c_addr, int eeprom_offset, const void *buf, int len) +{ + unsigned char cmd[2]; + const unsigned char *p = (unsigned char *) buf; + + // The simplest thing that could possibly work: + // all writes are single byte writes. + // + // We could speed this up using the page write feature, + // but we write so infrequently, why bother... + + while (len-- > 0){ + cmd[0] = eeprom_offset++; + cmd[1] = *p++; + bool r = i2c_write (i2c_addr, cmd, sizeof (cmd)); + mdelay (10); // delay 10ms worst case write time + if (!r) + return false; + } + return true; +} + +bool +eeprom_read (int i2c_addr, int eeprom_offset, void *buf, int len) +{ + unsigned char *p = (unsigned char *) buf; + + // We setup a random read by first doing a "zero byte write". + // Writes carry an address. Reads use an implicit address. + + unsigned char cmd[1]; + cmd[0] = eeprom_offset; + if (!i2c_write (i2c_addr, cmd, sizeof (cmd))) + return false; + + while (len > 0){ + // int n = std::min (len, MAX_EP0_PKTSIZE); + int n = len; + if (!i2c_read (i2c_addr, p, n)) + return false; + len -= n; + p += n; + } + return true; +} + diff --git a/usrp2/firmware/lib/eth_mac.c b/usrp2/firmware/lib/eth_mac.c new file mode 100644 index 000000000..f0b3a57ba --- /dev/null +++ b/usrp2/firmware/lib/eth_mac.c @@ -0,0 +1,134 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "eth_mac.h" +#include "memory_map.h" +#include "bool.h" +#include "eth_phy.h" // for simulation constants +#include "mdelay.h" + + +#define PHY_ADDR 1 + +void +eth_mac_set_addr(const u2_mac_addr_t *src) +{ + int i; + + // tell mac our source address and enable automatic insertion on Tx. + eth_mac->mac_tx_add_prom_wr = 0; // just in case + for (i = 0; i < 6; i++){ + eth_mac->mac_tx_add_prom_add = i; + eth_mac->mac_tx_add_prom_data = src->addr[i]; + eth_mac->mac_tx_add_prom_wr = 1; + mdelay(1); + eth_mac->mac_tx_add_prom_wr = 0; + mdelay(1); + } + eth_mac->mac_tx_add_en = 1; // overwrite pkt src addr field with this stuff + + // set up receive destination address filter + eth_mac->mac_rx_add_prom_wr = 0; // just in case + for (i = 0; i < 6; i++){ + eth_mac->mac_rx_add_prom_add = i; + eth_mac->mac_rx_add_prom_data = src->addr[i]; + eth_mac->mac_rx_add_prom_wr = 1; + mdelay(1); + eth_mac->mac_rx_add_prom_wr = 0; + mdelay(1); + } + // eth_mac->mac_rx_add_chk_en = 1; // FIXME enable when everything's working +} + + +void +eth_mac_init(const u2_mac_addr_t *src) +{ + eth_mac->miimoder = 25; // divider from CPU clock (50MHz/25 = 2MHz) + + eth_mac_set_addr(src); + + // set rx flow control high and low water marks + // unsigned int lwmark = (2*2048 + 64)/4; // 2 * 2048-byte frames + 1 * 64-byte pause frame + // eth_mac->fc_hwmark = lwmark + 2048/4; // plus a 2048-byte frame + + eth_mac->fc_lwmark = 600; // there are currently 2047 lines in the fifo + eth_mac->fc_hwmark = 1200; + + //eth_mac->tx_pause_en = 0; // 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 = 0; // enable sending pause frames +} + +int +eth_mac_read_rmon(int addr) +{ + int t; + + eth_mac->rmon_rd_addr = addr; + eth_mac->rmon_rd_apply = 1; + while(eth_mac->rmon_rd_grant == 0) + ; + + t = eth_mac->rmon_rd_dout; + eth_mac->rmon_rd_apply = 0; + return t; +} + +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; + eth_mac->miicommand = MIIC_RSTAT; + + while((eth_mac->miistatus & MIIS_BUSY) != 0) + ; + + return eth_mac->miirx_data; +} + +void +eth_mac_miim_write(int addr, int value) +{ + int phy_addr = PHY_ADDR; + eth_mac->miiaddress = ((addr & 0x1f) << 8) | phy_addr; + eth_mac->miitx_data = value; + eth_mac->miicommand = MIIC_WCTRLDATA; + + while((eth_mac->miistatus & MIIS_BUSY) != 0) + ; +} + +int +eth_mac_miim_read_status(void) +{ + if (hwconfig_simulation_p()) + return 0; + + return eth_mac->miistatus; +} diff --git a/usrp2/firmware/lib/eth_mac.h b/usrp2/firmware/lib/eth_mac.h new file mode 100644 index 000000000..291994c5c --- /dev/null +++ b/usrp2/firmware/lib/eth_mac.h @@ -0,0 +1,32 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_ETH_MAC_H +#define INCLUDED_ETH_MAC_H + +#include "usrp2_mac_addr.h" + +void eth_mac_init(const u2_mac_addr_t *src); + +void eth_mac_set_addr(const u2_mac_addr_t *src); +int eth_mac_read_rmon(int addr); +int eth_mac_miim_read(int addr); +void eth_mac_miim_write(int addr, int value); +int eth_mac_miim_read_status(void); + +#endif /* INCLUDED_ETH_MAC_H */ diff --git a/usrp2/firmware/lib/eth_mac_regs.h b/usrp2/firmware/lib/eth_mac_regs.h new file mode 100644 index 000000000..bb6d2519b --- /dev/null +++ b/usrp2/firmware/lib/eth_mac_regs.h @@ -0,0 +1,97 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_ETH_MAC_REGS_H +#define INCLUDED_ETH_MAC_REGS_H + +/* + * See opencores.org 10_100_1000 Mbps Tri-mode Ethernet MAC Specification + * + * In reality, these are 16-bit regs, but are assigned + * on 32-bit boundaries. Because we're little endian, + * declaring them "int" works. + */ +typedef struct { + volatile int tx_hwmark; + volatile int tx_lwmark; + + //! if set, send pause frames automatically + volatile int pause_frame_send_en; + + //! quanta value for pause frame in units of 512 bit times. + volatile int pause_quanta_set; + + volatile int ifg_set; + volatile int full_duplex; + volatile int max_retry; + volatile int mac_tx_add_en; + volatile int mac_tx_add_prom_data; + volatile int mac_tx_add_prom_add; + volatile int mac_tx_add_prom_wr; + + //! if set, other end can pause us (i.e., we pay attention to pause frames) + volatile int tx_pause_en; + + // Flow Control high and low water marks + //! when space available (in 32-bit lines) > hwmark, send un-pause frame + volatile int fc_hwmark; + + //! when space avail (in 32-bit lines) < lwmark, send pause frame + volatile int fc_lwmark; + + volatile int mac_rx_add_chk_en; + volatile int mac_rx_add_prom_data; + volatile int mac_rx_add_prom_add; + volatile int mac_rx_add_prom_wr; + volatile int broadcast_filter_en; + volatile int broadcast_bucket_depth; + volatile int broadcast_bucket_interval; + volatile int rx_append_crc; + volatile int rx_hwmark; + volatile int rx_lwmark; + volatile int crc_chk_en; + volatile int rx_ifg_set; + volatile int rx_max_length; + volatile int rx_min_length; + volatile int rmon_rd_addr; // performance counter access + volatile int rmon_rd_apply; + volatile int rmon_rd_grant; // READONLY + volatile int rmon_rd_dout; // READONLY + volatile int dummy; // READONLY + volatile int line_loop_en; + volatile int speed; + volatile int miimoder; + volatile int miicommand; + volatile int miiaddress; + volatile int miitx_data; + volatile int miirx_data; + volatile int miistatus; +} eth_mac_regs_t; + +// miicommand register +#define MIIC_SCANSSTAT (1 << 0) // Scan status +#define MIIC_RSTAT (1 << 1) // Read status +#define MIIC_WCTRLDATA (1 << 2) // Write control data + +// miistatus register +#define MIIS_LINKFAIL (1 << 0) // The link failed +#define MIIS_BUSY (1 << 1) // The MII is busy (operation in progress) +#define MIIS_NVALID (1 << 2) // The data in the status register is invalid + // This it is only valid when the scan status is active. + +#endif /* INCLUDED_ETH_MAC_REGS_H */ diff --git a/usrp2/firmware/lib/eth_phy.h b/usrp2/firmware/lib/eth_phy.h new file mode 100644 index 000000000..6c16f97b7 --- /dev/null +++ b/usrp2/firmware/lib/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 <http://www.gnu.org/licenses/>. + */ + +/* 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/usrp2/firmware/lib/ethernet.c b/usrp2/firmware/lib/ethernet.c new file mode 100644 index 000000000..0e2f46167 --- /dev/null +++ b/usrp2/firmware/lib/ethernet.c @@ -0,0 +1,281 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "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 "bool.h" +#include "i2c.h" +#include "usrp2_i2c_addr.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) +{ + 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); +} + +/* + * 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; + } + } + 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 that we handle PAUSE frames and asymmetric pause direction. + int t = eth_mac_miim_read(PHY_AUTONEG_ADV); + eth_mac_miim_write(PHY_AUTONEG_ADV, t | NWAY_AR_PAUSE | NWAY_AR_ASM_DIR); + + // 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 u2_mac_addr_t *t) +{ + int i; + bool all_zeros = true; + bool all_ones = true; + for (i = 0; i < 6; i++){ + all_zeros &= t->addr[i] == 0x00; + all_ones &= t->addr[i] == 0xff; + } + return all_ones | all_zeros; +} + +static int8_t src_addr_initialized = false; +static u2_mac_addr_t src_addr = {{ + 0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff + }}; + +const u2_mac_addr_t * +ethernet_mac_addr(void) +{ + if (!src_addr_initialized){ // fetch from eeprom + src_addr_initialized = true; + + // if we're simulating, don't read the EEPROM model, it's REALLY slow + if (hwconfig_simulation_p()) + return &src_addr; + + u2_mac_addr_t tmp; + bool ok = eeprom_read(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &tmp.addr[0], 6); + if (!ok || unprogrammed(&tmp)){ + // use the default + } + else + src_addr = tmp; + } + + return &src_addr; +} + +bool +ethernet_set_mac_addr(const u2_mac_addr_t *t) +{ + bool ok = eeprom_write(I2C_ADDR_MBOARD, MBOARD_MAC_ADDR, &t->addr[0], 6); + if (ok){ + src_addr = *t; + src_addr_initialized = true; + eth_mac_set_addr(t); + } + + 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/usrp2/firmware/lib/ethernet.h b/usrp2/firmware/lib/ethernet.h new file mode 100644 index 000000000..4e5490460 --- /dev/null +++ b/usrp2/firmware/lib/ethernet.h @@ -0,0 +1,75 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_ETHERNET_H +#define INCLUDED_ETHERNET_H + +#include "usrp2_mac_addr.h" +#include "bool.h" + +typedef void (*ethernet_link_changed_callback_t)(int speed); + + +/*! + * \brief one time call to initialize ethernet + */ +void ethernet_init(void); + +/*! + * \brief Specify the function to call on link state changes. + * + * When the link comes up, speed is the link speed in Mbit/s. + * When the link goes down, speed is 0. + */ +void ethernet_register_link_changed_callback(ethernet_link_changed_callback_t cb); + +/*! + * \returns ethernet MAC address + */ +const u2_mac_addr_t *ethernet_mac_addr(void); + +/*! + * \brief write mac address to eeprom and begin using it + */ +bool ethernet_set_mac_addr(const u2_mac_addr_t *t); + + +/* + * \brief read RMON regs and return error mask + */ +int ethernet_check_errors(void); + +#define RME_RX_CRC 0x0001 +#define RME_RX_FIFO_FULL 0x0002 +#define RME_RX_2SHORT_2LONG 0x0004 + +#define RME_TX_JAM_DROP 0x0010 +#define RME_TX_FIFO_UNDER 0x0020 +#define RME_TX_FIFO_OVER 0x0040 + + +typedef enum { LS_UNKNOWN, LS_DOWN, LS_UP } eth_link_state_t; + +#define S_UNKNOWN (-1) // unknown link speed + +typedef struct { + eth_link_state_t link_state; + int link_speed; // in Mb/s +} ethernet_t; + +#endif /* INCLUDED_ETHERNET_H */ diff --git a/usrp2/firmware/lib/hal_io.c b/usrp2/firmware/lib/hal_io.c new file mode 100644 index 000000000..541906825 --- /dev/null +++ b/usrp2/firmware/lib/hal_io.c @@ -0,0 +1,312 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +// conditionalized on HAL_IO_USES_DBOARD_PINS && HAL_IO_USES_UART + +#include "hal_io.h" +#include "memory_map.h" +#include "hal_uart.h" +#include "bool.h" +#include <stdio.h> +#include <string.h> +//#include <assert.h> + +/* + * ======================================================================== + * GPIOS + * ======================================================================== + */ +void +hal_gpio_set_ddr(int bank, int value, int mask) +{ + bank &= 0x1; + + if (bank == GPIO_TX_BANK){ // tx in top half + value <<= 16; + mask <<= 16; + } + else { + value &= 0xffff; + mask &= 0xffff; + } + + int ei = hal_disable_ints(); + gpio_base->ddr = (gpio_base->ddr & ~mask) | (value & mask); + hal_restore_ints(ei); +} + +static bool +code_to_int(char code, int *val) +{ + switch(code){ + case 's': *val = GPIO_SEL_SW; return true; + case 'a': *val = GPIO_SEL_ATR; return true; + case '0': *val = GPIO_SEL_DEBUG_0; return true; + case '1': *val = GPIO_SEL_DEBUG_1; return true; + case '.': + default: + return false; + } +} + +void +hal_gpio_set_sel(int bank, int bitno, char code) +{ + bank &= 0x1; + int t; + + if (!code_to_int(code, &t)) + return; + + int val = t << (2 * bitno); + int mask = 0x3 << (2 * bitno); + + volatile uint32_t *sel = bank == GPIO_TX_BANK ? &gpio_base->tx_sel : &gpio_base->rx_sel; + int ei = hal_disable_ints(); + int v = (*sel & ~mask) | (val & mask); + *sel = v; + hal_restore_ints(ei); + + if (0) + printf("hal_gpio_set_sel(bank=%d, bitno=%d, code=%c) *sel = 0x%x\n", + bank, bitno, code, v); +} + +void +hal_gpio_set_sels(int bank, char *codes) +{ + //assert(strlen(codes) == 16); + + int val = 0; + int mask = 0; + int i; + + for (i = 15; i >= 0; i--){ + val <<= 2; + mask <<= 2; + int t; + if (code_to_int(codes[i], &t)){ + val |= t; + mask |= 0x3; + } + } + + volatile uint32_t *sel = bank == GPIO_TX_BANK ? &gpio_base->tx_sel : &gpio_base->rx_sel; + int ei = hal_disable_ints(); + *sel = (*sel & ~mask) | (val & mask); + hal_restore_ints(ei); +} + + +/*! + * \brief write \p value to gpio pins specified by \p mask. + */ +void +hal_gpio_write(int bank, int value, int mask) +{ + static uint32_t _gpio_io_shadow; + + bank &= 0x1; + + if (bank == GPIO_TX_BANK){ // tx in top half + value <<= 16; + mask <<= 16; + } + else { + value &= 0xffff; + mask &= 0xffff; + } + + //int ei = hal_disable_ints(); + _gpio_io_shadow = (_gpio_io_shadow & ~mask) | (value & mask); + gpio_base->io = _gpio_io_shadow; + //hal_restore_ints(ei); +} + + +/*! + * \brief read GPIO bits + */ +int +hal_gpio_read(int bank) +{ + bank &= 0x1; + int r = gpio_base->io; + if (bank == GPIO_TX_BANK) + r >>= 16; + + return r & 0xffff; +} + +/* + * ======================================================================== + * leds + * ======================================================================== + */ + +static unsigned long leds_shadow = 0; + +void +hal_set_leds(int value, int mask) +{ + int ei = hal_disable_ints(); + leds_shadow = (leds_shadow & ~mask) | (value & mask); + output_regs->leds = leds_shadow; + hal_restore_ints(ei); +} + +void +hal_toggle_leds(int mask) +{ + int ei = hal_disable_ints(); + leds_shadow ^= mask; + output_regs->leds = leds_shadow; + hal_restore_ints(ei); +} + + +// ================================================================ +// primitives +// ================================================================ + +#if defined(HAL_IO_USES_DBOARD_PINS) +// +// Does i/o using high 9-bits of rx daughterboard pins. +// +// 1 1 1 1 1 1 +// 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | char |W| | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// +// Asserts W when writing char +// + +#define W 0x0080 + +void +hal_io_init(void) +{ + // make high 9 bits of tx daughterboard outputs + hal_gpio_set_rx_mode(15, 7, GPIOM_OUTPUT); + + // and set them to zero + hal_gpio_set_rx(0x0000, 0xff80); +} + +void +hal_finish(void) +{ + volatile unsigned long *p = (unsigned long *) 0xC2F0; + *p = 0; +} + +// %c +inline int +putchar(int ch) +{ + hal_gpio_set_rx((s << 8) | W, 0xff80); + hal_gpio_set_rx(0, 0xff80); + return ch; +} + +#elif defined(HAL_IO_USES_UART) + +void +hal_io_init(void) +{ + hal_uart_init(); +} + +void +hal_finish(void) +{ +} + +// %c +inline int +putchar(int ch) +{ + hal_uart_putc(ch); + return ch; +} + +int +getchar(void) +{ + return hal_uart_getc(); +} + +#else // nop all i/o + +void +hal_io_init(void) +{ +} + +void +hal_finish(void) +{ +} + +// %c +inline int +putchar(int ch) +{ + return ch; +} + +int +getchar(void) +{ + return EOF; +} + +#endif + +// ================================================================ +// (slightly) higher level functions +// +// These are here so we can inline the calls to putchar. +// The rest of the stuff was moved to nonstdio.c +// ================================================================ + +// \n +inline void +newline(void) +{ + putchar('\n'); +} + +int +putstr(const char *s) +{ + while (*s) + putchar(*s++); + + return 0; +} + +int +puts(const char *s) +{ + putstr(s); + putchar('\n'); + return 0; +} diff --git a/usrp2/firmware/lib/hal_io.h b/usrp2/firmware/lib/hal_io.h new file mode 100644 index 000000000..5ffebf57b --- /dev/null +++ b/usrp2/firmware/lib/hal_io.h @@ -0,0 +1,174 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_HAL_IO_H +#define INCLUDED_HAL_IO_H + +#include "memory_map.h" + +void hal_io_init(void); +void hal_finish(); + + +/* + * ------------------------------------------------------------------------ + * The GPIO pins are organized into two banks of 16-bits. + * Bank 0 goes to the Tx daughterboard, Bank 1 goes to the Rx daughterboard. + * + * Each pin may be configured as an input or an output from the FPGA. + * For output pins, there are four signals which may be routed to the + * pin. The four signals are the value written by s/w, the output of + * the ATR controller, or two different sources of debug info from the + * FPGA fabric. + * ------------------------------------------------------------------------ + */ + +#define GPIO_TX_BANK 0 // pins that connect to the Tx daughterboard +#define GPIO_RX_BANK 1 // pins that connect to the Rx daughterboard + + +/*! + * \brief Set the data direction for GPIO pins + * + * If the bit is set, it's an output from the FPGA. + * \param value is a 16-bit bitmask of values + * \param mask is a 16-bit bitmask of which bits to effect. + */ +void hal_gpio_set_ddr(int bank, int value, int mask); + +/*! + * \brief Select the source of the signal for an output pin. + * + * \param code is is one of 's', 'a', '0', '1' + * where 's' selects software output, 'a' selects ATR output, '0' selects + * debug 0, '1' selects debug 1. + */ +void hal_gpio_set_sel(int bank, int bitno, char code); + +/*! + * \brief Select the source of the signal for the output pins. + * + * \param codes is is a string of 16 characters composed of '.', 's', + * 'a', '0', or '1' where '.' means "don't change", 's' selects + * software output, 'a' selects ATR output, '0' selects debug 0, '1' + * selects debug 1. + */ +void hal_gpio_set_sels(int bank, char *codes); + + +/*! + * \brief write \p value to gpio pins specified by \p mask. + */ +void hal_gpio_write(int bank, int value, int mask); + +/*! + * \brief read GPIO bits + */ +int hal_gpio_read(int bank); + + +/* + * ------------------------------------------------------------------------ + * control the leds + * + * Low 4-bits are the general purpose leds on the board + * The next bit is the led on the ethernet connector + * ------------------------------------------------------------------------ + */ + +#define LED_0 0x0001 +#define LED_1 0x0002 +#define LED_3 0x0004 +#define LED_4 0x0008 +#define LED_ETH_CONN 0x0010 + +void hal_set_leds(int value, int mask); +void hal_toggle_leds(int mask); + +/* + * ------------------------------------------------------------------------ + * simple timeouts + * ------------------------------------------------------------------------ + */ + + + +static inline void +hal_set_timeout(int delta_ticks) +{ + int t = timer_regs->time + delta_ticks; + if (t == 0) // kills timer + t = 1; + timer_regs->time = t; +} + +/* + * ------------------------------------------------------------------------ + * interrupt enable/disable + * ------------------------------------------------------------------------ + */ + +/*! + * \brief Disable interrupts and return previous interrupt enable state. + * [Microblaze specific] + */ +static inline int +hal_disable_ints(void) +{ + int result, t0; + + asm volatile("mfs %0, rmsr \n\ + andni %1, %0, 0x2 \n\ + mts rmsr, %1" + : "=r" (result), "=r" (t0)); + return result; +} + +/*! + * \brief Enable interrupts and return previous interrupt enable state. + * [Microblaze specific] + */ +static inline int +hal_enable_ints(void) +{ + int result, t0; + + asm volatile("mfs %0, rmsr \n\ + ori %1, %0, 0x2 \n\ + mts rmsr, %1" + : "=r" (result), "=r" (t0)); + return result; +} + +/*! + * \brief Set interrupt enable state to \p prev_state. + * [Microblaze specific] + */ +static inline void +hal_restore_ints(int prev_state) +{ + int t0, t1; + asm volatile("andi %0, %2, 0x2 \n\ + mfs %1, rmsr \n\ + andni %1, %1, 0x2 \n\ + or %1, %1, %0 \n\ + mts rmsr, %1" + : "=r" (t0), "=r"(t1) : "r" (prev_state)); +} + +#endif /* INCLUDED_HAL_IO_H */ diff --git a/usrp2/firmware/lib/hal_uart.c b/usrp2/firmware/lib/hal_uart.c new file mode 100644 index 000000000..f1d46fe84 --- /dev/null +++ b/usrp2/firmware/lib/hal_uart.c @@ -0,0 +1,67 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "hal_uart.h" +#include "hal_io.h" +#include "memory_map.h" + +// First pass, no interrupts + +// Replaced with divisors.py which generates best divisor +//#define CALC_DIVISOR(rate) (WISHBONE_CLK_RATE / ((rate) * 16)) + +#define NSPEEDS 6 +#define MAX_WB_DIV 4 + +static const uint16_t +divisor_table[MAX_WB_DIV+1][NSPEEDS] = { + { 2, 2, 2, 2, 2, 2}, // 0: can't happen + { 651, 326, 163, 109, 54, 27 }, // 1: 100 MHz + { 326, 163, 81, 54, 27, 14 }, // 2: 50 MHz + { 217, 109, 54, 36, 18, 9 }, // 3: 33.3333 MHz + { 163, 81, 41, 27, 14, 7 }, // 4: 25 MHz +}; + +#define u uart_regs + +void +hal_uart_init(void) +{ + u->clkdiv = 217; // 230400 bps +} + +void +hal_uart_putc(int ch) +{ + if (ch == '\n') // FIXME for now map \n -> \r\n + hal_uart_putc('\r'); + + while (u->txlevel == 0) // wait for fifo to have space + ; + + u->txchar = ch; +} + +int +hal_uart_getc(void) +{ + while ((u->rxlevel) == 0) // wait for data to be ready + ; + + return u->rxchar; +} diff --git a/usrp2/firmware/lib/hal_uart.h b/usrp2/firmware/lib/hal_uart.h new file mode 100644 index 000000000..41e78894b --- /dev/null +++ b/usrp2/firmware/lib/hal_uart.h @@ -0,0 +1,63 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_HAL_UART_H +#define INCLUDED_HAL_UART_H + + +/*! + * \brief one-time call to init + */ +void hal_uart_init(void); + +typedef enum { + US_9600, + US_19200, + US_38400, + US_57600, + US_115200, + US_230400, +} hal_uart_speed_t; + +typedef struct { + hal_uart_speed_t speed; +} hal_uart_config_t; + +/*! + * \brief Set uart parameters + * Default is 115,200 bps, 8N1. + */ +void hal_uart_set_config(const hal_uart_config_t *c); + +/*! + * \brief Get uart configuation. + */ +void hal_uart_get_config(hal_uart_config_t *c); + +/*! + * \brief Enqueue \p ch for output over serial port + */ +void hal_uart_putc(int ch); + +/* + * \brief Blocking read of next char from serial port + */ +int hal_uart_getc(void); + + +#endif /* INCLUDED_HAL_UART_H */ diff --git a/usrp2/firmware/lib/i2c.c b/usrp2/firmware/lib/i2c.c new file mode 100644 index 000000000..3f738733b --- /dev/null +++ b/usrp2/firmware/lib/i2c.c @@ -0,0 +1,127 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "i2c.h" +#include "memory_map.h" +#include "stdint.h" + +#define MAX_WB_DIV 4 // maximum wishbone divisor (from 100 MHz MASTER_CLK) + +// prescaler divisor values for 100 kHz I2C [uses 5 * SCLK internally] + +#define PRESCALER(wb_div) (((MASTER_CLK_RATE/(wb_div)) / (5 * 100000)) - 1) + +static uint16_t prescaler_values[MAX_WB_DIV+1] = { + 0xffff, // 0: can't happen + PRESCALER(1), // 1: 100 MHz + PRESCALER(2), // 2: 50 MHz + PRESCALER(3), // 3: 33.333 MHz + PRESCALER(4), // 4: 25 MHz +}; + +void +i2c_init(void) +{ + i2c_regs->ctrl = 0; // disable core + + // setup prescaler depending on wishbone divisor + int wb_div = hwconfig_wishbone_divisor(); + if (wb_div > MAX_WB_DIV) + wb_div = MAX_WB_DIV; + + i2c_regs->prescaler_lo = prescaler_values[wb_div] & 0xff; + i2c_regs->prescaler_hi = (prescaler_values[wb_div] >> 8) & 0xff; + + i2c_regs->ctrl = I2C_CTRL_EN; // enable core + + // FIXME interrupt driven? +} + +static inline void +wait_for_xfer(void) +{ + while (i2c_regs->cmd_status & I2C_ST_TIP) // wait for xfer to complete + ; +} + +static inline bool +wait_chk_ack(void) +{ + wait_for_xfer(); + + if ((i2c_regs->cmd_status & I2C_ST_RXACK) != 0){ // target NAK'd + return false; + } + return true; +} + +bool +i2c_read (unsigned char i2c_addr, unsigned char *buf, unsigned int len) +{ + if (len == 0) // reading zero bytes always works + return true; + + while (i2c_regs->cmd_status & I2C_ST_BUSY) + ; + + i2c_regs->data = (i2c_addr << 1) | 1; // 7 bit address and read bit (1) + // generate START and write addr + i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START; + if (!wait_chk_ack()) + goto fail; + + for (; len > 0; buf++, len--){ + i2c_regs->cmd_status = I2C_CMD_RD | (len == 1 ? (I2C_CMD_NACK | I2C_CMD_STOP) : 0); + wait_for_xfer(); + *buf = i2c_regs->data; + } + return true; + + fail: + i2c_regs->cmd_status = I2C_CMD_STOP; // generate STOP + return false; +} + + +bool +i2c_write(unsigned char i2c_addr, const unsigned char *buf, unsigned int len) +{ + while (i2c_regs->cmd_status & I2C_ST_BUSY) + ; + + i2c_regs->data = (i2c_addr << 1) | 0; // 7 bit address and write bit (0) + + // generate START and write addr (and maybe STOP) + i2c_regs->cmd_status = I2C_CMD_WR | I2C_CMD_START | (len == 0 ? I2C_CMD_STOP : 0); + if (!wait_chk_ack()) + goto fail; + + for (; len > 0; buf++, len--){ + i2c_regs->data = *buf; + i2c_regs->cmd_status = I2C_CMD_WR | (len == 1 ? I2C_CMD_STOP : 0); + if (!wait_chk_ack()) + goto fail; + } + return true; + + fail: + i2c_regs->cmd_status = I2C_CMD_STOP; // generate STOP + return false; +} + + diff --git a/usrp2/firmware/lib/i2c.h b/usrp2/firmware/lib/i2c.h new file mode 100644 index 000000000..ad1e4159a --- /dev/null +++ b/usrp2/firmware/lib/i2c.h @@ -0,0 +1,39 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_I2C_H +#define INCLUDED_I2C_H + +#include "bool.h" + +void i2c_init(void); +bool i2c_read (unsigned char i2c_addr, unsigned char *buf, unsigned int len); +bool i2c_write(unsigned char i2c_addr, const unsigned char *buf, unsigned int len); + + +// Write 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. +// Which EEPROM is determined by i2c_addr. See i2c_addr.h + +bool eeprom_write (int i2c_addr, int eeprom_offset, const void *buf, int len); + +// Read 24LC024 / 24LC025 EEPROM on motherboard or daughterboard. +// Which EEPROM is determined by i2c_addr. See i2c_addr.h + +bool eeprom_read (int i2c_addr, int eeprom_offset, void *buf, int len); + +#endif /* INCLUDED_I2C_H */ diff --git a/usrp2/firmware/lib/lsadc.c b/usrp2/firmware/lib/lsadc.c new file mode 100644 index 000000000..7983552e7 --- /dev/null +++ b/usrp2/firmware/lib/lsadc.c @@ -0,0 +1,73 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "lsadc.h" +#include "spi.h" +#include "memory_map.h" + + +// AD9712 or AD7922 1 MS/s, 10-/12-bit ADCs + +//#define SPI_SS_DEBUG SPI_SS_RX_DB +#define SPI_SS_DEBUG 0 + + +void +lsadc_init(void) +{ + // nop +} + +/* + * The ADC's are pipelined. That is, you have to tell them + * which of the two inputs you want one cycle ahead of time. + * We could optimize and keep track of which one we used last + * time, but for simplicity we'll always tell it which + * one we want. This takes 2 16-bit xfers, one to set the + * input and one to read the one we want. + */ + +int +_lsadc_read(int which_adc, int slave_select) +{ + uint32_t r; + int channel = which_adc & 0x1; + + // Set CHN and STY equal to channel number. We don't want "daisy chain mode" + uint16_t cmd = (channel << 13) | (channel << 12); + + spi_transact(SPI_TXONLY, slave_select | SPI_SS_DEBUG, + cmd, 16, SPIF_PUSH_RISE | SPIF_LATCH_RISE); + + r = spi_transact(SPI_TXRX, slave_select | SPI_SS_DEBUG, + cmd, 16, SPIF_PUSH_RISE | SPIF_LATCH_RISE); + + return r & 0x0fff; +} + +int +lsadc_read_rx(int which_adc) +{ + return _lsadc_read(which_adc, SPI_SS_RX_ADC); +} + +int +lsadc_read_tx(int which_adc) +{ + return _lsadc_read(which_adc, SPI_SS_TX_ADC); +} diff --git a/usrp2/firmware/lib/lsadc.h b/usrp2/firmware/lib/lsadc.h new file mode 100644 index 000000000..319f34d91 --- /dev/null +++ b/usrp2/firmware/lib/lsadc.h @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_LSADC_H +#define INCLUDED_LSADC_H + +#include "memory_map.h" + +/*! + * \brief One time call to initialize low-speed ADCs. + */ +void lsadc_init(void); + +/*! + * \brief Read one of the low-speed Rx daughterboard ADCs. + * \param which_adc in [0, 1] + * + * \returns 12-bit value in [0,4095] + */ +int lsadc_read_rx(int which_adc); + +/*! + * \brief Read one of the low-speed Tx daughterboard ADCs. + * \param which_adc in [0, 1] + * + * \returns 12-bit value in [0,4095] + */ +int lsadc_read_tx(int which_adc); + + +#endif /* INCLUDED_LSADC_H */ diff --git a/usrp2/firmware/lib/lsdac.c b/usrp2/firmware/lib/lsdac.c new file mode 100644 index 000000000..6bc2e5cb5 --- /dev/null +++ b/usrp2/firmware/lib/lsdac.c @@ -0,0 +1,68 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "lsdac.h" +#include "spi.h" +#include "memory_map.h" + +// AD5624, AD5623 + +#define CMD(x) ((x) << 19) +#define CMD_WR_INPUT_N CMD(0) // write input N +#define CMD_UP_DAC_N CMD(1) // update DAC N from input reg +#define CMD_WR_INPUT_N_LDAC CMD(2) // write input N, update all +#define CMD_WR_UP_DAC_N CMD(3) // write and update N +#define CMD_WR_PWR_CONFIG CMD(4) // write power up/down config reg +#define CMD_SW_RESET CMD(5) // force s/w reset +#define CMD_WR_LDAC_CFG CMD(6) // write LDAC config reg +#define CMD_WR_INT_REF_CFG CMD(7) // write internal ref cfg reg (AD5623R only) + + +//#define SPI_SS_DEBUG SPI_SS_TX_DB +#define SPI_SS_DEBUG 0 + +inline static void +_write_rx(uint32_t v) +{ + spi_transact(SPI_TXONLY, SPI_SS_RX_DAC | SPI_SS_DEBUG, v, 24, SPIF_PUSH_RISE); +} + +inline static void +_write_tx(uint32_t v) +{ + spi_transact(SPI_TXONLY, SPI_SS_TX_DAC | SPI_SS_DEBUG, v, 24, SPIF_PUSH_RISE); +} + +void +lsdac_init(void) +{ + _write_tx(CMD_SW_RESET | 0x1); // power-on reset + _write_rx(CMD_SW_RESET | 0x1); // power-on reset +} + +void +lsdac_write_rx(int which_dac, int value) +{ + _write_rx(CMD_WR_UP_DAC_N | ((which_dac & 0x7) << 16) | ((value << 4) & 0xffff)); +} + +void +lsdac_write_tx(int which_dac, int value) +{ + _write_tx(CMD_WR_UP_DAC_N | ((which_dac & 0x7) << 16) | ((value << 4) & 0xffff)); +} diff --git a/usrp2/firmware/lib/lsdac.h b/usrp2/firmware/lib/lsdac.h new file mode 100644 index 000000000..9cad917e3 --- /dev/null +++ b/usrp2/firmware/lib/lsdac.h @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_LSDAC_H +#define INCLUDED_LSDAC_H + +#include "memory_map.h" + +/*! + * \brief One time call to initialize low-speed DACs. + */ +void lsdac_init(void); + +/*! + * \brief Write one of the low-speed Rx daughterboard DACs. + * \param which_dac in [0, 3] + * \param unsigned 12-bit value in [0, 4095] + * + * value maps linearly to output voltage from 0 to 3.3V + */ +void lsdac_write_rx(int which_dac, int value); + +/*! + * \brief Write one of the low-speed Tx daughterboard DACs. + * \param which_dac in [0, 3] + * \param unsigned 12-bit value in [0, 4095] + * + * value maps linearly to output voltage from 0 to 3.3V + */ +void lsdac_write_tx(int which_dac, int value); + + +#endif /* INCLUDED_LSDAC_H */ diff --git a/usrp2/firmware/lib/mdelay.c b/usrp2/firmware/lib/mdelay.c new file mode 100644 index 000000000..c8c119b1a --- /dev/null +++ b/usrp2/firmware/lib/mdelay.c @@ -0,0 +1,73 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "mdelay.h" +#include "memory_map.h" + +// Delay about one millisecond. +// +// Need 33,333 cycles at 33 MHz. +// Each time around the loop is 10 cycles +// + +#define LOOPCNT(wb_div) (MASTER_CLK_RATE/(wb_div) / 10000) + +inline static void +delay_1ms(int loop_count) +{ + int i; + for (i = 0; i < loop_count; i++){ + asm volatile ("or r0, r0, r0\n\ + or r0, r0, r0\n\ + or r0, r0, r0\n\ + or r0, r0, r0\n\ + or r0, r0, r0\n\ + or r0, r0, r0\n\ + or r0, r0, r0\n"); + } +} + +// delay about ms milliseconds +void +mdelay(int ms) +{ + static int loop_count = -1; + + if (hwconfig_simulation_p()) + return; + + if (loop_count < 0){ + // set correct loop_count + static unsigned short lc[8] = { + 0, + LOOPCNT(1), + LOOPCNT(2), + LOOPCNT(3), + LOOPCNT(4), + LOOPCNT(5), + LOOPCNT(6), + LOOPCNT(7) + }; + + loop_count = lc[hwconfig_wishbone_divisor() & 0x7]; + } + + int i; + for (i = 0; i < ms; i++) + delay_1ms(loop_count); +} diff --git a/usrp2/firmware/lib/mdelay.h b/usrp2/firmware/lib/mdelay.h new file mode 100644 index 000000000..226bbb3f7 --- /dev/null +++ b/usrp2/firmware/lib/mdelay.h @@ -0,0 +1,29 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_MDELAY_H +#define INCLUDED_MDELAY_H + +/*! + * \brief Delay about ms milliseconds + * + * If simulating, _very_ short delay + */ +void mdelay(int ms); + +#endif /* INCLUDED_MDELAY_H */ diff --git a/usrp2/firmware/lib/memcpy_wa.c b/usrp2/firmware/lib/memcpy_wa.c new file mode 100644 index 000000000..ef20efaa9 --- /dev/null +++ b/usrp2/firmware/lib/memcpy_wa.c @@ -0,0 +1,42 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "memcpy_wa.h" +#include <stdint.h> +#include <stdlib.h> + +/* + * For copying to/from non-byte-adressable memory, such as + * the buffers. dst, src, and nbytes must all satisfy (x % 4 == 0) + */ +void +memcpy_wa(void *dst, const void *src, size_t nbytes) +{ + if (((intptr_t) dst & 0x3) + || ((intptr_t) src & 0x3) + || (nbytes & 0x3)) + exit(1); /* die! */ + + int *dp = (int *) dst; + int *sp = (int *) src; + unsigned nw = nbytes/4; + + unsigned i; + for (i = 0; i < nw; i++) + dp[i] = sp[i]; +} diff --git a/usrp2/firmware/lib/memcpy_wa.h b/usrp2/firmware/lib/memcpy_wa.h new file mode 100644 index 000000000..072fc148f --- /dev/null +++ b/usrp2/firmware/lib/memcpy_wa.h @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_MEMCPY_WA_H +#define INCLUDED_MEMCPY_WA_H + +#include <stddef.h> + +/* + * For copying to/from non-byte-adressable memory, such as + * the buffers. dst, src, and nbytes must all satisfy (x % 4 == 0) + */ +void memcpy_wa(void *dst, const void *src, size_t nbytes); + +#endif /* INCLUDED_MEMCPY_WA_H */ + + diff --git a/usrp2/firmware/lib/memory_map.h b/usrp2/firmware/lib/memory_map.h new file mode 100644 index 000000000..7dce59d03 --- /dev/null +++ b/usrp2/firmware/lib/memory_map.h @@ -0,0 +1,583 @@ +/* -*- c -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Overall Memory Map + * 0000-7FFF 32K RAM space (16K on 1500, 24K on 2000, 32K on DSP) + * 8000-BFFF 16K Buffer Pool + * C000-FFFF 16K Peripherals + */ + + +#ifndef INCLUDED_MEMORY_MAP_H +#define INCLUDED_MEMORY_MAP_H + +#include <stdint.h> + + +#define MASTER_CLK_RATE 100000000 // 100 MHz + + +//////////////////////////////////////////////////////////////// +// +// Memory map for embedded wishbone bus +// +//////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////// +// Main RAM, Slave 0 + +#define RAM_BASE 0x0000 + +//////////////////////////////////////////////////////////////// +// Buffer Pool RAM, Slave 1 +// +// The buffers themselves are located in Slave 1, Buffer Pool RAM. +// The status registers are in Slave 5, Buffer Pool Status. +// The control register is in Slave 7, Settings Bus. + +#define BUFFER_POOL_RAM_BASE 0x8000 + +#define NBUFFERS 8 +#define BP_NLINES 0x0200 // number of 32-bit lines in a buffer +#define BP_LAST_LINE (BP_NLINES - 1) // last line in a buffer + +#define buffer_pool_ram \ + ((uint32_t *) BUFFER_POOL_RAM_BASE) + +#define buffer_ram(n) (&buffer_pool_ram[(n) * BP_NLINES]) + + +///////////////////////////////////////////////////// +// SPI Core, Slave 2. See core docs for more info +#define SPI_BASE 0xC000 // Base address (16-bit) + +typedef struct { + volatile uint32_t txrx0; + volatile uint32_t txrx1; + volatile uint32_t txrx2; + volatile uint32_t txrx3; + volatile uint32_t ctrl; + volatile uint32_t div; + volatile uint32_t ss; +} spi_regs_t; + +#define spi_regs ((spi_regs_t *) SPI_BASE) + + +// Masks for controlling different peripherals +#define SPI_SS_AD9510 1 +#define SPI_SS_AD9777 2 +#define SPI_SS_RX_DAC 4 +#define SPI_SS_RX_ADC 8 +#define SPI_SS_RX_DB 16 +#define SPI_SS_TX_DAC 32 +#define SPI_SS_TX_ADC 64 +#define SPI_SS_TX_DB 128 + +// 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 +} 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 DSP_TX_BASE 0xD600 +#define DSP_RX_BASE 0xD680 + +#define LAST_SETTING_REG 0xD7FC // last valid setting register + +// --- 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; +} 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 + +#define output_regs ((output_regs_t *) MISC_OUTPUT_BASE) + +// --- dsp tx regs --- + +#define MIN_INTERP 1 +#define MAX_INTERP 128 + +typedef struct { + volatile int32_t freq; + volatile uint32_t scale_iq; // {scale_i,scale_q} + volatile uint32_t interp_rate; + volatile uint32_t clear_state; // clears out state machine, fifos, + // NOT freq, scale, interp +} dsp_tx_regs_t; + +#define dsp_tx_regs ((dsp_tx_regs_t *) DSP_TX_BASE) + +// --- dsp rx regs --- + +#define T_NOW (-1) + +#define MIN_DECIM 1 +#define MAX_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 rx_time; // when to begin reception + volatile uint32_t rx_command; // {now, chain, num_lines(21), lines_per_frame(9) + volatile uint32_t clear_state; // clears out state machine, fifos, + // cmd queue, NOT freq, scale, decim + 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 + volatile uint32_t adc_mux; // 4 bits -- lowest 2 for adc_i, next for adc_q + +} dsp_rx_regs_t; + +#define dsp_rx_regs ((dsp_rx_regs_t *) DSP_RX_BASE) + +#define MK_RX_CMD(num_lines, lines_per_frame, now, chain) \ + (((num_lines) << 9) | ((lines_per_frame) & 0x1ff) \ + | (((now) & 0x1) << 31) | (((chain) & 0x1) << 30)) + +/* + * --- 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_TIMER 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_TO_MASK(x) (1 << (x)) + +#define PIC_BUFFER_INT IRQ_TO_MASK(IRQ_BUFFER) +#define PIC_TIMER_INT IRQ_TO_MASK(IRQ_TIMER) +#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) + + +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) + +/////////////////////////////////////////////////// +// Timer, Slave 9 + +#define TIMER_BASE 0xDC00 + +typedef struct { + volatile uint32_t time; // R: current, W: set time to interrupt +} timer_regs_t; + +#define timer_regs ((timer_regs_t *) TIMER_BASE) + +/////////////////////////////////////////////////// +// 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) + +/////////////////////////////////////////////////// +// Time Sync Controller, Slave 12 + +#define TIMESYNC_BASE 0xE800 + +typedef struct { + volatile uint32_t tick_control; + volatile uint32_t tick_interval; + volatile uint32_t delta_time; +} timesync_regs_t; + +#define timesync_regs ((timesync_regs_t *) TIMESYNC_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; + 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) + +/////////////////////////////////////////////////// + +#endif + diff --git a/usrp2/firmware/lib/memset_wa.c b/usrp2/firmware/lib/memset_wa.c new file mode 100644 index 000000000..da5da21ab --- /dev/null +++ b/usrp2/firmware/lib/memset_wa.c @@ -0,0 +1,45 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "memset_wa.h" +#include <stdint.h> +#include <stdlib.h> + +/* + * For setting non-byte-adressable memory, such as + * the buffers. dst and nbytes must all satisfy (x % 4 == 0) + */ +void * +memset_wa(void *dst, int c, size_t nbytes) +{ + if (((intptr_t) dst & 0x3) + || (nbytes & 0x3)) + exit(1); /* die! */ + + int *dp = (int *) dst; + + c &= 0xff; + int v = (c << 24) | (c << 16) | (c << 8) | c; + unsigned nw = nbytes/4; + + unsigned i; + for (i = 0; i < nw; i++) + dp[i] = v; + + return dst; +} diff --git a/usrp2/firmware/lib/memset_wa.h b/usrp2/firmware/lib/memset_wa.h new file mode 100644 index 000000000..46d903d53 --- /dev/null +++ b/usrp2/firmware/lib/memset_wa.h @@ -0,0 +1,27 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_MEMSET_WA_H +#define INCLUDED_MEMSET_WA_H + +#include <stdlib.h> + +void *memset_wa(void *s, int c, size_t n); + + +#endif /* INCLUDED_MEMSET_WA_H */ diff --git a/usrp2/firmware/lib/microblaze.ld b/usrp2/firmware/lib/microblaze.ld new file mode 100644 index 000000000..ef816cf2e --- /dev/null +++ b/usrp2/firmware/lib/microblaze.ld @@ -0,0 +1,163 @@ +OUTPUT_FORMAT("elf32-microblaze", "", "") +/* SEARCH_DIR(/home/jwilliams/tmp/microblaze-toolchain-sources/release/lin/microblaze//lib);*/ + +ENTRY(_start) + +MEMORY { + flatmem : ORIGIN = 0x0, LENGTH = 0x00008000 /* 32KB */ +} + +SECTIONS +{ + _TEXT_START_ADDR = DEFINED(_TEXT_START_ADDR) ? _TEXT_START_ADDR : 0x50; + .vectors.reset 0x0 : { *(.vectors.reset) } > flatmem + .vectors.sw_exception 0x8 : { *(.vectors.sw_exception) } > flatmem + .vectors.interrupt 0x10 : { *(.vectors.interrupt) } > flatmem + .vectors.hw_exception 0x20 : { *(.vectors.hw_exception) } >flatmem + . = _TEXT_START_ADDR; + _ftext = .; + .text : { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + } > flatmem + _etext = .; + .init : { KEEP (*(.init)) } > flatmem =0 + .fini : { KEEP (*(.fini)) } > flatmem =0 + PROVIDE (__CTOR_LIST__ = .); + PROVIDE (___CTOR_LIST__ = .); + .ctors : + { + /* gcc uses crtbegin.o to find the start of + the constructors, so we make sure it is + first. Because this is a wildcard, it + doesn't matter if the user does not + actually link against crtbegin.o; the + linker won't look for a file to match a + wildcard. The wildcard also means that it + doesn't matter which directory crtbegin.o + is in. */ + KEEP (*crtbegin.o(.ctors)) + /* We don't want to include the .ctor section from + from the crtend.o file until after the sorted ctors. + The .ctor section from the crtend file contains the + end of ctors marker and it must be last */ + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + } > flatmem + PROVIDE (__CTOR_END__ = .); + PROVIDE (___CTOR_END__ = .); + PROVIDE (__DTOR_LIST__ = .); + PROVIDE (___DTOR_LIST__ = .); + .dtors : + { + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } > flatmem + PROVIDE (__DTOR_END__ = .); + PROVIDE (___DTOR_END__ = .); + . = ALIGN(4); + _frodata = . ; + .rodata : { + *(.rodata) + *(.gnu.linkonce.r.*) + CONSTRUCTORS; /* Is this needed? */ + } > flatmem + _erodata = .; + /* Alignments by 8 to ensure that _SDA2_BASE_ on a word boundary */ + /* Note that .sdata2 and .sbss2 must be contiguous */ + . = ALIGN(8); + _ssrw = .; + .sdata2 : { + *(.sdata2) + *(.gnu.linkonce.s2.*) + } > flatmem + . = ALIGN(4); + .sbss2 : { + PROVIDE (__sbss2_start = .); + *(.sbss2) + *(.gnu.linkonce.sb2.*) + PROVIDE (__sbss2_end = .); + } > flatmem + . = ALIGN(8); + _essrw = .; + _ssrw_size = _essrw - _ssrw; + PROVIDE (_SDA2_BASE_ = _ssrw + (_ssrw_size / 2 )); + . = ALIGN(4); + _fdata = .; + .data : { + *(.data) + *(.gnu.linkonce.d.*) + CONSTRUCTORS; /* Is this needed? */ + } > flatmem + _edata = . ; + /* Added to handle pic code */ + .got : { + *(.got) + } > flatmem + .got1 : { + *(.got1) + } > flatmem + .got2 : { + *(.got2) + } > flatmem + /* Added by Sathya to handle C++ exceptions */ + .eh_frame : { + *(.eh_frame) + } > flatmem + .jcr : { + *(.jcr) + } > flatmem + .gcc_except_table : { + *(.gcc_except_table) + } > flatmem + /* Alignments by 8 to ensure that _SDA_BASE_ on a word boundary */ + /* Note that .sdata and .sbss must be contiguous */ + . = ALIGN(8); + _ssro = .; + .sdata : { + *(.sdata) + *(.gnu.linkonce.s.*) + } > flatmem + . = ALIGN(4); + .sbss : { + PROVIDE (__sbss_start = .); + *(.sbss) + *(.gnu.linkonce.sb.*) + PROVIDE (__sbss_end = .); + } > flatmem + . = ALIGN(8); + _essro = .; + _ssro_size = _essro - _ssro; + PROVIDE (_SDA_BASE_ = _ssro + (_ssro_size / 2 )); + . = ALIGN(4); + _fbss = .; + .bss : { + PROVIDE (__bss_start = .); + *(.bss) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + PROVIDE (__bss_end = .); + _heap = .; + _HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : 0x0; + _STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : 0x800; + . += _HEAP_SIZE; + _heap_end = .; + . += _STACK_SIZE; + . = ALIGN(8); + _stack = .; + _end = .; + } > flatmem + .tdata : { + *(.tdata) + *(.gnu.linkonce.td.*) + } > flatmem + .tbss : { + *(.tbss) + *(.gnu.linkonce.tb.*) + } > flatmem +} diff --git a/usrp2/firmware/lib/nonstdio.c b/usrp2/firmware/lib/nonstdio.c new file mode 100644 index 000000000..1c991afee --- /dev/null +++ b/usrp2/firmware/lib/nonstdio.c @@ -0,0 +1,80 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <nonstdio.h> + +static const char hex[16] = "0123456789ABCDEF"; + +// %x +void +puthex4(unsigned long x) +{ + putchar(hex[x & 0xf]); +} + +// %02x +void +puthex8(unsigned long x) +{ + putchar(hex[(x >> 4) & 0xf]); + putchar(hex[x & 0xf]); +} + +// %04x +void +puthex16(unsigned long x) +{ + puthex8(x >> 8); + puthex8(x); +} + +// %08x +void +puthex32(unsigned long x) +{ + puthex16(x >> 16); + puthex16(x); +} + +void +puthex4_nl(unsigned long x) +{ + puthex4(x); + newline(); +} + +void +puthex8_nl(unsigned long x) +{ + puthex8(x); + newline(); +} + +void +puthex16_nl(unsigned long x) +{ + puthex16(x); + newline(); +} + +void +puthex32_nl(unsigned long x) +{ + puthex32(x); + newline(); +} diff --git a/usrp2/firmware/lib/nonstdio.h b/usrp2/firmware/lib/nonstdio.h new file mode 100644 index 000000000..3fd9e39bb --- /dev/null +++ b/usrp2/firmware/lib/nonstdio.h @@ -0,0 +1,46 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_NONSTDIO_H +#define INCLUDED_NONSTDIO_H + +#include <stdio.h> +#include <usrp2_types.h> +#include <stddef.h> + +void putstr(const char *s); // cf puts, no added newline +void puthex4(unsigned long x); // output 1 hex digit +void puthex8(unsigned long x); // output 2 hex digits +void puthex16(unsigned long x); // output 4 hex digits +void puthex32(unsigned long x); // output 8 hex digits +void puthex4_nl(unsigned long x); // ... followed by newline +void puthex8_nl(unsigned long x); +void puthex16_nl(unsigned long x); +void puthex32_nl(unsigned long x); +#define puthex puthex32 +#define puthex_nl puthex32_nl +void newline(); // putchar('\n') + +void print_mac_addr(const unsigned char addr[6]); +void print_fxpt_freq(u2_fxpt_freq_t v); +void print_fxpt_gain(u2_fxpt_gain_t v); +void print_uint64(uint64_t v); + +void print_buffer(uint32_t *buf, size_t n); + +#endif /* INCLUDED_NONSTDIO_H */ diff --git a/usrp2/firmware/lib/pic.c b/usrp2/firmware/lib/pic.c new file mode 100644 index 000000000..592a07aef --- /dev/null +++ b/usrp2/firmware/lib/pic.c @@ -0,0 +1,94 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "pic.h" +#include "hal_io.h" +#include "memory_map.h" + + +#define NVECTORS 8 + +/* + * Our secondary interrupt vector. + */ +irq_handler_t pic_vector[NVECTORS] = { + nop_handler, + nop_handler, + nop_handler, + nop_handler, + nop_handler, + nop_handler, + nop_handler, + nop_handler +}; + + +void +pic_init(void) +{ + // uP is level triggered + + pic_regs->mask = ~0; // mask all interrupts + pic_regs->edge_enable = PIC_TIMER_INT | PIC_PHY_INT; + pic_regs->polarity = ~0 & ~PIC_PHY_INT; // rising edge + pic_regs->pending = ~0; // clear all pending ints +} + +/* + * This magic gets pic_interrupt_handler wired into the + * system interrupt handler with the appropriate prologue and + * epilogue. + */ +void pic_interrupt_handler() __attribute__ ((interrupt_handler)); + +void pic_interrupt_handler() +{ + // pending and not masked interrupts + int live = pic_regs->pending & ~pic_regs->mask; + + // FIXME loop while there are interrupts to service. + // That will reduce our overhead. + + // handle the first one set + int i; + int mask; + for (i=0, mask=1; i < NVECTORS; i++, mask <<= 1){ + if (mask & live){ // handle this one + // puthex_nl(i); + (*pic_vector[i])(i); + pic_regs->pending = mask; // clear pending interrupt + return; + } + } +} + +void +pic_register_handler(unsigned irq, irq_handler_t handler) +{ + if (irq >= NVECTORS) + return; + pic_vector[irq] = handler; + + pic_regs->mask &= ~IRQ_TO_MASK(irq); +} + +void +nop_handler(unsigned irq) +{ + // nop +} diff --git a/usrp2/firmware/lib/pic.h b/usrp2/firmware/lib/pic.h new file mode 100644 index 000000000..68918f9ad --- /dev/null +++ b/usrp2/firmware/lib/pic.h @@ -0,0 +1,35 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_PIC_H +#define INCLUDED_PIC_H + +typedef void (*irq_handler_t)(unsigned irq); + +void pic_init(void); +void pic_register_handler(unsigned irq, irq_handler_t handler); + +void nop_handler(unsigned irq); // default handler does nothing + +// FIXME inline assembler +int pic_disable_interrupts(); +int pic_enable_interrupts(); +void pic_restore_interrupts(int prev_status); + + +#endif /* INCLUDED_PIC_H */ diff --git a/usrp2/firmware/lib/print_buffer.c b/usrp2/firmware/lib/print_buffer.c new file mode 100644 index 000000000..9f9104bb5 --- /dev/null +++ b/usrp2/firmware/lib/print_buffer.c @@ -0,0 +1,36 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <nonstdio.h> + +void +print_buffer(uint32_t *buf, size_t n) +{ + size_t i; + for (i = 0; i < n; i++){ + if (i % 4 == 0) + puthex16(i * 4); + + putchar(' '); + puthex32(buf[i]); + if (i % 4 == 3) + newline(); + } + + newline(); +} + diff --git a/usrp2/firmware/lib/print_fxpt.c b/usrp2/firmware/lib/print_fxpt.c new file mode 100644 index 000000000..185bbc51b --- /dev/null +++ b/usrp2/firmware/lib/print_fxpt.c @@ -0,0 +1,83 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <nonstdio.h> + +/* + * print uint64_t + */ +void +print_uint64(uint64_t u) +{ + const char *_hex = "0123456789ABCDEF"; + if (u >= 10) + print_uint64(u/10); + putchar(_hex[u%10]); +} + +static void +print_thousandths(int thousandths) +{ + putchar('.'); + if (thousandths < 100) + putchar('0'); + if (thousandths < 10) + putchar('0'); + printf("%d", thousandths); +} + + +void +print_fxpt_freq(u2_fxpt_freq_t v) +{ + if (v < 0){ + v = -v; + putchar('-'); + } + + int64_t int_part = v >> 20; + int32_t frac_part = v & ((1 << 20) - 1); + +#if 0 + // would work, if we had it + printf("%lld.%03d", int_part, (frac_part * 1000) >> 20); +#else + print_uint64(int_part); + print_thousandths((frac_part * 1000) >> 20); +#endif +} + +void +print_fxpt_gain(u2_fxpt_gain_t v) +{ + if (v < 0){ + v = -v; + putchar('-'); + } + + int32_t int_part = v >> 7; + int32_t frac_part = v & ((1 << 7) - 1); + +#if 0 + // would work, if we had it + printf("%d.%03d", int_part, (frac_part * 1000) >> 7); +#else + printf("%d", int_part); + print_thousandths((frac_part * 1000) >> 7); +#endif +} + diff --git a/usrp2/firmware/lib/print_mac_addr.c b/usrp2/firmware/lib/print_mac_addr.c new file mode 100644 index 000000000..838fd614a --- /dev/null +++ b/usrp2/firmware/lib/print_mac_addr.c @@ -0,0 +1,30 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "nonstdio.h" + +void +print_mac_addr(const unsigned char addr[6]) +{ + puthex8(addr[0]); putchar(':'); + puthex8(addr[1]); putchar(':'); + puthex8(addr[2]); putchar(':'); + puthex8(addr[3]); putchar(':'); + puthex8(addr[4]); putchar(':'); + puthex8(addr[5]); +} + diff --git a/usrp2/firmware/lib/print_rmon_regs.c b/usrp2/firmware/lib/print_rmon_regs.c new file mode 100644 index 000000000..6d9986909 --- /dev/null +++ b/usrp2/firmware/lib/print_rmon_regs.c @@ -0,0 +1,44 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "print_rmon_regs.h" +#include "eth_mac.h" +#include "nonstdio.h" + +void +print_rmon_regs(void) +{ + int i; + for (i=0; i <= 0x10; i++){ + putstr("RMON[0x"); + puthex8(i); + putstr("] = "); + printf("%d\n", eth_mac_read_rmon(i)); + } + + for (i=0x20; i <= 0x30; i++){ + putstr("RMON[0x"); + puthex8(i); + putstr("] = "); + printf("%d\n", eth_mac_read_rmon(i)); + } +} diff --git a/usrp2/firmware/lib/print_rmon_regs.h b/usrp2/firmware/lib/print_rmon_regs.h new file mode 100644 index 000000000..44e52da84 --- /dev/null +++ b/usrp2/firmware/lib/print_rmon_regs.h @@ -0,0 +1,24 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_PRINT_RMON_REGS_H +#define INCLUDED_PRINT_RMON_REGS_H + +void print_rmon_regs(void); + +#endif /* INCLUDED_PRINT_RMON_REGS_H */ diff --git a/usrp2/firmware/lib/printf.c b/usrp2/firmware/lib/printf.c new file mode 100644 index 000000000..45bd57cb9 --- /dev/null +++ b/usrp2/firmware/lib/printf.c @@ -0,0 +1,134 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Based on code from the SDCC z80 library ;) + */ + +#include <stdarg.h> +#include <stdio.h> + +static void +_printn(unsigned u, unsigned base, char issigned, + void (*emitter)(char, void *), void *pData) +{ + const char *_hex = "0123456789ABCDEF"; + if (issigned && ((int)u < 0)) { + (*emitter)('-', pData); + u = (unsigned)-((int)u); + } + if (u >= base) + _printn(u/base, base, 0, emitter, pData); + (*emitter)(_hex[u%base], pData); +} + +static void +_printf(const char *format, void (*emitter)(char, void *), + void *pData, va_list va) +{ + while (*format) { + if (*format != '%') + (*emitter)(*format, pData); + else { + switch (*++format) { + case 0: /* hit end of format string */ + return; + case 'c': + { + char c = (char)va_arg(va, int); + (*emitter)(c, pData); + break; + } + case 'u': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 10, 0, emitter, pData); + break; + } + case 'd': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 10, 1, emitter, pData); + break; + } + case 'x': + case 'p': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 16, 0, emitter, pData); + break; + } + case 's': + { + char *s = va_arg(va, char *); + while (*s) { + (*emitter)(*s, pData); + s++; + } + break; + } + } + } + format++; + } +} + +static void +_char_emitter(char c, void *pData __attribute__((unused))) +{ + putchar(c); +} + +int +printf(const char *format, ...) +{ + va_list va; + va_start(va, format); + + _printf(format, _char_emitter, NULL, va); + + va_end(va); + + // wrong return value... + return 0; +} + + +#if 0 + +// Totally dangerous. Don't use +static void +_buf_emitter(char c, void *pData) +{ + *((*((char **)pData)))++ = c; +} + +int sprintf(char *pInto, const char *format, ...) +{ + va_list va; + va_start(va, format); + + _printf(format, _buf_emitter, &pInto, va); + *pInto++ = '\0'; + + va_end(va); + + // FIXME wrong return value + return 0; +} +#endif diff --git a/usrp2/firmware/lib/printf.c.smaller b/usrp2/firmware/lib/printf.c.smaller new file mode 100644 index 000000000..4d858648d --- /dev/null +++ b/usrp2/firmware/lib/printf.c.smaller @@ -0,0 +1,134 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Based on code from the SDCC z80 library ;) + */ + +#include <stdarg.h> +#include <stdio.h> +#include <hal_io.h> /* FIXME refactor into stdio */ + +#define PUTCHAR(x) hal_putc(x) + + +static void +_printn(unsigned u, unsigned base, char issigned) +{ + const char *_hex = "0123456789ABCDEF"; + if (issigned && ((int)u < 0)) { + PUTCHAR('-'); + u = (unsigned)-((int)u); + } + if (u >= base) + _printn(u/base, base, 0); + PUTCHAR(_hex[u%base]); +} + +static void +_printf(const char *format, va_list va) +{ + while (*format) { + if (*format != '%') + PUTCHAR(*format); + else { + switch (*++format) { + case 0: /* hit end of format string */ + return; + case 'c': + { + char c = (char)va_arg(va, int); + PUTCHAR(c); + break; + } + case 'u': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 10, 0); + break; + } + case 'd': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 10, 1); + break; + } + case 'x': + case 'p': + { + unsigned u = va_arg(va, unsigned); + _printn(u, 16, 0); + break; + } + case 's': + { + char *s = va_arg(va, char *); + while (*s) { + PUTCHAR(*s); + s++; + } + break; + } + } + } + format++; + } +} + +#if 0 +static void +_char_emitter(char c, void *pData __attribute__((unused))) +{ + hal_putc(c); +} +#endif + +int +printf(const char *format, ...) +{ + va_list va; + va_start(va, format); + + _printf(format, va); + + // wrong return value... + return 0; +} + + +#if 0 + +// Totally dangerous. Don't use +static void +_buf_emitter(char c, void *pData) +{ + *((*((char **)pData)))++ = c; +} + +int sprintf(char *pInto, const char *format, ...) +{ + va_list va; + va_start(va, format); + + _printf(format, _buf_emitter, &pInto, va); + *pInto++ = '\0'; + + // FIXME wrong return value + return 0; +} +#endif diff --git a/usrp2/firmware/lib/spi.c b/usrp2/firmware/lib/spi.c new file mode 100644 index 000000000..937397df6 --- /dev/null +++ b/usrp2/firmware/lib/spi.c @@ -0,0 +1,61 @@ +/* + * Copyright 2007,2008 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "spi.h" +#include "memory_map.h" + +void +spi_init(void) +{ + /* + * f_sclk = f_wb / ((div + 1) * 2) + */ + spi_regs->div = 1; // 0 = Div by 2 (25 MHz); 1 = Div-by-4 (12.5 MHz) +} + +void +spi_wait(void) +{ + while (spi_regs->ctrl & SPI_CTRL_GO_BSY) + ; +} + +uint32_t +spi_transact(bool readback, int slave, uint32_t data, int length, uint32_t flags) +{ + flags &= (SPI_CTRL_TXNEG | SPI_CTRL_RXNEG); + int ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & length) | flags; + + spi_wait(); + + // Tell it which SPI slave device to access + spi_regs->ss = slave & 0xff; + + // Data we will send + spi_regs->txrx0 = data; + + // Run it -- write once and rewrite with GO set + spi_regs->ctrl = ctrl; + spi_regs->ctrl = ctrl | SPI_CTRL_GO_BSY; + + if(readback) { + spi_wait(); + return spi_regs->txrx0; + } + else + return 0; +} diff --git a/usrp2/firmware/lib/spi.h b/usrp2/firmware/lib/spi.h new file mode 100644 index 000000000..0914d6c3b --- /dev/null +++ b/usrp2/firmware/lib/spi.h @@ -0,0 +1,52 @@ +/* -*- c -*- */ +/* + * Copyright 2006,2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_SPI_H +#define INCLUDED_SPI_H + +#include <memory_map.h> +#include <bool.h> + +/*! + * \brief One time call to initialize SPI + */ +void spi_init(void); + +/*! + * \brief Wait for last SPI transaction to complete. + * Unless you need to know it completed, it's not necessary to call this. + */ +void spi_wait(void); + +#define SPI_TXONLY false // readback: no +#define SPI_TXRX true // readback: yes + +/* + * Flags for spi_transact + */ +#define SPIF_PUSH_RISE 0 // push tx data on rising edge of SCLK +#define SPIF_PUSH_FALL SPI_CTRL_TXNEG // push tx data on falling edge of SCLK +#define SPIF_LATCH_RISE 0 // latch rx data on rising edge of SCLK +#define SPIF_LATCH_FALL SPI_CTRL_RXNEG // latch rx data on falling edge of SCLK + + +uint32_t +spi_transact(bool readback, int slave, uint32_t data, int length, uint32_t flags); + + +#endif /* INCLUDED_SPI_H */ diff --git a/usrp2/firmware/lib/stdint.h b/usrp2/firmware/lib/stdint.h new file mode 100644 index 000000000..8086c25d5 --- /dev/null +++ b/usrp2/firmware/lib/stdint.h @@ -0,0 +1,34 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_STDINT_H +#define INCLUDED_STDINT_H + +typedef char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef long long int int64_t; +typedef unsigned long long int uint64_t; + +typedef int intptr_t; +typedef unsigned int uintptr_t; + +#endif /* INCLUDED_STDINT_H */ diff --git a/usrp2/firmware/lib/stdio.h b/usrp2/firmware/lib/stdio.h new file mode 100644 index 000000000..12a7ed0bb --- /dev/null +++ b/usrp2/firmware/lib/stdio.h @@ -0,0 +1,38 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef INCLUDED_STDIO_H +#define INCLUDED_STDIO_H + +// very trimmed down stdio.h See also nonstdio.h + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef EOF +#define EOF (-1) +#endif + +int putchar(int c); +int puts(const char *s); +int printf(const char *format, ...); + +int getchar(void); + +#endif /* INCLUDED_STDIO_H */ diff --git a/usrp2/firmware/lib/u2_init.c b/usrp2/firmware/lib/u2_init.c new file mode 100644 index 000000000..cb4b679c4 --- /dev/null +++ b/usrp2/firmware/lib/u2_init.c @@ -0,0 +1,111 @@ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "u2_init.h" +#include "memory_map.h" +#include "spi.h" +#include "pic.h" +#include "hal_io.h" +#include "lsadc.h" +#include "lsdac.h" +#include "buffer_pool.h" +#include "hal_uart.h" +#include "i2c.h" +#include "bool.h" +#include "mdelay.h" +#include "ad9777.h" +#include "clocks.h" +#include "db.h" + +//#include "nonstdio.h" + +/* + * We ought to arrange for this to be called before main, but for now, + * we require that the user's main call u2_init as the first thing... + */ +bool +u2_init(void) +{ + // Set GPIOs to inputs + hal_gpio_set_ddr(GPIO_TX_BANK, 0x0000, 0xffff); + hal_gpio_set_ddr(GPIO_RX_BANK, 0x0000, 0xffff); + + hal_gpio_write(GPIO_TX_BANK, 0x0000, 0xffff); // init s/w output value to zero + hal_gpio_write(GPIO_RX_BANK, 0x0000, 0xffff); + + hal_io_init(); + + // init spi, so that we can switch over to the high-speed clock + spi_init(); + + // set up the default clocks + clocks_init(); + + // clocks_enable_test_clk(true); + + // Enable ADCs + output_regs->adc_ctrl = ADC_CTRL_ON; + + // Set up AD9777 DAC + ad9777_write_reg(0, R0_1R); + ad9777_write_reg(1, R1_INTERP_4X | R1_REAL_MIX); + ad9777_write_reg(2, 0); + ad9777_write_reg(3, R3_PLL_DIV_1); + ad9777_write_reg(4, R4_PLL_ON | R4_CP_AUTO); + ad9777_write_reg(5, R5_I_FINE_GAIN(0)); + ad9777_write_reg(6, R6_I_COARSE_GAIN(0xf)); + ad9777_write_reg(7, 0); // I dac offset + ad9777_write_reg(8, 0); + ad9777_write_reg(9, R9_Q_FINE_GAIN(0)); + ad9777_write_reg(10, R10_Q_COARSE_GAIN(0xf)); + ad9777_write_reg(11, 0); // Q dac offset + ad9777_write_reg(12, 0); + + // Set up serdes + output_regs->serdes_ctrl = (SERDES_ENABLE | SERDES_RXEN); + + pic_init(); // progammable interrupt controller + bp_init(); // buffer pool + i2c_init(); + lsadc_init(); // low-speed ADCs + lsdac_init(); // low-speed DACs + db_init(); // daughterboard init + + hal_enable_ints(); + + // flash all leds to let us know board is alive + hal_set_leds(0x0, 0x1f); + mdelay(100); + hal_set_leds(0x1f, 0x1f); + mdelay(100); + hal_set_leds(0x0, 0x1f); + mdelay(100); + +#if 0 + // test register readback + int rr, vv; + vv = ad9777_read_reg(0); + printf("ad9777 reg[0] = 0x%x\n", vv); + + for (rr = 0x04; rr <= 0x0d; rr++){ + vv = ad9510_read_reg(rr); + printf("ad9510 reg[0x%x] = 0x%x\n", rr, vv); + } +#endif + + return true; +} diff --git a/usrp2/firmware/lib/u2_init.h b/usrp2/firmware/lib/u2_init.h new file mode 100644 index 000000000..901e61250 --- /dev/null +++ b/usrp2/firmware/lib/u2_init.h @@ -0,0 +1,26 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_U2_INIT_H +#define INCLUDED_U2_INIT_H + +/*! + * one-time init + */ +int u2_init(void); + +#endif /* INCLUDED_U2_INIT_H */ diff --git a/usrp2/firmware/lib/usrp2_bytesex.h b/usrp2/firmware/lib/usrp2_bytesex.h new file mode 100644 index 000000000..2b74f2a0b --- /dev/null +++ b/usrp2/firmware/lib/usrp2_bytesex.h @@ -0,0 +1,66 @@ +/* -*- c++ -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef INCLUDED_USRP2_BYTESEX_H +#define INCLUDED_USRP2_BYTESEX_H + +// The USRP2 speaks big-endian... +// Use the standard include files or provide substitutions for +// htons and friends + +#if defined(HAVE_ARPA_INET_H) +#include <arpa/inet.h> +#elif defined(HAVE_NETINET_IN_H) +#include <netinet/in.h> +#else +#include <stdint.h> + +#ifdef WORDS_BIGENDIAN // nothing to do... + +static inline uint32_t htonl(uint32_t x){ return x; } +static inline uint16_t htons(uint16_t x){ return x; } +static inline uint32_t ntohl(uint32_t x){ return x; } +static inline uint16_t ntohs(uint16_t x){ return x; } + +#else + +#ifdef HAVE_BYTESWAP_H +#include <byteswap.h> +#else + +static inline uint16_t +bswap_16 (uint16_t x) +{ + return ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)); +} + +static inline uint32_t +bswap_32 (uint32_t x) +{ + return ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) \ + | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)); +} +#endif + +static inline uint32_t htonl(uint32_t x){ return bswap_32(x); } +static inline uint16_t htons(uint16_t x){ return bswap_16(x); } +static inline uint32_t ntohl(uint32_t x){ return bswap_32(x); } +static inline uint16_t ntohs(uint16_t x){ return bswap_16(x); } + +#endif +#endif +#endif /* INCLUDED_USRP2_BYTESEX_H */ diff --git a/usrp2/firmware/lib/wb16550.h b/usrp2/firmware/lib/wb16550.h new file mode 100644 index 000000000..7522f4438 --- /dev/null +++ b/usrp2/firmware/lib/wb16550.h @@ -0,0 +1,98 @@ +/* -*- c -*- */ +/* + * Copyright 2007 Free Software Foundation, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +// Wishbone National Semiconductor 16550A compatible UART + +#ifndef INCLUDED_WB16550_H +#define INCLUDED_WB16550_H + +#include <stdint.h> + +typedef struct { + volatile uint8_t data; // 0 r/w: r: rx fifo, w: tx fifo (if DLAB: LSB of divisor) + volatile uint8_t ier; // 1 r/w: Interrupt Enable Register (if DLAB: MSB of divisor) + volatile uint8_t iir_fcr; // 2 r/w: r: Interrupt ID Register, + // w: Fifo Control Register + volatile uint8_t lcr; // 3 r/w: Line Control Register + volatile uint8_t mcr; // 4 w: Modem Control Register + volatile uint8_t lsr; // 5 r: Line Status Register + volatile uint8_t msr; // 6 r: Modem Status Register + +} wb16550_reg_t; + +#define UART_IER_RDI 0x01 // Enable received data interrupt +#define UART_IER_THRI 0x02 // Enable transmitter holding reg empty int. +#define UART_IER_RLSI 0x04 // Enable receiver line status interrupt +#define UART_IER_MSI 0x08 // Enable modem status interrupt + +#define UART_IIR_NO_INT 0x01 // No interrupts pending +#define UART_IIR_ID_MASK 0x06 // Mask for interrupt ID +#define UART_IIR_MSI 0x00 // Modem status interrupt +#define UART_IIR_THRI 0x02 // Tx holding register empty int +#define UART_IIR_RDI 0x04 // Rx data available int +#define UART_IIR_RLSI 0x06 // Receiver line status int + +#define UART_FCR_ENABLE_FIFO 0x01 // ignore, always enabled +#define UART_FCR_CLEAR_RCVR 0x02 // Clear the RCVR FIFO +#define UART_FCR_CLEAR_XMIT 0x04 // Clear the XMIT FIFO +#define UART_FCR_TRIGGER_MASK 0xC0 // Mask for FIFO trigger range +#define UART_FCR_TRIGGER_1 0x00 // Rx fifo trigger level: 1 byte +#define UART_FCR_TRIGGER_4 0x40 // Rx fifo trigger level: 4 bytes +#define UART_FCR_TRIGGER_8 0x80 // Rx fifo trigger level: 8 bytes +#define UART_FCR_TRIGGER_14 0xC0 // Rx fifo trigger level: 14 bytes + +#define UART_LCR_DLAB 0x80 // Divisor latch access bit +#define UART_LCR_SBC 0x40 // Set break control +#define UART_LCR_SPAR 0x20 // Stick parity +#define UART_LCR_EPAR 0x10 // Even parity select +#define UART_LCR_PARITY 0x08 // Parity Enable +#define UART_LCR_STOP 0x04 // Stop bits: 0=1 bit, 1=2 bits +#define UART_LCR_WLEN5 0x00 // Wordlength: 5 bits +#define UART_LCR_WLEN6 0x01 // Wordlength: 6 bits +#define UART_LCR_WLEN7 0x02 // Wordlength: 7 bits +#define UART_LCR_WLEN8 0x03 // Wordlength: 8 bits + +#define UART_MCR_LOOP 0x10 // Enable loopback test mode +#define UART_MCR_OUT2n 0x08 // Out2 complement (loopback mode) +#define UART_MCR_OUT1n 0x04 // Out1 complement (loopback mode) +#define UART_MCR_RTSn 0x02 // RTS complement +#define UART_MCR_DTRn 0x01 // DTR complement + +#define UART_LSR_TEMT 0x40 // Transmitter empty +#define UART_LSR_THRE 0x20 // Transmit-hold-register empty +#define UART_LSR_BI 0x10 // Break interrupt indicator +#define UART_LSR_FE 0x08 // Frame error indicator +#define UART_LSR_PE 0x04 // Parity error indicator +#define UART_LSR_OE 0x02 // Overrun error indicator +#define UART_LSR_DR 0x01 // Receiver data ready +#define UART_LSR_BRK_ERROR_BITS 0x1E // BI, FE, PE, OE bits +#define UART_LSR_ERROR 0x80 // At least 1 PE, FE or BI are in the fifo + +#define UART_MSR_DCD 0x80 // Data Carrier Detect +#define UART_MSR_RI 0x40 // Ring Indicator +#define UART_MSR_DSR 0x20 // Data Set Ready +#define UART_MSR_CTS 0x10 // Clear to Send +#define UART_MSR_DDCD 0x08 // Delta DCD +#define UART_MSR_TERI 0x04 // Trailing edge ring indicator +#define UART_MSR_DDSR 0x02 // Delta DSR +#define UART_MSR_DCTS 0x01 // Delta CTS +#define UART_MSR_ANY_DELTA 0x0F // Any of the delta bits! + + +#endif // INCLUDED_WB16550_H |