This code creates a class that lets you read & write bitfields using field names. It wasn't written with ctypes
in mind, but you may still find it useful.
#!/usr/bin/env python
""" A bit field class
See http://stackoverflow.com/q/31960327/4014959
Written by PM 2Ring 2015.08.12
"""
class BitFields(object):
""" A bit field class
fieldwidth is a tuple or list containing the
bit width of each field, from least significant
to most significant.
"""
def __init__(self, totalwidth, fieldwidths):
if sum(fieldwidths) != totalwidth:
raise ValueError, "Field width error"
self.fieldwidths = fieldwidths
self.num_fields = len(fieldwidths)
#Calculate field offsets
self.offsets = []
pos = 0
for w in fieldwidths:
self.offsets.append(pos)
pos += w
#Set up bitfield attribute names
self.field_names = ['b' + str(i) for i in range(self.num_fields)]
self.clear()
#Set all fields to zero
def clear(self):
for f in self.field_names:
setattr(self, f, 0)
#A generator expression of all the field values
def _all_fields(self):
return (getattr(self, f) for f in self.field_names)
def __str__(self):
return ', '.join(['%s: 0x%x' % (f, v)
for f, v in zip(self.field_names, self._all_fields())])
#Get the register value as an int
@property
def value(self):
return sum(v<<p for v, p in zip(self._all_fields(), self.offsets))
#Set field values
def regset(self, **kwargs):
for f, v in kwargs.items():
setattr(self, f, v)
#Test
fields = (16, 8, 12, 12, 6, 4, 6)
reg = BitFields(64, fields)
#Set some fields by attribute
reg.b0 = 10
reg.b1 = 1
reg.b2 = 3
#Print the register using its __str__ method
print reg
#Print a single field
print reg.b1
#Print the current value of the register in decimal and as a hex string
v = reg.value
print v, hex(v)
#Reset all fields to zero
reg.clear()
print reg
#Set some fields by keyword
reg.regset(b0=7, b1=3, b2=1)
print reg
#Set some fields using a dict
field_dict = {'b0':5, 'b3':0xa, 'b4':0xf}
reg.regset(**field_dict)
print reg
output
b0: 0xa, b1: 0x1, b2: 0x3, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
1
50397194 0x301000a
b0: 0x0, b1: 0x0, b2: 0x0, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x7, b1: 0x3, b2: 0x1, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x5, b1: 0x3, b2: 0x1, b3: 0xa, b4: 0xf, b5: 0x0, b6: 0x0
Here's a simple Python 2 to_bytes()
function.
def to_bytes(n, width):
b = bytearray(width)
for i in range(width-1, -1, -1):
b[i] = n & 0xff
n >>= 8
if n == 0:
break
return bytes(b)
n = 0x8182838485868788
print repr(to_bytes(n, 8))
output
'\x81\x82\x83\x84\x85\x86\x87\x88'
Here's a slightly modified version of the class with a new method, .setvalue()
, which lets you set the value of the register from an integer. This method is called in the constructor, so you can now pass an optional integer to initialize the register. If no initial value is passed to the constructor then the register is initialized to zero, as before.
class BitFields(object):
""" A bit field class
fieldwidth is a tuple or list containing the
bit width of each field, from least significant
to most significant.
"""
def __init__(self, totalwidth, fieldwidths, value=0):
if sum(fieldwidths) != totalwidth:
raise ValueError, "Field width error"
self.fieldwidths = fieldwidths
self.num_fields = len(fieldwidths)
#Calculate field offsets
self.offsets = []
pos = 0
for w in fieldwidths:
self.offsets.append(pos)
pos += w
#Set up bitfield attribute names
self.field_names = ['b' + str(i) for i in range(self.num_fields)]
#Initialize
self.setvalue(value)
#Set all fields to zero
def clear(self):
for f in self.field_names:
setattr(self, f, 0)
#A generator expression of all the field values
def _all_fields(self):
return (getattr(self, f) for f in self.field_names)
def __str__(self):
return ', '.join(['%s: 0x%x' % (f, v)
for f, v in zip(self.field_names, self._all_fields())])
#Get the register value as an int
@property
def value(self):
return sum(v<<p for v, p in zip(self._all_fields(), self.offsets))
#Set field values
def regset(self, **kwargs):
for f, v in kwargs.items():
setattr(self, f, v)
#Set the register from an int
def setvalue(self, value):
for f, w in zip(self.field_names, self.fieldwidths):
#print f, w
mask = (1<<w) - 1
v = value & mask
value >>= w
setattr(self, f, v)
#Test
fields = (16, 8, 12, 12, 6, 4, 6)
reg = BitFields(64, fields)
#Set some fields by attribute
reg.b0 = 10
reg.b1 = 1
reg.b2 = 3
#Print the register using its __str__ method
print reg
#Print a single field
print reg.b1
#Print the current value of the register in decimal and as a hex string
v = reg.value
print v, hex(v)
#Reset all fields to zero
reg.clear()
print reg
#Set some fields by keyword
reg.regset(b0=7, b1=3, b2=1)
print reg
#Set some fields using a dict
field_dict = {'b0':5, 'b3':0xa, 'b4':0xf}
reg.regset(**field_dict)
print reg
#Set the register from an int or long
n = 0x111133337777ffff
reg = BitFields(64, fields, n)
print reg
v = reg.value
print v, hex(v), n == v
n = 0x123456789abcdef0
reg.setvalue(n)
print reg
v = reg.value
print v, hex(v), n == v
import random
print 'Testing .setvalue()...'
for _ in xrange(50000):
n = random.randint(0, (1<<64) - 1)
reg.setvalue(n)
v = reg.value
assert v == n, (n, v)
print 'OK'
output
b0: 0xa, b1: 0x1, b2: 0x3, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
1
50397194 0x301000a
b0: 0x0, b1: 0x0, b2: 0x0, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x7, b1: 0x3, b2: 0x1, b3: 0x0, b4: 0x0, b5: 0x0, b6: 0x0
b0: 0x5, b1: 0x3, b2: 0x1, b3: 0xa, b4: 0xf, b5: 0x0, b6: 0x0
b0: 0xffff, b1: 0x77, b2: 0x377, b3: 0x333, b4: 0x11, b5: 0x4, b6: 0x4
1229820469389557759 0x111133337777ffffL True
b0: 0xdef0, b1: 0xbc, b2: 0x89a, b3: 0x567, b4: 0x34, b5: 0x8, b6: 0x4
1311768467463790320 0x123456789abcdef0L True
Testing .setvalue()...
OK