#!/usr/bin/env python
#
# Copyright 2010 Ettus Research LLC
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

########################################################################
# Template for raw text data describing registers
# name addr[bit range inclusive] default optional enums
########################################################################
REGS_TMPL="""\
sdo_active                  0x000[7]                 0           sdio, sdo_sdio
lsb_first_addr_incr         0x000[6]                 0           msb, lsb
soft_reset                  0x000[5]                 0
mirror                      0x000[3:0]               0
readback_active_registers   0x004[0]                 0           buffer, active
pfd_polarity                0x010[7]                 0           pos, neg
cp_current                  0x010[6:4]               7           0_6ma, 1_2ma, 1_8ma, 2_4ma, 3_0ma, 3_6ma, 4_2ma, 4_8ma
cp_mode                     0x010[3:2]               3           high_imp, force_source, force_sink, normal
pll_power_down              0x010[1:0]               1           normal=0, async=1, sync=3
r_counter_lsb               0x011[7:0]               1
r_counter_msb               0x012[5:0]               0
~r_counter                  r_counter_lsb, r_counter_msb
a_counter                   0x013[5:0]               0
b_counter_lsb               0x014[7:0]               3
b_counter_msb               0x015[4:0]               0
~b_counter                  b_counter_lsb, b_counter_msb
set_cp_pin_to_vcp_2         0x016[7]                 0           normal, vcp_2
reset_r_counter             0x016[6]                 0
reset_a_and_b_counters      0x016[5]                 0
reset_all_counters          0x016[4]                 0
b_counter_bypass            0x016[3]                 0           normal, div1
prescaler_p                 0x016[2:0]               6           div1, div2, div2_3, div4_5, div8_9, div16_17, div32_33, div3
status_pin_control          0x017[7:2]               0
antibacklash_pulse_width    0x017[1:0]               0           2_9ns, 1_3ns, 6_0ns
enb_cmos_ref_input_dc_off   0x018[7]                 0
lock_detect_counter         0x018[6:5]               0           5cyc, 16cyc, 64cyc, 255cyc
digital_lock_detect_window  0x018[4]                 0           high_range, low_range
disable_digital_lock_detect 0x018[3]                 0           normal, disabled
vco_calibration_divider     0x018[2:1]               3           div2, div4, div8, div16
vco_calibration_now         0x018[0]                 0
r_a_b_counters_sync_pin_rst 0x019[7:6]               0           nothing, async, sync
r_path_delay                0x019[5:3]               0
n_path_delay                0x019[2:0]               0
enable_status_pin_divider   0x01A[7]                 0
ref_freq_monitor_threshold  0x01A[6]                 0           1_02mhz, 6khz
ld_pin_control              0x01A[5:0]               0
enable_vco_freq_monitor     0x01B[7]                 0
enable_ref2_freq_monitor    0x01B[6]                 0
enable_ref1_freq_monitor    0x01B[5]                 0
refmon_pin_control          0x01B[4:0]               0
disable_switchover_deglitch 0x01C[7]                 0
select_ref                  0x01C[6]                 0           ref1, ref2
use_ref_sel_pin             0x01C[5]                 0           register, ref_sel
enb_auto_ref_switchover     0x01C[4]                 0           manual, auto
stay_on_ref2                0x01C[3]                 0           return_ref1, stay_ref2
enable_ref2                 0x01C[2]                 0
enable_ref1                 0x01C[1]                 0
enable_differential_ref     0x01C[0]                 0
enb_stat_eeprom_at_stat_pin 0x01D[7]                 1
enable_xtal_osc             0x01D[6]                 0
enable_clock_doubler        0x01D[5]                 0
disable_pll_status_reg      0x01D[4]                 0
enable_ld_pin_comparator    0x01D[3]                 0
enable_external_holdover    0x01D[1]                 0
enable_holdover             0x01D[0]                 0
external_zero_delay_fcds    0x01E[4:3]               0
enable_external_zero_delay  0x01E[2]                 0
enable_zero_delay           0x01E[1]                 0
########################################################################
#for $i in range(12)
#set $addr = ($i + 0x0F0)
out$(i)_format              $(addr)[7]             0             lvds, cmos
out$(i)_cmos_configuration  $(addr)[6:5]           3             off, a_on, b_on, ab_on
out$(i)_polarity            $(addr)[4:3]           0             lvds_a_non_b_inv=0, lvds_a_inv_b_non=1, cmos_ab_non=0, cmos_ab_inv=1, cmos_a_non_b_inv=2, cmos_a_inv_b_non=3
out$(i)_lvds_diff_voltage   $(addr)[2:1]           1             1_75ma, 3_5ma, 5_25ma, 7_0ma
out$(i)_lvds_power_down     $(addr)[0]             0
#end for
########################################################################
#for $i in reversed(range(8))
csdld_en_out_$i             0x0FC[$i]                0           ignore, async
#end for
########################################################################
#for $i in reversed(range(4))
csdld_en_out_$(8 + $i)      0x0FD[$i]                0           ignore, async
#end for
########################################################################
#set $default_val = 0x7
#for $i in range(4)
#set $addr0 = hex($i*3 + 0x190)
#set $addr1 = hex($i*3 + 0x191)
#set $addr2 = hex($i*3 + 0x192)
divider$(i)_low_cycles      $(addr0)[7:4]         $default_val
divider$(i)_high_cycles     $(addr0)[3:0]         $default_val
divider$(i)_bypass          $(addr1)[7]           0
divider$(i)_ignore_sync     $(addr1)[6]           0
divider$(i)_force_high      $(addr1)[5]           0
divider$(i)_start_high      $(addr1)[4]           0
divider$(i)_phase_offset    $(addr1)[3:0]         0
channel$(i)_power_down      $(addr2)[2]           0
disable_divider$(i)_ddc     $(addr2)[0]           0
#set $default_val /= 2
#end for
########################################################################
vco_divider                  0x1E0[2:0]              2             div2, div3, div4, div5, div6, static, div1
power_down_clock_input_sel   0x1E1[4]                0
power_down_vco_clock_ifc     0x1E1[3]                0
power_down_vco_and_clock     0x1E1[2]                0
select_vco_or_clock          0x1E1[1]                0             external, vco
bypass_vco_divider           0x1E1[0]                0
disable_power_on_sync        0x230[3]                0
power_down_sync              0x230[2]                0
power_down_dist_ref          0x230[1]                0
soft_sync                    0x230[0]                0
io_update                    0x232[0]                0
soft_eeprom                  0xB02[1]                0
enable_eeprom_write          0xB02[0]                0
reg2eeprom                   0xB03[0]                0
"""

