diff --git a/gr-usrp2/src/usrp2.i b/gr-usrp2/src/usrp2.i index 3197402..e960372 100644 --- a/gr-usrp2/src/usrp2.i +++ b/gr-usrp2/src/usrp2.i @@ -72,6 +72,7 @@ public: bool set_center_freq(double frequency, usrp2::tune_result *r); bool set_decim(int decimation_factor); bool set_scale_iq(int scale_i, int scale_q); + bool set_shift_iq(int shift_q, int shift_i); int decim(); %rename(_real_adc_rate) adc_rate; bool adc_rate(long *rate); diff --git a/gr-usrp2/src/usrp2_source_base.cc b/gr-usrp2/src/usrp2_source_base.cc index 0ad7008..12fcd88 100644 --- a/gr-usrp2/src/usrp2_source_base.cc +++ b/gr-usrp2/src/usrp2_source_base.cc @@ -76,6 +76,12 @@ usrp2_source_base::set_scale_iq(int scale_i, int scale_q) return d_u2->set_rx_scale_iq(scale_i, scale_q); } +bool +usrp2_source_base::set_shift_iq(int shift_q, int shift_i) +{ + return d_u2->set_rx_shift_iq(shift_q, shift_i); +} + int usrp2_source_base::decim() { diff --git a/gr-usrp2/src/usrp2_source_base.h b/gr-usrp2/src/usrp2_source_base.h index 2e2d51f..d318ad9 100644 --- a/gr-usrp2/src/usrp2_source_base.h +++ b/gr-usrp2/src/usrp2_source_base.h @@ -61,11 +61,24 @@ public: bool set_decim(int decimation_factor); /*! - * \brief Set receive IQ scale factors + * \brief Set receive IQ input scale factors */ bool set_scale_iq(int scale_i, int scale_q); /*! + * \brief Set receive IQ output shift + * shift 0 (default) scales by 1 + * shift 1 scales by 2 + * shift 2 scales by 4 + * shift 3 scales by 8 + * Note that with a shift >0 overflow and clipping can occur. + * This can be resolved by reducing scale_iq to below the default 1024. + * By using a shift of 1 or 2 and reducing scale_iq, + * the full 16 bit output range (-32768 to +32767) can be used + */ + bool set_shift_iq(int shift_q, int shift_i); + + /*! * \brief Get receive decimation rate */ int decim(); diff --git a/usrp2/firmware/apps/app_common_v2.c b/usrp2/firmware/apps/app_common_v2.c index 036d0ba..54bef3f 100644 --- a/usrp2/firmware/apps/app_common_v2.c +++ b/usrp2/firmware/apps/app_common_v2.c @@ -272,6 +272,9 @@ config_rx_v2_cmd(const op_config_rx_v2_t *p, if (p->valid & CFGV_SCALE_IQ){ dsp_rx_regs->scale_iq = p->scale_iq; } + if (p->valid & CFGV_SHIFT_IQ){ + dsp_rx_regs->shift_iq = p->shift_iq; + } // Build reply subpacket diff --git a/usrp2/firmware/include/usrp2_eth_packet.h b/usrp2/firmware/include/usrp2_eth_packet.h index 63d4b3a..b1db5ac 100644 --- a/usrp2/firmware/include/usrp2_eth_packet.h +++ b/usrp2/firmware/include/usrp2_eth_packet.h @@ -283,6 +283,7 @@ typedef struct { uint32_t freq_lo; // low 32-bits of 64-bit fxpt_freq (Q44.20) uint32_t decim; // desired decimation factor (NOT -1) uint32_t scale_iq; // (scale_i << 16) | scale_q [16.0 format] + uint32_t shift_iq; // (shift_i << 4) | shift_q [0, 1, 2 or 3] } _AL4 op_config_rx_v2_t; // bitmask for "valid" field. If the bit is set, there's @@ -292,7 +293,7 @@ typedef struct { #define CFGV_FREQ 0x0002 // target_freq field is valid #define CFGV_INTERP_DECIM 0x0004 // interp or decim is valid #define CFGV_SCALE_IQ 0x0008 // scale_iq is valid - +#define CFGV_SHIFT_IQ 0x0010 // shift_iq is valid /*! * \brief Reply to receiver configuration */ diff --git a/usrp2/firmware/lib/memory_map.h b/usrp2/firmware/lib/memory_map.h index 0d0cf04..14b5f7b 100644 --- a/usrp2/firmware/lib/memory_map.h +++ b/usrp2/firmware/lib/memory_map.h @@ -500,6 +500,32 @@ typedef struct { */ volatile uint32_t gpio_stream_enable; + /*! + * \brief arithmic shift rx samples 0, 1, 2 or 3 bits to the left + * This can increasy dynamic range + * Every shift left corresponds to 6 dB + * Default is shift_iq is {0,0} (and scale_iq={1024,1024]) + * With optimal combination of shift_iq and scale_iq the output samples can use the entire 16 bit range of -32768 to 32767. + * Max output range is shift=2, scale_iq around {578,578} + * shifts >0 will experience overflow and clipping if you don't scale_down scale_iq below the default 1024 + * At the moment the maximum shift is 3, but this may be increased to 15 in the future. + *
+ * shift_iq value: + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | | shiftQ| shiftI| + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * Each 4-bit shiftI field is either 0000 (shift 0), 0001 (shift 1), 0010 (shift 2) or 0011 (shift 3) + * Each 4-bit shiftQ field is either 0000 (shift 0), 0001 (shift 1), 0010 (shift 2) or 0011 (shift 3) + * + * The default value is 0 + *+ */ + volatile uint32_t shift_iq; // {shift_q,shift_i} + } dsp_rx_regs_t; #define dsp_rx_regs ((dsp_rx_regs_t *) DSP_RX_BASE) diff --git a/usrp2/fpga/sdr_lib/dsp_core_rx.v b/usrp2/fpga/sdr_lib/dsp_core_rx.v index af4f0b9..ece6fc5 100644 --- a/usrp2/fpga/sdr_lib/dsp_core_rx.v +++ b/usrp2/fpga/sdr_lib/dsp_core_rx.v @@ -63,6 +63,12 @@ module dsp_core_rx (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), .in(set_data),.out(gpio_ena),.changed()); + wire [3:0] shift_i; + wire [3:0] shift_q; + setting_reg #(.my_addr(`DSP_CORE_RX_BASE+10)) sr_10 + (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr), + .in(set_data),.out({shift_q,shift_i}),.changed()); + // The TVRX connects to what is called adc_b, thus A and B are // swapped throughout the design. // @@ -155,8 +161,11 @@ module dsp_core_rx (.clk(clk),.rst(rst),.bypass(~enable_hb2),.run(run),.cpi(cpi_hb), .stb_in(strobe_hb1),.data_in(q_hb1),.stb_out(),.data_out(q_hb2)); - round #(.bits_in(18),.bits_out(16)) round_iout (.in(i_hb2),.out(i_out)); - round #(.bits_in(18),.bits_out(16)) round_qout (.in(q_hb2),.out(q_out)); + //round #(.bits_in(18),.bits_out(16)) round_iout (.in(i_hb2),.out(i_out)); + //round #(.bits_in(18),.bits_out(16)) round_qout (.in(q_hb2),.out(q_out)); + + clip_and_round_configurable #(.bits_in(18),.bits_out(16)) round_iout (.in(i_hb2),.out(i_out),.clip_bits(shift_i[1:0])); + clip_and_round_configurable #(.bits_in(18),.bits_out(16)) round_qout (.in(q_hb2),.out(q_out),.clip_bits(shift_q[1:0])); // Streaming GPIO // diff --git a/usrp2/fpga/top/u2_rev3/Makefile b/usrp2/fpga/top/u2_rev3/Makefile index 4358d7c..c04ef15 100644 --- a/usrp2/fpga/top/u2_rev3/Makefile +++ b/usrp2/fpga/top/u2_rev3/Makefile @@ -159,6 +159,8 @@ sdr_lib/dsp_core_tx.v \ sdr_lib/hb_dec.v \ sdr_lib/hb_interp.v \ sdr_lib/round.v \ +sdr_lib/clip_and_round.v \ +sdr_lib/clip_and_round_configurable.v \ sdr_lib/round_reg.v \ sdr_lib/rx_control.v \ sdr_lib/rx_dcoffset.v \ diff --git a/usrp2/host/include/usrp2/usrp2.h b/usrp2/host/include/usrp2/usrp2.h index 2d9e2a4..9e45934 100644 --- a/usrp2/host/include/usrp2/usrp2.h +++ b/usrp2/host/include/usrp2/usrp2.h @@ -159,6 +159,13 @@ namespace usrp2 { bool set_rx_scale_iq(int scale_i, int scale_q); /*! + * Set receiver IQ left shift (1 shift left is a scaling by 2) + * Note that overflow and clipping can occur if shift >0 and scale_iq is the default 1024 + * With optimum scaling and shifting the entire 16 bit output dynamic range can be used + */ + bool set_rx_shift_iq(int shift_i, int shift_q); + + /*! * Set received sample format * * domain: complex or real diff --git a/usrp2/host/lib/usrp2.cc b/usrp2/host/lib/usrp2.cc index 801a436..6e7cf70 100644 --- a/usrp2/host/lib/usrp2.cc +++ b/usrp2/host/lib/usrp2.cc @@ -236,6 +236,12 @@ namespace usrp2 { } bool + usrp2::set_rx_shift_iq(int shift_i, int shift_q) + { + return d_impl->set_rx_shift_iq(shift_i, shift_q); + } + + bool usrp2::start_rx_streaming(unsigned int channel, unsigned int items_per_frame) { return d_impl->start_rx_streaming(channel, items_per_frame); diff --git a/usrp2/host/lib/usrp2_impl.cc b/usrp2/host/lib/usrp2_impl.cc index b19c6ec..b7bc5f8 100644 --- a/usrp2/host/lib/usrp2_impl.cc +++ b/usrp2/host/lib/usrp2_impl.cc @@ -46,6 +46,7 @@ #endif static const int DEFAULT_RX_SCALE = 1024; +static const int DEFAULT_RX_SHIFT = 0; namespace usrp2 { @@ -203,6 +204,10 @@ namespace usrp2 { // set workable defaults for scaling if (!set_rx_scale_iq(DEFAULT_RX_SCALE, DEFAULT_RX_SCALE)) std::cerr << "usrp2::ctor set_rx_scale_iq failed\n"; + + // set workable defaults for schift + if (!set_rx_shift_iq(DEFAULT_RX_SHIFT, DEFAULT_RX_SHIFT)) + std::cerr << "usrp2::ctor set_rx_shift_iq failed\n"; } usrp2::impl::~impl() @@ -614,6 +619,24 @@ namespace usrp2 { } bool + usrp2::impl::set_rx_shift_iq(int shift_i, int shift_q) + { + op_config_rx_v2_cmd cmd; + op_config_rx_reply_v2_t reply; + + init_config_rx_v2_cmd(&cmd); + cmd.op.valid = htons(CFGV_SHIFT_IQ); + cmd.op.shift_iq = htonl(((shift_i & 0x3) << 4) | (shift_q & 0x3)); + + pending_reply p(cmd.op.rid, &reply, sizeof(reply)); + if (!transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT)) + return false; + + bool success = (ntohx(reply.ok) == 1); + return success; + } + + bool usrp2::impl::start_rx_streaming(unsigned int channel, unsigned int items_per_frame) { if (channel > MAX_CHAN) { @@ -814,6 +837,7 @@ namespace usrp2 { success = transmit_cmd_and_wait(&cmd, sizeof(cmd), &p, DEF_CMD_TIMEOUT); success = success && (ntohx(reply.ok) == 1); d_channel_rings[channel].reset(); + d_rx_seqno=-1; //fprintf(stderr, "usrp2::stop_rx_streaming: success = %d\n", success); return success; } diff --git a/usrp2/host/lib/usrp2_impl.h b/usrp2/host/lib/usrp2_impl.h index d78a00d..782dbcf 100644 --- a/usrp2/host/lib/usrp2_impl.h +++ b/usrp2/host/lib/usrp2_impl.h @@ -137,6 +137,7 @@ namespace usrp2 { bool set_rx_decim(int decimation_factor); int rx_decim() { return d_rx_decim; } bool set_rx_scale_iq(int scale_i, int scale_q); + bool set_rx_shift_iq(int shift_i, int shift_q); bool set_gpio_ddr(int bank, uint16_t value, uint16_t mask); bool set_gpio_sels(int bank, std::string src); bool enable_gpio_streaming(int bank, int enable); diff -urN a/usrp2/fpga/sdr_lib/clip_and_round_configurable.v b/usrp2/fpga/sdr_lib/clip_and_round_configurable.v --- a/usrp2/fpga/sdr_lib/clip_and_round_configurable.v 1970-01-01 01:00:00.000000000 +0100 +++ b/usrp2/fpga/sdr_lib/clip_and_round_configurable.v 2010-07-08 20:03:59.000000000 +0200 @@ -0,0 +1,116 @@ +// -*- verilog -*- +// +// USRP - Universal Software Radio Peripheral +// +// Copyright (C) 2010 Matt Ettus, Martin Dudok van Heel +// +// 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 2 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, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Boston, MA 02110-1301 USA +// + +// Clipping "macro", keeps the bottom bits, clips out the requested amount of bits (left shift) and rounds +// +// clip_bits is a 3 bit input, not a parameter, +// so this can be dynamically configured. +// clip_bits can be set to 0, 1, 2, or 3 +// Note: Due to the use of arithmic shift, only works with tools which understand verilog 2001 +// This could be resolved by coding out all 4 shift cases + +module clip_and_round_configurable + #(parameter bits_in=0, + parameter bits_out=0) + (input [bits_in-1:0] in, + output [bits_out-1:0] out, + input [1:0] clip_bits); + + wire [bits_out-1:0] clip3; + wire [bits_out-1:0] clip2; + wire [bits_out-1:0] clip1; + wire [bits_out-1:0] clip0; + + clip #(.bits_in(bits_in+1),.bits_out(bits_out)) round_3 (.in({in,1'b0}),.out(clip3)); + clip #(.bits_in(bits_in),.bits_out(bits_out)) round_2 (.in({in}),.out(clip2)); + clip_and_round #(.bits_in(bits_in),.bits_out(bits_out),.clip_bits(1)) round_1 (.in(in),.out(clip1)); + round #(.bits_in(bits_in),.bits_out(bits_out)) round_0 (.in(in),.out(clip0)); + + assign out= + (clip_bits==3'd3)? clip3: + (clip_bits==3'd2)? clip2: + (clip_bits==3'd1)? clip1: + clip0; //default case, no clipping + +/* + parameter max_clip_bits=3;//must be 3, used as a constant here + + wire [bits_out-1:0] rounded; + + // rounded0 = in[bits_in-1:bits_in-bits_out] + (in[bits_in-1] & |in[bits_in-bits_out-1:0]); + // rounded1 = in[bits_in-1-1:bits_in-1-bits_out] + (in[bits_in-1-1] & |in[bits_in-1-bits_out-1:0]); + // rounded2 = in[bits_in-1-2:bits_in-2-bits_out] + (in[bits_in-2-1] & |in[bits_in-2-bits_out-1:0]); + // rounded3 = in[bits_in-1-3:bits_in-3-bits_out] + (in[bits_in-3-1] & |in[bits_in-3-bits_out-1:0]); + + + wire [max_clip_bits-1:0] mask = (clip_bits==2'd0)?3'b000: + (clip_bits==2'd1)?3'b100: + (clip_bits==2'd2)?3'b110: + 3'b111; + + + //Note, uses arithmic shift, only available in verilog 2001 + wire [bits_in-1:0] tmp1 = $signed(in) >>> clip_bits; + wire [bits_out-1:0] tmp2 = tmp1[bits_out-1:0]; + wire [bits_in-1:0] tmp3 = in<