# Copyright 2026 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#

"""Register map for ADMV1420."""

########################################################################
# Template for raw text data describing registers
# name addr[bit range inclusive] default optional enums
########################################################################
REGS_TMPL = """\
########################################################################
## ADMV1420 Register Map
## As the MS C++ compiler does not allow for more than 128 nested blocks
## but we have more than 128 bitfields, some of them are commented out.
## If needed, they need to be uncommented again but check if building
## still works then.
########################################################################
SOFTRESET                       0x000[0]            0x00
LSB_FIRST                       0x000[1]            0x00
ADDR_ASCN                       0x000[2]            0x00
SDOACTIVE                       0x000[3]            0x00
SDOACTIVE_R                     0x000[4]            0x00
ADDR_ASCN_R                     0x000[5]            0x00
LSB_FIRST_R                     0x000[6]            0x00
SOFTRESET_R                     0x000[7]            0x00
## PRODUCT_ID_L                    0x004[0:7]          ro
## PRODUCT_ID_H                    0x005[0:7]          ro
SCRATCH_PAD                     0x00A[0:7]          0x00
## SPI_REV                         0x00B[0]            ro
## VENDOR_ID_L                     0x00C[0:7]          ro
## VENDOR_ID_H                     0x00D[0:7]          ro
SDO_LEVEL                       0x013[0]            0x01
ADC_TEMP_SENSOR_READBACK        0x040[0:7]          ro
ADC_DETECTOR_READBACK           0x041[0:7]          ro
ADC_CLK_EN                      0x043[0]            0x00
MANUAL_ADC_MUX_SEL              0x051[0:4]          0x00
MANUAL_ADC_STCONV               0x051[5]            0x00
MANUAL_ADC_EN                   0x051[6]            0x01
MANUAL_ADC_MODE_EN              0x051[7]            0x00
ADC_CLK_FREQ                    0x052[0:1]          0x00
ADC_RESET                       0x052[3]            0x00
DIGITAL_FILTER_RESET            0x052[4]            0x00
DETECTOR_SAMPLING_TRIG          0x052[5]            0x00
## ADC_OUT                         0x053[0:7]          ro
## ADC_EOC                         0x054[0]            ro
ADC_FILTER_TAPS                 0x055[0:3]          0x00
HARD_RESET_DIGITAL_FILTER       0x05F[0]            0x00
RX_DSA_OVERLOAD                 0x0A0[0]            ro
RX_LNA_OVERLOAD                 0x0A0[1]            ro
RX_LNA_OVERLOAD_LATCHED         0x0A1[0]            ro
RF_DSA1_PD                      0x100[0]            0x00
RF_LNA_B0_PD                    0x100[1]            0x01
RF_LNA_B1_PD                    0x100[2]            0x01
RF_LNA_B2_PD                    0x100[3]            0x01
RF_DSA_OVERLOAD_PD              0x102[0]            0x00
RF_MIX_TEMPCOMP_PD              0x102[3]            0x00
RF_DS_TEMPCOMP_PD               0x102[4]            0x00
BBI_PD                          0x105[0]            0x01
BBQ_PD                          0x105[1]            0x01
OVERLOAD_PROTECTION_MODE        0x108[0:1]          0x00
ADC_CNTRL                       0x159[0:2]          0x04
RF_MIX_TEMPCOMP_RES             0x176[0:2]          0x04
RF_MIX_TEMPCOMP_SLOPE           0x176[4:6]          0x04
TEMP_SENSOR_SEL                 0x177[0]            0x00
DETECTOR_RES_CTRL               0x178[0:6]          0x07
MIXER_GATE_SELECT               0x17D[0]            0x00
MIXER_GATE_DAC_MANUAL_CTRL      0x17E[0:6]          0x04
DELTA_MIXER_GATE_CTRL           0x180[0:2]          0x02
IF_BB_SWITCH_CTRL               0x184[0]            0x01
BB_VOCMSEL                      0x18A[0]            0x01
BBI_DCOFFSETP                   0x18C[0:4]          0x00
BBI_DCOFFSETN                   0x18D[0:4]          0x00
BBQ_DCOFFSETP                   0x18E[0:4]          0x00
BBQ_DCOFFSETN                   0x18F[0:4]          0x00
RF_DS_TEMPCOMP_SLOPE            0x192[0:2]          0x04
RF_DS_TEMPCOMP_RES              0x193[0:2]          0x04
FILTER_ADDRESS_STEP             0x200[0:7]          0x01
FILTER_TABLE_EN                 0x202[0]            0x01
FILTER_TABLE_MODE               0x203[0:1]          0x00
FILTER_ADDRESS_RESET            0x204[0]            0x01
FILTER_ADDRESS_STOP             0x206[0:4]          0x1F
FILTER_TABLE_SELECT             0x207[0]            0x00
FILTER_LOAD_EN                  0x208[0]            0x00
FILTER_ADDRESS_POINTER          0x209[0:4]          0x00
## FILTER_STATE_READBACK_L1        0x220[0:7]          ro
## FILTER_STATE_READBACK_L2        0x221[0:7]          ro
## FILTER_STATE_READBACK_L3        0x222[0:7]          ro
## FILTER_STATE_READBACK_L4        0x223[0:7]          ro
## FILTER_STATE_READBACK_L5        0x224[0:7]          ro
## FILTER_STATE_READBACK_L6        0x225[0:7]          ro
## FILTER_STATE_READBACK_L7        0x226[0:7]          ro
## FILTER_STATE_READBACK_L8        0x227[0:7]          ro
## FILTER_STATE_READBACK_L9        0x228[0:7]          ro
## FILTER_ADDRESS_READBACK         0x240[0:7]          ro
## DSA4_IF_BAND0_OFFSET            0x270[0:3]          ro
## DSA4_IF_BAND1_OFFSET            0x270[4:7]          ro
## DSA3_RF_BAND1_OFFSET            0x271[0:3]          ro
## DSA3_RF_BAND2_OFFSET            0x271[4:7]          ro
## DSA3_RF_BAND3_OFFSET            0x272[0:3]          ro
GAIN_LOAD_EN                    0x281[0]            0x00
GAIN_ADDRESS_POINTER            0x282[0:6]          0x00
GAIN_ADDRESS_STEP               0x283[0:7]          0x01
GAIN_ADDRESS_OFFSET             0x284[0:7]          0x00
GAIN_TABLE_EN                   0x285[0]            0x01
GAIN_TABLE_MODE                 0x286[0:1]          0x00
GAIN_ADDRESS_RESET              0x287[0]            0x01
GAIN_ADDRESS_STOP               0x289[0:6]          0x42
GAIN_TABLE_BYPASS_EN            0x28A[0]            0x00
DSA1_BYPASS_VALUE               0x28B[0:3]          0x00
DSA2_BYPASS_VALUE               0x28B[4]            0x00
DSA3_BYPASS_VALUE               0x28C[0:3]          0x00
DSA4_BYPASS_VALUE               0x28C[4:7]          0x00
DSA5_BYPASS_VALUE               0x28D[0:3]          0x00
## GAIN_ADDRESS_READBACK           0x298[0:7]          ro
## GAIN_ADDRESS_PRE_GAIN_OFFSET    0x29B[0:7]          ro
RF_LPF_VALUE                    0x2A0[0:3]          0x00
RF_LPF_SELECT                   0x2A0[4]            0x00
HPF_VALUE                       0x2A1[0:5]          0x00
HPF_SELECT                      0x2A1[7]            0x00
IF_LPF_VALUE                    0x2A2[0:3]          0x00
IF_LPF_SELECT                   0x2A2[4]            0x00
RF_LPF_READBACK                 0x2A3[0:3]          ro
HPF_READBACK                    0x2A4[0:5]          ro
IF_LPF_READBACK                 0x2A5[0:3]          ro
GAIN_PINS_READBACK              0x2B0[0:5]          ro
RF_DSA1_GAIN                    0x600[0:3]          0x00                  
RF_DSA2_GAIN                    0x600[4]            0x00
RF_DSA3_GAIN                    0x601[0:3]          0x00
IF_DSA4_GAIN                    0x601[4:7]          0x00
IF_DSA5_GAIN                    0x602[0:3]          0x00
GPO_G                           0x603[0:6]          0x00
GPO_F_OE_L                      0x780[0:7]          0x00
GPO_F_OE_H                      0x781[0]            0x00
GPO_G_OE                        0x781[1:7]          0x00
DIRECT_LO_X3_FILTER             0x800[0:3]          0x00
LO_SIDEBAND                     0x800[4]            0x01
IF_BAND_SELECT                  0x800[5]            0x01
RF_BAND_SELECT                  0x800[6:7]          0x03
DIRECT_RF_LPF                   0x801[0:3]          0x00
DIRECT_HPF                      0x801[4:7]          0x00
GPO_F_L                         0x802[0:7]          0x00
GPO_F_H                         0x803[0]            0x00
DIRECT_LO_PHASE_I               0x803[1:5]          0x00
DIRECT_LO_PHASE_Q               0x804[0:4]          0x00
DIRECT_IF_LPF                   0x804[5:6]          0x00
DIRECT_DSA1_OFFSET              0x805[0:3]          0x00
DIRECT_DSA2_OFFSET              0x805[4:5]          0x00
DIRECT_DSA3_OFFSET              0x806[0:3]          0x00
DIRECT_DSA4_OFFSET              0x806[4:7]          0x00
DIRECT_DSA5_OFFSET              0x807[0:3]          0x00
DSAI_0P1DB                      0x808[0:3]          0x00
DSAQ_0P1DB                      0x808[4:7]          0x00
DSA3_CAL_EN                     0x860[0]            0x01
DSA4_CAL_EN                     0x860[1]            0x01
"""