########################################################################
# Template for methods in the body of the struct
########################################################################
BODY_TMPL="""\
boost::uint32_t get_reg(boost::uint16_t addr){
    boost::uint32_t reg = 0;
    switch(addr){
    #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs)))
    case $addr:
        #for $reg in filter(lambda r: r.get_addr() == addr, $regs)
        reg |= (boost::uint8_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift();
        #end for
        break;
    #end for
    }
    if (addr == 0){ //mirror 4 bits in register 0
        reg |= ((reg >> 7) & 0x1) << 0;
        reg |= ((reg >> 6) & 0x1) << 1;
        reg |= ((reg >> 5) & 0x1) << 2;
        reg |= ((reg >> 4) & 0x1) << 3;
    }
    return reg;
}

void set_reg(boost::uint16_t addr, boost::uint32_t reg){
    switch(addr){
    #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs)))
    case $addr:
        #for $reg in filter(lambda r: r.get_addr() == addr, $regs)
        $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask());
        #end for
        break;
    #end for
    }
}

boost::uint32_t get_write_reg(boost::uint16_t addr){
    return (boost::uint32_t(addr) << 8) | get_reg(addr);
}

boost::uint32_t get_read_reg(boost::uint16_t addr){
    return (boost::uint32_t(addr) << 8) | (1 << 23);
}

"""

if __name__ == '__main__':
    import common; common.generate(
        name='ad9522_regs',
        regs_tmpl=REGS_TMPL,
        body_tmpl=BODY_TMPL,
        file=__file__,
    )