#!/usr/bin/env python
#
# Copyright 2011 Free Software Foundation, Inc.
# 
# This file is part of GNU Radio
# 
# GNU Radio 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, or (at your option)
# any later version.
# 
# GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
# 

from xml.dom import minidom

HEADER_TEMPL = """\
/*this file is auto_generated by volk_register.py*/

#include <volk/volk_cpu.h>
#include <volk/volk_config_fixed.h>

struct VOLK_CPU volk_cpu;

#if defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)
#  define VOLK_CPU_x86
#endif

#if defined(VOLK_CPU_x86)

//implement get cpuid for gcc compilers using a copy of cpuid.h
#if defined(__GNUC__)
#include <gcc_x86_cpuid.h>
#define cpuid_x86(op, r) __get_cpuid(op, (unsigned int *)r+0, (unsigned int *)r+1, (unsigned int *)r+2, (unsigned int *)r+3)

//implement get cpuid for MSVC compilers using __cpuid intrinsic
#elif defined(_MSC_VER)
#include <intrin.h>
#define cpuid_x86(op, r) __cpuid(r, op)

#else
#error "A get cpuid for volk is not available on this compiler..."
#endif

static inline unsigned int cpuid_eax(unsigned int op) {
    int regs[4];
    cpuid_x86 (op, regs);
    return regs[0];
}

static inline unsigned int cpuid_ebx(unsigned int op) {
    int regs[4];
    cpuid_x86 (op, regs);
    return regs[1];
}

static inline unsigned int cpuid_ecx(unsigned int op) {
    int regs[4];
    cpuid_x86 (op, regs);
    return regs[2];
}

static inline unsigned int cpuid_edx(unsigned int op) {
    int regs[4];
    cpuid_x86 (op, regs);
    return regs[3];
}
#endif

"""

def make_cpuid_c(dom) :
    tempstring = HEADER_TEMPL;
    
    for domarch in dom:
        if str(domarch.attributes["type"].value) == "x86":
            if "no_test" in domarch.attributes.keys():
                no_test = str(domarch.attributes["no_test"].value);
                if no_test == "true":
                    no_test = True;
                else:
                    no_test = False;
            else:
                no_test = False;
            arch = str(domarch.attributes["name"].value);
            op = domarch.getElementsByTagName("op");
            if op:
                op = str(op[0].firstChild.data);
            reg = domarch.getElementsByTagName("reg");
            if reg:
                reg = str(reg[0].firstChild.data);
            shift = domarch.getElementsByTagName("shift");
            if shift:
                shift = str(shift[0].firstChild.data);
            val = domarch.getElementsByTagName("val");
            if val:
                val = str(val[0].firstChild.data);
            
            if no_test:
                tempstring = tempstring + """\
int i_can_has_%s () {
#if defined(VOLK_CPU_x86)
    return 1;
#else
    return 0;
#endif
}
                
""" % (arch)
                
            elif op == "1":
                tempstring = tempstring + """\
int i_can_has_%s () {
#if defined(VOLK_CPU_x86)
    unsigned int e%sx = cpuid_e%sx (%s);
    return ((e%sx >> %s) & 1) == %s;
#else
    return 0;
#endif
}

""" % (arch, reg, reg, op, reg, shift, val)

            elif op == "0x80000001":
                tempstring = tempstring + """\
int i_can_has_%s () {
#if defined(VOLK_CPU_x86)
    unsigned int extended_fct_count = cpuid_eax(0x80000000);
    if (extended_fct_count < 0x80000001)
        return %s^1;
    unsigned int extended_features = cpuid_e%sx (%s);
    return ((extended_features >> %s) & 1) == %s;
#else
    return 0;
#endif
}

""" % (arch, val, reg, op, shift, val)
        
        elif str(domarch.attributes["type"].value) == "powerpc":
            arch = str(domarch.attributes["name"].value);
            tempstring = tempstring + """\
int i_can_has_%s () {
#ifdef __PPC__
    return 1;
#else
    return 0;
#endif
}

""" % (arch)

        elif str(domarch.attributes["type"].value) == "arm":
            arch = str(domarch.attributes["name"].value);
            tempstring = tempstring + """\
#if defined(__arm__) && defined(__linux__)
#include <asm/hwcap.h>
#include <linux/auxvec.h>
#include <stdio.h>
#define LOOK_FOR_NEON
#endif

int i_can_has_%s () {
//it's linux-specific, but if you're compiling libvolk for NEON
//on Windows you have other problems

#ifdef LOOK_FOR_NEON
    FILE *auxvec_f;
    unsigned long auxvec[2];
    unsigned int found_neon = 0;
    auxvec_f = fopen("/proc/self/auxv", "rb");
    if(!auxvec_f) return 0;
    
    //so auxv is basically 32b of ID and 32b of value
    //so it goes like this
    while(!found_neon && auxvec_f) {
      fread(auxvec, sizeof(unsigned long), 2, auxvec_f);
      if((auxvec[0] == AT_HWCAP) && (auxvec[1] & HWCAP_NEON))
        found_neon = 1;
    }
    
    fclose(auxvec_f);
    return found_neon;

#else
    return 0;
#endif
}

""" % (arch)
        
        elif str(domarch.attributes["type"].value) == "all":
            arch = str(domarch.attributes["name"].value);
            tempstring = tempstring + """\
int i_can_has_%s () {
    return 1;
}

""" % (arch)
        else:
            arch = str(domarch.attributes["name"].value);
            tempstring = tempstring + """\
int i_can_has_%s () {
    return 0;
}

""" % (arch)
    
    tempstring = tempstring + "void volk_cpu_init() {\n";
    for domarch in dom:
        arch = str(domarch.attributes["name"].value);
        tempstring = tempstring + "    volk_cpu.has_" + arch + " = &i_can_has_" + arch + ";\n"
    tempstring = tempstring + "}\n\n"

    tempstring = tempstring + "unsigned int volk_get_lvarch() {\n";
    tempstring = tempstring + "    unsigned int retval = 0;\n"
    tempstring = tempstring + "    volk_cpu_init();\n"
    for domarch in dom:
        arch = str(domarch.attributes["name"].value);
        tempstring = tempstring + "    retval += volk_cpu.has_" + arch + "() << LV_" + arch.swapcase() + ";\n"
    tempstring = tempstring + "    return retval;\n"
    tempstring = tempstring + "}\n\n"

    return tempstring;