########################################################################
# Template for python methods in the body of the struct
########################################################################

PY_BODY_TMPL = """\
def get_reg(self, addr):
    "Return the value of register at address addr"
    reg = 0
    # First the regular registers
    % for addr in sorted(set([r.get_addr() for r in regs if not r.is_array])):
    if addr == ${addr}:
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
            % if reg.get_enums():
        reg |= (self.${reg.get_name()}.value & ${reg.get_mask()}) << ${reg.get_shift()}
            % else:
        reg |= (self.${reg.get_name()} & ${reg.get_mask()}) << ${reg.get_shift()}
            % endif
        % endfor
    % endfor
    return reg

<%
    ro_regs = set()
    for reg in filter(lambda reg: reg.is_readonly(), regs):
        for index in range(reg.get_array_len() if reg.is_array else 1):
            ro_regs.add(reg.get_addr() + index * reg.get_addr_step_size())

    rw_regs = set()
    for reg in filter(lambda reg: not reg.is_readonly(), regs):
        for index in range(reg.get_array_len() if reg.is_array else 1):
            rw_regs.add(reg.get_addr() + index * reg.get_addr_step_size())
%>
def get_all_addrs(self, include_ro = False):
    addrs = []
    % if len(ro_regs) > 0:
    if (include_ro):
    % for addr in sorted(ro_regs):
        addrs.append(${addr})
    % endfor
    % endif
    % for addr in sorted(rw_regs):
    addrs.append(${addr})
    % endfor
    return addrs

def get_addr(self, reg_name):
    "returns the address of a register with the given name"
    return {
        % for reg in regs:
        '${reg.get_name()}': ${reg.get_addr()},
        % endfor
    }[reg_name]

def set_reg(self, addr, reg):
    % for addr in sorted(set([r.get_addr() for r in regs if not r.is_array])):
    <% if_state = 'if' if loop.index == 0 else 'elif' %>
    ${if_state} addr == ${addr}:
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
        % if reg.get_enums():
        self.${reg.get_name()} = self.${reg.get_name()}_t((reg >> ${reg.get_shift()}) & ${reg.get_mask()})
        % else:
        self.${reg.get_name()} = (reg >> ${reg.get_shift()}) & ${reg.get_mask()}
        % endif
        % endfor
    % endfor

"""


