#!/usr/bin/env python
# Copyright (C) 2013, Elphel.inc.
# Registers/bit fields manipulation
# 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 .
__author__ = "Andrey Filippov"
__copyright__ = "Copyright 2013, Elphel, Inc."
__license__ = "GPL"
__version__ = "3.0+"
__maintainer__ = "Andrey Filippov"
__email__ = "andrey@elphel.com"
__status__ = "Development"
# new_sets.append((addr,data,mask,self.module_name,register_name,self.registers[register_name]))
def print_html_reg_header(html_file, title='',show_bit_fields=True, show_comments=True,filter_fields=True):
if title:
html_file.write('
'+title+'
\n')
html_file.write('\n')
if show_bit_fields:
html_file.write('Address/ bit field | Register name/ Bit field name | R/W | Value | Previous Value | Default | \n')
else:
html_file.write('
---|
Address | Register name | R/W | Value | Default | \n')
if show_comments:
html_file.write('Comments | ')
html_file.write('
')
def print_html_reg_footer(html_file):
html_file.write('
\n')
def print_html_registers(html_file, reg_sets, show_bit_fields=True, show_comments=True,filter_fields=True):
# new_sets.append((addr,data,mask,self.module_name,register_name,self.registers[register_name]))
current_reg_state={} #address: (data,mask)
for addr, data, mask, module_name, register_name, r_def in reg_sets:
if mask!=0:
try:
dflt_data=r_def['DFLT']
except:
dflt_data=0
try:
rw=r_def['RW']
except:
rw='RW'
try:
old_data,old_mask=current_reg_state[addr]
prev_sdata=hex(old_data)
except:
old_data=dflt_data
old_mask=0
prev_sdata='-'
new_data=((old_data ^ data) & mask) ^ old_data
new_mask= old_mask | mask
html_file.write('\n')
try:
comments=r_def['COMMENTS']
except:
comments=''
if show_bit_fields:
html_file.write(' '+hex(addr)+' | '+module_name+'.'+register_name+' | '+rw+' | '+
hex(new_data)+' | '+prev_sdata+' | '+hex(dflt_data)+' | ')
if show_comments:
html_file.write(''+comments+' | ')
html_file.write('\n
\n')
if 'FIELDS' in r_def:
#sort bit fields
# name_addr= [(n,r_def['FIELDS'][n]['r'][0]) for n in r_def['FIELDS']]
# names=[pair[0] for pair in sorted(name_addr, key = lambda rr: -rr[1])]
# names=[pair[0] for pair in sorted([(n,r_def['FIELDS'][n]['r'][0]) for n in r_def['FIELDS']], key = lambda rr: -rr[1])]
for f_name in [pair[0] for pair in sorted([(nam,r_def['FIELDS'][nam]['r'][0]) for nam in r_def['FIELDS']], key = lambda rr: -rr[1])]:
field=r_def['FIELDS'][f_name]
try:
f_rw=('R','RW')[field['m']!='R']
except:
f_rw='RW'
f_mask=0
r=(min(field['r'][0],field['r'][1]),max(field['r'][0],field['r'][1]))
for i in range(r[0],r[1]+1):
f_mask|=(1<> r[0]
f_dflt=(dflt_data & f_mask) >> r[0]
f_prev=(old_data & f_mask) >> r[0]
field_prev=('-', hex(f_prev))[prev_sdata!='-']
modified=f_data != f_prev
html_file.write(' '+str(r[0])+":"+str(r[1])+' | '+f_name+' | '+f_rw+' | ')
html_file.write(''+('','')[modified]+hex(f_data)+('','')[modified]+' | '+field_prev+' | '+hex(f_dflt)+' | ')
if show_comments:
try:
f_comments=field['c']
except:
f_comments=''
html_file.write(''+f_comments+' | ')
html_file.write('\n
\n')
else:
html_file.write(' '+hex(addr)+' | '+module_name+'.'+register_name+' | '+rw+' | '+
hex(new_data)+' | '+prev_sdata+' | '+hex(dflt_data)+' | ')
if show_comments:
html_file.write(''+comments+' | ')
html_file.write('\n\n')
current_reg_state[addr]=(new_data,new_mask)
class EzynqRegisters:
ERRORS={
'ERR_REG': 'Register name is not defined',
'ERR_FIELD': 'Bit field is not defined',
'ERR_RO': 'Trying to overwrite read only register/ bit field',
'ERR_BITS': 'Data exceeds the available bits',
'ERR_NOTSET':'Register is not set',
}
def _mask(self,bits):
mask=0
for i in range (min(bits),max(bits)+1):
mask |= 1<>min(bits)
if (data & ~smask)!=0 :
msg ='Data '+hex(data)+' does not fit info the bit field '+str(bits)
if warn:
print 'WARNING: '+msg
else:
raise Exception (self.ERRORS['ERR_BITS']+' '+msg)
return ((data & smask) << min(bits),mask)
def _final_data_mask(self,register_name):
if not register_name in self.defs:
raise Exception (self.ERRORS['ERR_REG']+' '+register_name)
addr=self.base_addr+self.defs[register_name]['OFFS']
try:
data,mask=self.registers[register_name] # already in the list of registers?
except:
raise Exception (self.ERRORS['ERR_REG']+' '+register_name+" is not set in this series")
try:
old_data,old_mask=self.initial_state[addr]
except:
old_data=data
old_mask=0
data = ((old_data ^ data) & mask) ^old_data
mask |= old_mask
return (data,mask)
def set_initial_state(self,added_reg_sets, init=True):
if init:
self.initial_state={}
self.previous_reg_sets=[]
try:
self.initial_register_count=len(added_reg_sets)
except:
self.initial_register_count=0
if not added_reg_sets:
return
# print added_reg_sets
self.previous_reg_sets+=added_reg_sets # appends, not overwrites
for addr,data,mask,_,_,_ in added_reg_sets: # Do not need to care about default values - they will have 0 in the mask bits.
if addr in self.initial_state:
old_data,old_mask=self.initial_state[addr]
data=((old_data ^ data) & mask) ^ old_data
mask |= old_mask
self.initial_state[addr]=(data,mask)
def get_reg_names(self):
# name_offs=sorted([(name,self.registers[name]['OFFS']) for name in self.registers], key = lambda l: l[1])
# print '---self.registers=',self.registers
# unsorted_name_offs=[(name,self.defs[name]['OFFS']) for name in self.registers]
# print '---unsorted_name_offs=',unsorted_name_offs
# name_offs=sorted(unsorted_name_offs, key = lambda l: l[1])
# print '---name_offs=',name_offs
# return [n for n in name_offs]
# sort register names in the order of addresses
# return [n for n in sorted([(name,self.defs[name]['OFFS']) for name in self.registers], key = lambda l: l[1])]
return [n[0] for n in sorted([(name,self.defs[name]['OFFS']) for name in self.registers], key = lambda l: l[1])]
#number of registers set before this module (can be removed from the result of get_register_sets(sort_addr=True,apply_new=True))
def get_initial_count(self):
return self.initial_register_count
def get_register_sets(self, sort_addr=True,apply_new=True):
new_sets=[]
# for register_name in self.registers:
# print self.get_reg_names()
for register_name in self.get_reg_names(): # sorted by increasing offsets
addr=self.base_addr+self.defs[register_name]['OFFS']
_,mask=self.registers[register_name] # added (new only) mask, new data
if mask == 0:
continue # no bits set
data,_= self._final_data_mask(register_name) # combined data
new_sets.append((addr,data,mask,self.module_name,register_name,self.defs[register_name]))
if apply_new:
self.set_initial_state(new_sets, False)
self.registers={} # delete the applied registers
return self.previous_reg_sets # all register sets - previous with new attached
else:
return new_sets # only new register sets, does not change state
def __init__(self,defines,channel=0,current_reg_sets=[],permit_undefined_bits=False):
self.set_initial_state(current_reg_sets,True) # 'replay' previous register settings to the initial register state (et
self.defs=defines
self.base_addr=self.defs['BASE_ADDR'][channel]
self.module_name=self.defs['MODULE_NAME'][channel]
self.registers={} # now for name it will hold (address, data, mask) tuples
# calculate R/W masks
self.ro_masks={}
for reg_name in self.defs:
reg=self.defs[reg_name]
if ('RW' in reg) and permit_undefined_bits:
if 'W' in reg['RW']:
self.ro_masks[reg_name]=0 # all enabled
break;
elif 'R' in reg['RW']: # R, but no W - read only
self.ro_masks[reg_name]=0xFFFFFFFF # all read only
break;
else:
# some bits are writable, some - readonly
ro_mask=0
wr_mask=0
try:
fields=reg['FIELDS']
except:
continue
for field_name in fields:
field=fields[field_name]
# print field_name,field
m= self._mask(field['r'])
if ('m' in field) and (field['m']=='R'):
ro_mask |= m
else:
wr_mask |= m
self.ro_masks[reg_name]=((~wr_mask) & 0xFFFFFFFF, ro_mask)[permit_undefined_bits != 0]
# Combine bit field defaults into register default?
def set_word(self,register_name,data,force=False):
if not register_name in self.defs:
raise Exception (self.ERRORS['ERR_REG']+' '+register_name)
# addr=self.base_addr+self.defs[register_name]['OFFS']
# see if that register at this address was previously set
default_mask=(self.ro_masks[register_name] ^ 0xffffffff) & 0xffffffff # write enabled bits in the register
#no need to look at self.initial_state - it will be combined later. When handling never-defined-fields will use data from the first use of the address
try:
old_data=self.defs[register_name]['DFLT']
except:
old_data=0 # DFLT not defined
diff = (data ^ old_data) & self.ro_masks[register_name]
if diff:
msg= register_name+" readonly mask="+hex(self.ro_masks[register_name])+" previous data="+hex(old_data)+" new data = "+data+" force="+force
if force:
print 'WARNING: '+msg
else:
raise Exception (self.ERRORS['ERR_RO'] + " " + msg)
data= ((data ^ old_data) & default_mask) ^ old_data
# mask= old_mask | default_mask
# self.registers[register_name]=(data,mask)
#assuming that writing words overwrites all defined bitfields
self.registers[register_name]=(data,default_mask) # that does not include old mask, will have to be combined for the next state of the registers
# old_data,old_mask=self.initial_state[addr]
def unset_word(self,register):
if not register in self.defs:
raise Exception (self.ERRORS['ERR_REG'])
try:
del self.registers[register]
except:
pass # OK if it did not exist
# force - force readonly/undefined, warn - if data does n ot fit into the bit field
def set_bitfield(self,register_name,field_name,data,force=False,warn=False):
if not register_name in self.defs:
raise Exception (self.ERRORS['ERR_REG']+' '+register_name)
try:
old_data,old_mask=self.registers[register_name] # already in the list of registers?
except:
try:
old_data=self.defs[register_name]['DFLT'] # default provided?
except:
old_data=0 # DFLT not defined, use 0
old_mask=0
try:
field=self.defs[register_name]['FIELDS'][field_name]
except:
print self.defs[register_name]
raise Exception (self.ERRORS['ERR_FIELD']+': '+register_name+'.'+field_name)
new_data,new_mask=self._data(field['r'],data,warn)
combined_data= ((new_data ^ old_data) & new_mask) ^ old_data # new data applied to old data
combined_mask=old_mask | new_mask
# print "field['r']=",field['r']
# print 'register_name=',register_name,' field_name=', field_name,
# print 'new_data=',hex(new_data),' new_mask=',hex(new_mask)
# print 'old_data=',hex(old_data),' old_mask=',hex(old_mask)
# print 'combined_data=',hex(combined_data),' combined_mask=',hex(combined_mask)
# print
diff = (combined_data ^ old_data) & self.ro_masks[register_name]
if diff:
msg= register_name+" readonly mask="+hex(self.ro_masks[register_name])+" old data="+hex(old_data)+" new data = "+combined_data+" force="+force
if force:
print 'WARNING: '+msg
else:
raise Exception (self.ERRORS['ERR_RO'] + " " + msg)
self.registers[register_name]=(combined_data,combined_mask)
def set_bitfields(self,register_name,field_data,force=False,warn=False):
if (len(field_data)==2) and not isinstance(field_data[1],tuple):
field_data=(field_data,)
# print field_data
for field_name,data in field_data:
self.set_bitfield(register_name,field_name,data,force,warn)
def unset_bitfield(self,register_name,field_name):
if not register_name in self.defs:
raise Exception (self.ERRORS['ERR_REG']+' '+register_name)
try:
old_data,old_mask=self.registers[register_name] # already in the list of registers?
except:
return # register is not set
try:
dflt_data=self.defs[register_name]['DFLT'] # default provided?
except:
dflt_data=0 # DFLT not defined, use 0
#new data and mask
try:
field=self.defs[register_name]['FIELDS'][field_name]
except:
raise Exception (self.ERRORS['ERR_FIELD']+': '+register_name+'.'+field_name)
mask= self._mask(field['r'])
combined_data= ((dflt_data ^ old_data) & mask) ^ old_data # new data applied to old data
combined_mask=old_mask & (~mask)
self.registers[register_name]=(combined_data,combined_mask) # no need to verify readonly - restoring defaults
def _get_register_address_data_mask_default_previous(self,register_name,from_defines=False):
if not register_name in self.defs:
raise Exception (self.ERRORS['ERR_REG']+' '+register_name)
def_reg= self.defs[register_name]
address=self.base_addr+def_reg['OFFS']
try:
dflt_data=self.defs[register_name]['DFLT']
except:
dflt_data=0 #default is not defined for the register
data=dflt_data
mask=(self.ro_masks[register_name] ^ 0xffffffff) & 0xffffffff # write enabled bits in the register
try:
prev_data,_=self.initial_state[address]
except:
prev_data=-1 # never set before
#self.initial_state[addr]=(data,mask)
if not from_defines:
try:
_,mask=self.registers[register_name] # keep only this mask fro the register
data,_=self._final_data_mask(register_name) # return final data, combining mew settings with the previous
except:
raise Exception (self.ERRORS['ERR_NOTSET'] + " " + register_name)
return (address,data,mask,dflt_data,prev_data)
def get_address_data_pairs_list(self):
return[{'ADDRESS':self.base_addr+self.defs[name]['OFFS']} for name in self.get_reg_names()]
def get_register_details(self,register_name,from_defines=False,filter_fields=True):
# print '===register_name=',register_name
address, data, mask, dflt_data, prev_data = self._get_register_address_data_mask_default_previous(register_name,from_defines)
details={'NAME':register_name,'ADDRESS':address,'DATA':data}
def_reg= self.defs[register_name]
for key in def_reg:
if key=='FIELDS':
pass
else:
details[key]=def_reg[key]
# fill in needed fields if they were missing
if not 'DFLT' in details:
details['DFLT']=dflt_data
if not 'RW' in details:
details['RW']='RW'
if not 'COMMENTS' in details:
details['COMMENTS']=''
details['PREVIOUS']=prev_data #negative - not previously set
field_details=[]
for field_name in def_reg['FIELDS']:
# print '+++++ field_name=',field_name
# print ' details=',details
field=self.get_bitfield_details(register_name,field_name,from_defines)
field_mask=self._mask(field['r'])
if (not filter_fields) or (field_mask & mask):
field_details.append(field)
# print '+++++ field_details=',field_details
details['FIELDS']=sorted(field_details, key = lambda rr: -rr['r'][0]) # from MSB to LSB
return details
def get_bitfield_details(self,register_name,field_name,from_defines=False):
# _, data, dflt_data = self._get_register_address_data_default(register_name,from_defines)
# address, data, mask, dflt_data, prev_data = self._get_register_address_data_mask_default_previous(register_name,from_defines)
_, data, _, dflt_data, prev_data = self._get_register_address_data_mask_default_previous(register_name,from_defines)
if not field_name in self.defs[register_name]['FIELDS']:
raise Exception (self.ERRORS['ERR_FIELD']+' '+register_name+'.'+field_name)
bit_field=self.defs[register_name]['FIELDS'][field_name]
mask=self._mask(bit_field['r'])
field_data=(data & mask) >> min(bit_field['r'])
field_dflt=(dflt_data & mask) >> min(bit_field['r'])
field_prev=((prev_data & mask) >> min(bit_field['r']) & mask) >> min(bit_field['r'])
field_details={'NAME':field_name,'DATA':field_data,'PREV':field_prev}
for key in bit_field:
field_details[key]=bit_field[key]
field_details['d']=field_dflt
try:
field_details['m'] = bit_field['m']
except:
field_details['m'] = 'RW'
if not 'c' in field_details:
field_details['c']=''
return field_details
def print_html_register(self, register_name, html_file, show_bit_fields=True, show_comments=True,filter_fields=True):
r=self.get_register_details(register_name,False,filter_fields)
html_file.write('\n')
prev_data_exists=r['PREVIOUS']>=0
prev_data = ('-', hex(r['PREVIOUS']))[prev_data_exists]
if show_bit_fields:
html_file.write(' '+hex(r['ADDRESS'])+' | '+r['NAME']+' | '+r['RW']+' | '+hex(r['DATA'])+' | '+prev_data+' | '+hex(r['DFLT'])+' | ')
if show_comments:
html_file.write(''+(r['COMMENTS'])+' | ')
html_file.write('\n
\n')
for field in r['FIELDS']:
field_prev=('-', hex(field['PREV']))[prev_data_exists]
html_file.write(' '+str(field['r'][0])+":"+str(field['r'][1])+' | '+field['NAME']+' | '+field['m']+' | ')
html_file.write(''+hex(field['DATA'])+' | '+field_prev+' | '+hex(field['d'])+' | ')
if show_comments:
html_file.write(''+(field['c'])+' | ')
html_file.write('\n
\n')
else:
html_file.write(' '+hex(r['ADDRESS'])+' | '+r['NAME']+' | '+r['RW']+' | '+hex(r['DATA'])+' | '+prev_data+' | '+hex(r['DFLT'])+' | ')
if show_comments:
html_file.write(''+r['COMMENTS']+' | ')
html_file.write('\n\n')
def print_html_registers(self, html_file, show_bit_fields=True, show_comments=True,filter_fields=True):
html_file.write('\n')
if show_bit_fields:
html_file.write('Address/ bit field | Register name/ Bit field name | R/W | Value | Previous Value | Default | \n')
if show_comments:
html_file.write('Comments | ')
html_file.write('
')
else:
html_file.write('Address | Register name | R/W | Value | Default | \n')
if show_comments:
html_file.write('Comments | ')
html_file.write('
')
for register in self.get_reg_names():
self.print_html_register(register[0], html_file, show_bit_fields, show_comments,filter_fields)
html_file.write('
\n')
#QULAIFIER_CHAR
#prefix like CONFIG_EZYNQ_DDR_SET_
#postfix like '_PRE','_POST' or ''
#qualifier_char '__'
#value may be hex/decimal or "FREE" (value convert to upper)
def parse_options_set(self,raw_configs,prefix,postfix,postfixes,qualifier_char,force=True,warn=True): #force - readonly/undefined fields, warn: data does not fit in the bit field
raw_regs={}
for line in raw_configs:
reg = line['KEY']
value = line['VALUE']
value=str(value).upper()
if (reg[:len(prefix) ]== prefix) and (reg[len(reg)-len(postfix):] == postfix):
#see if any other postfixes match
for other_postfix in postfixes:
if (other_postfix!=postfix) and (len(other_postfix)> len(postfix)) and (reg[len(reg)-len(other_postfix):] == other_postfix):
# print '== reg=',reg
# print '== postfix=',postfix
# print '== other_postfix=',other_postfix
break
else: #no other postfixes match
reg=reg[len(prefix):len(reg)-len(postfix)]
# print '++++ reg=',reg
if qualifier_char in reg:
reg,field=reg.split(qualifier_char,1)
else:
field=''
if not reg in self.defs:
raise Exception (self.ERRORS['ERR_REG']+' '+reg)
try:
value=int(value,0)
except:
value="FREE" # "Free" - anything but numeric/hex. Or do something else, parse more options?
if not reg in raw_regs:
raw_regs[reg] = {}
if len(field)>0:
if not field in self.defs[reg]['FIELDS']:
raise Exception (self.ERRORS['ERR_FIELD']+': '+reg+'.'+field)
try:
raw_regs[reg]['FIELDS'][field]=value
except:
raw_regs[reg]['FIELDS']={field:value}
else:
raw_regs[reg]['VALUE']=value
# apply raw_regs (should be done after normal parameters processed and some registers are populated
# if len(raw_regs) >0:
# print raw_regs
for reg_name in raw_regs:
reg=raw_regs[reg_name]
if 'VALUE' in reg:
if reg['VALUE'] == 'FREE':
self.unset_word(reg_name)
else:
self.set_word(reg_name,reg['VALUE'],force)
try:
fields=raw_regs[reg_name]['FIELDS']
except:
continue
for field_name in fields:
if fields[field_name] == 'FREE': #) or (reg_name in self.registers):
self.unset_bitfield(reg_name,field_name) # will do nothing if register is not there
else:
self.set_bitfield(reg_name,field_name,fields[field_name],force,warn)