aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/usrp_e/clock_ctrl.cpp
blob: 5f72694121948da03b57b29dc75da019008f01b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
//
// Copyright 2010 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#include "clock_ctrl.hpp"
#include "ad9522_regs.hpp"
#include <boost/cstdint.hpp>
#include "usrp_e_regs.hpp" //spi slave constants
#include <boost/assign/list_of.hpp>
#include <boost/foreach.hpp>
#include <utility>
#include <iostream>

using namespace uhd;

/***********************************************************************
 * Clock Control Implementation
 **********************************************************************/
class clock_ctrl_impl : public clock_ctrl{
public:
    //structors
    clock_ctrl_impl(usrp_e_iface::sptr iface);
    ~clock_ctrl_impl(void);

    void enable_rx_dboard_clock(bool enb);
    void enable_tx_dboard_clock(bool enb);

    double get_fpga_clock_rate(void){return 64e6;}
    double get_rx_dboard_clock_rate(void){return get_fpga_clock_rate();}
    double get_tx_dboard_clock_rate(void){return get_fpga_clock_rate();}

private:
    usrp_e_iface::sptr _iface;
    ad9522_regs_t _ad9522_regs;

    void latch_regs(void){
        _ad9522_regs.io_update = 1;
        this->send_reg(0x232);
    }
    void send_reg(boost::uint16_t addr);
};

/***********************************************************************
 * Clock Control Methods
 **********************************************************************/
clock_ctrl_impl::clock_ctrl_impl(usrp_e_iface::sptr iface){
    _iface = iface;

    //init the clock gen registers
    //Note: out0 should already be clocking the FPGA or this isnt going to work
    _ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO;
    _ad9522_regs.status_pin_control = 0x1; //n divider
    _ad9522_regs.ld_pin_control = 0x32; //show ref2
    _ad9522_regs.refmon_pin_control = 0x12; //show ref2

    _ad9522_regs.enable_ref2 = 1;
    _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF2;

    _ad9522_regs.r_counter_lsb = 1;
    _ad9522_regs.r_counter_msb = 0;
    _ad9522_regs.a_counter = 0;
    _ad9522_regs.b_counter_lsb = 20;
    _ad9522_regs.b_counter_msb = 0;
    _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV8_9;

    _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL;
    _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_3_0MA;

    _ad9522_regs.vco_calibration_now = 1; //calibrate it!
    _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5;
    _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_VCO;

    //setup fpga master clock
    _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS;
    _ad9522_regs.divider0_low_cycles = 2; //3 low
    _ad9522_regs.divider0_high_cycles = 1; //2 high

    //setup codec clock
    _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS;
    _ad9522_regs.divider1_low_cycles = 2; //3 low
    _ad9522_regs.divider1_high_cycles = 1; //2 high

    //setup test clock (same divider as codec clock)
    _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS;
    _ad9522_regs.out4_cmos_configuration = (true)?
        ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON :
        ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF;

    //setup a list of register ranges to write
    typedef std::pair<boost::uint16_t, boost::uint16_t> range_t;
    static const std::vector<range_t> ranges = boost::assign::list_of
        (range_t(0x000, 0x000)) (range_t(0x010, 0x01F))
        (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B))
        (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230))
    ;

    //write initial register values and latch/update
    BOOST_FOREACH(const range_t &range, ranges){
        for(boost::uint16_t addr = range.first; addr <= range.second; addr++){
            this->send_reg(addr);
        }
    }
    this->latch_regs();
    //test read:
    //boost::uint32_t reg = _ad9522_regs.get_read_reg(0x01b);
    //boost::uint32_t result = _iface->transact_spi(
    //    UE_SPI_SS_AD9522,
    //    spi_config_t::EDGE_RISE,
    //    reg, 24, true /*no*/
    //);
    //std::cout << "result " << std::hex << result << std::endl;
    this->enable_rx_dboard_clock(false);
    this->enable_tx_dboard_clock(false);
}

clock_ctrl_impl::~clock_ctrl_impl(void){
    this->enable_rx_dboard_clock(false);
    this->enable_tx_dboard_clock(false);
}

void clock_ctrl_impl::enable_rx_dboard_clock(bool enb){
    _ad9522_regs.out9_format = ad9522_regs_t::OUT9_FORMAT_CMOS;
    _ad9522_regs.out9_cmos_configuration = (enb)?
        ad9522_regs_t::OUT9_CMOS_CONFIGURATION_B_ON :
        ad9522_regs_t::OUT9_CMOS_CONFIGURATION_OFF;
    this->send_reg(0x0F9);

    _ad9522_regs.divider3_low_cycles = 2; //3 low
    _ad9522_regs.divider3_high_cycles = 1; //2 high
    this->send_reg(0x199);
    this->latch_regs();
}

void clock_ctrl_impl::enable_tx_dboard_clock(bool enb){
    _ad9522_regs.out6_format = ad9522_regs_t::OUT6_FORMAT_CMOS;
    _ad9522_regs.out6_cmos_configuration = (enb)?
        ad9522_regs_t::OUT6_CMOS_CONFIGURATION_B_ON :
        ad9522_regs_t::OUT6_CMOS_CONFIGURATION_OFF;
    this->send_reg(0x0F6);

    _ad9522_regs.divider2_low_cycles = 2; //3 low
    _ad9522_regs.divider2_high_cycles = 1; //2 high
    this->send_reg(0x196);
    this->latch_regs();
}

void clock_ctrl_impl::send_reg(boost::uint16_t addr){
    boost::uint32_t reg = _ad9522_regs.get_write_reg(addr);
    //std::cout << "clock control write reg: " << std::hex << reg << std::endl;
    _iface->transact_spi(
        UE_SPI_SS_AD9522,
        spi_config_t::EDGE_RISE,
        reg, 24, false /*no rb*/
    );
}

/***********************************************************************
 * Clock Control Make
 **********************************************************************/
clock_ctrl::sptr clock_ctrl::make(usrp_e_iface::sptr iface){
    return sptr(new clock_ctrl_impl(iface));
}