########################################################################
# Template for C++ methods in the body of the struct
########################################################################
BODY_TMPL = """\
enum class hbx_reg_field_t {
    % for addr in sorted(set(map(lambda r: r.get_addr(), regs))):
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
    ${reg.get_name()},
        % endfor
    % endfor
};

hbx_reg_field_t get_field_type(const std::string& field_name) {
    % for addr in sorted(set(map(lambda r: r.get_addr(), regs))):
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
    if (field_name == "${reg.get_name()}") {
        return hbx_reg_field_t::${reg.get_name()};
    } else
        % endfor
    % endfor
    {
        UHD_ASSERT_THROW(false);
    }
}

uint32_t get_array_size(hbx_reg_field_t field) {
    switch(field) {
    % for addr in sorted(set([r.get_addr() for r in regs if r.is_array])):
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
    case hbx_reg_field_t::${reg.get_name()}:
        return uint32_t(${reg.get_name()}.size());
        % endfor
    % endfor
    default:
        UHD_ASSERT_THROW(false);
        return 0;
    }
}

uint32_t get_field(hbx_reg_field_t field) {
    switch(field) {
    % for addr in sorted(set([r.get_addr() for r in regs if not r.is_array])):
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
    case hbx_reg_field_t::${reg.get_name()}:
        return uint32_t(${reg.get_name()});
        % endfor
    % endfor
    default:
        UHD_ASSERT_THROW(false);
        return 0;
    }
}

uint32_t get_field(hbx_reg_field_t field, const size_t idx) {
    % if not any(map(lambda r: r.is_array, regs)):
        (void)field;
        (void)idx;
    % endif
    switch(field) {
    % for addr in sorted(set([r.get_addr() for r in regs if r.is_array])):
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
    case hbx_reg_field_t::${reg.get_name()}:
        return uint32_t(${reg.get_name()}[idx]);
        % endfor
    % endfor
    default:
        UHD_ASSERT_THROW(false);
        return 0;
    }
}

void set_field(hbx_reg_field_t field, uint32_t value) {
    switch(field) {
    % for addr in sorted(set([r.get_addr() for r in regs if not r.is_array])):
        % for reg in filter(lambda r: r.get_addr() == addr and not r.is_readonly(), regs):
    case hbx_reg_field_t::${reg.get_name()}:
        ${reg.get_name()} = static_cast<${reg.get_type()}>(value);
        break;
        % endfor
    % endfor
    default:
        UHD_ASSERT_THROW(false);
    }
}

void set_field(hbx_reg_field_t field, uint32_t value, const size_t idx)
{
    % if not any(map(lambda r: r.is_array, regs)):
        (void)field;
        (void)value;
        (void)idx;
    % endif
    switch(field) {
    % for addr in sorted(set([r.get_addr() for r in regs if r.is_array])):
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
    case hbx_reg_field_t::${reg.get_name()}:
        ${reg.get_name()}[idx] = static_cast<${reg.get_type()}>(value);
        break;
        % endfor
    % endfor
    default:
        UHD_ASSERT_THROW(false);
    }
}

uint32_t get_reg(uint16_t addr)
{
    uint32_t reg = 0;
    // First the regular registers
    switch(addr) {
    % for addr in sorted(set([r.get_addr() for r in regs if not r.is_array])):
    case ${addr}:
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
        reg |= (uint32_t(${reg.get_name()}) & ${reg.get_mask()}) << ${reg.get_shift()};
        % endfor
        break;
    % endfor
    default:
        break;
    }
    return reg;
}

void set_reg(uint16_t addr, uint32_t val)
{
    switch(addr) {
    % for addr in sorted(set([r.get_addr() for r in regs if not r.is_array])):
    case ${addr}:
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
            ${reg.get_name()} = static_cast<${reg.get_type()}>((val & (${reg.get_mask()} << ${reg.get_shift()})) >> ${reg.get_shift()});
        % endfor
        break;
    % endfor
    default:
        break;
    }
}

<%
    ro_regs = set()
    for reg in filter(lambda reg: reg.is_readonly(), regs):
        for index in range(reg.get_array_len() if reg.is_array else 1):
            ro_regs.add(reg.get_addr() + index * reg.get_addr_step_size())

    rw_regs = set()
    for reg in filter(lambda reg: not reg.is_readonly(), regs):
        for index in range(reg.get_array_len() if reg.is_array else 1):
            rw_regs.add(reg.get_addr() + index * reg.get_addr_step_size())
%>

template<typename T> std::set<T> get_all_addrs(bool include_ro = false)
{
    std::set<T> addrs;
    if (include_ro) {
    % for reg in sorted(ro_regs):
        addrs.insert(${reg});
    % endfor
    }
    % for reg in sorted(rw_regs):
    addrs.insert(${reg});
    % endfor
    return addrs;
}

uint16_t get_addr(hbx_reg_field_t field) {
    switch(field) {
    % for addr in sorted(set([r.get_addr() for r in regs if r.is_array])):
        % for reg in filter(lambda r: r.get_addr() == addr, regs):
    case hbx_reg_field_t::${reg.get_name()}:
        return ${addr};
        % endfor
    % endfor
    default:
        UHD_ASSERT_THROW(false);
        return 0;
    }
}

uint16_t get_addr(const std::string& reg_name)
{
    % for reg in regs:
    if ("${reg.get_name()}" == reg_name) {
        return ${reg.get_addr()};
    }
    % endfor
    return 0;
}
"""

if __name__ == "__main__":
    import sys

    import common

    outfile = sys.argv[1]
    common.generate(
        name="admv1420_regs",
        regs_tmpl=REGS_TMPL,
        body_tmpl=BODY_TMPL,
        py_body_tmpl=PY_BODY_TMPL,
        file=__file__,
    )
