#
# 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 .
#
import re
import sys
import math
from Cheetah.Template import Template
COMMON_TMPL = """\
#import time
/***********************************************************************
* This file was generated by $file on $time.strftime("%c")
**********************************************************************/
\#ifndef INCLUDED_$(name.upper())_HPP
\#define INCLUDED_$(name.upper())_HPP
\#include
\#include
\#include
\#include
class $(name)_t{
public:
#for $reg in $regs
#if $reg.get_enums()
enum $reg.get_type(){
#for $i, $enum in enumerate($reg.get_enums())
#set $end_comma = ',' if $i < len($reg.get_enums())-1 else ''
$(reg.get_name().upper())_$(enum[0].upper()) = $enum[1]$end_comma
#end for
};
#end if
$reg.get_type() $reg.get_name();
#end for
$(name)_t(void){
_state = NULL;
#for $reg in $regs
$reg.get_name() = $reg.get_default();
#end for
}
~$(name)_t(void){
delete _state;
}
$body
void save_state(void){
if (_state == NULL) _state = new $(name)_t();
#for $reg in $regs
_state->$reg.get_name() = this->$reg.get_name();
#end for
}
template std::set get_changed_addrs(void){
if (_state == NULL) throw std::runtime_error("no saved state");
//check each register for changes
std::set addrs;
#for $reg in $regs
if(_state->$reg.get_name() != this->$reg.get_name()){
addrs.insert($reg.get_addr());
}
#end for
return addrs;
}
#for $mreg in $mregs
$mreg.get_type() get_$(mreg.get_name())(void){
return
#set $shift = 0
#for $reg in $mreg.get_regs()
($(mreg.get_type())($reg.get_name() & $reg.get_mask()) << $shift) |
#set $shift = $shift + $reg.get_bit_width()
#end for
0;
}
void set_$(mreg.get_name())($mreg.get_type() reg){
#set $shift = 0
#for $reg in $mreg.get_regs()
$reg.get_name() = (reg >> $shift) & $reg.get_mask();
#set $shift = $shift + $reg.get_bit_width()
#end for
}
#end for
private:
$(name)_t *_state;
};
\#endif /* INCLUDED_$(name.upper())_HPP */
"""
def parse_tmpl(_tmpl_text, **kwargs):
return str(Template(_tmpl_text, kwargs))
def to_num(arg): return int(eval(arg))
class reg:
def __init__(self, reg_des):
try: self.parse(reg_des)
except Exception, e:
raise Exception, 'Error parsing register description: "%s"\nWhat: %s'%(reg_des, e)
def parse(self, reg_des):
x = re.match('^(\w*)\s*(\w*)\[(.*)\]\s*(\w*)\s*(.*)$', reg_des)
name, addr, bit_range, default, enums = x.groups()
#store variables
self._name = name
self._addr = to_num(addr)
if ':' in bit_range: self._addr_spec = sorted(map(int, bit_range.split(':')))
else: self._addr_spec = int(bit_range), int(bit_range)
self._default = to_num(default)
#extract enum
self._enums = list()
if enums:
enum_val = 0
for enum_str in map(str.strip, enums.split(',')):
if '=' in enum_str:
enum_name, enum_val = enum_str.split('=')
enum_val = to_num(enum_val)
else: enum_name = enum_str
self._enums.append((enum_name, enum_val))
enum_val += 1
def get_addr(self): return self._addr
def get_enums(self): return self._enums
def get_name(self): return self._name
def get_default(self):
for key, val in self.get_enums():
if val == self._default: return str.upper('%s_%s'%(self.get_name(), key))
return self._default
def get_type(self):
if self.get_enums(): return '%s_t'%self.get_name()
return 'boost::uint%d_t'%max(2**math.ceil(math.log(self.get_bit_width(), 2)), 8)
def get_shift(self): return self._addr_spec[0]
def get_mask(self): return hex(int('1'*self.get_bit_width(), 2))
def get_bit_width(self): return self._addr_spec[1] - self._addr_spec[0] + 1
class mreg:
def __init__(self, mreg_des, regs):
try: self.parse(mreg_des, regs)
except Exception, e:
raise Exception, 'Error parsing meta register description: "%s"\nWhat: %s'%(mreg_des, e)
def parse(self, mreg_des, regs):
x = re.match('^~(\w*)\s+(.*)\s*$', mreg_des)
self._name, reg_names = x.groups()
regs_dict = dict([(reg.get_name(), reg) for reg in regs])
self._regs = [regs_dict[reg_name] for reg_name in map(str.strip, reg_names.split(','))]
def get_name(self): return self._name
def get_regs(self): return self._regs
def get_bit_width(self): return sum(map(reg.get_bit_width, self._regs))
def get_type(self):
return 'boost::uint%d_t'%max(2**math.ceil(math.log(self.get_bit_width(), 2)), 8)
def generate(name, regs_tmpl, body_tmpl='', file=__file__, append=False):
#evaluate the regs template and parse each line into a register
regs = list(); mregs = list()
for entry in parse_tmpl(regs_tmpl).splitlines():
if entry.startswith('~'): mregs.append(mreg(entry, regs))
else: regs.append(reg(entry))
#evaluate the body template with the list of registers
body = '\n '.join(parse_tmpl(body_tmpl, regs=regs).splitlines())
#evaluate the code template with the parsed registers and arguments
code = parse_tmpl(COMMON_TMPL,
name=name,
regs=regs,
mregs=mregs,
body=body,
file=file,
)
#write the generated code to file specified by argv1
open(sys.argv[1], 'a' if append else 'w').write(code)