I'm writing a code, which handles some hardware accessible via certain communication interface.
I'd like to define certain sets of registers as properties of the object describing the whole device.
Lets assume, that I have functions hw_read(address, size)
and hw_write(address, size, array_of_values)
, which allow to access the hardware.
Let's assume, that I have a block of registers with length 2, starting from address 10, and want to assign it to a property "reg1". I can do it in a following way:
class my_hardware(object):
def __init__(self):
# Perform initialization
return
@property
def reg1(self):
return hw_read(10,2)
@reg1.setter
def reg1(self,value):
hw_write(10,2,value)
The above implementation is not very convenient, as I have to provide address and size twice. It is error prone, especially if I have to define bigger number of registers. Is it possible to implement it in such a way, that I could easily define set a few registers like below:
dev1=my_hardware()
dev1.define_register("reg1",10,2)
dev1.define_register("reg2",12,6)
And then access them via:
dev1.reg1=(0x1234,0x3211)
print dev1.reg2
The second approach has also significant advantage, that list of registers (their names, addresses and sizes) may be read from an external text file (e.g. used also for VHDL synthesis).
Update - possible solution?
After studying soe posts related to dynamical adding of properties, I have achieved the required functionality with the code shown below:
def hw_write (first,size,val):
print "write:"+str(first)+", "+str(size)+" val="+str(val)
def hw_read (first,size):
print "read:"+str(first)+", "+str(size)
return (0x1234,)*size
class my_hardware(object):
def __init__(self):
return
def add_reg(self,name,first,size):
setattr(my_hardware,name,property(lambda self : hw_read(first,size), lambda self, x: hw_write(first,size,x)))
Below are sample results with dummy hw_read and hw_write functions:
>>> a=my_hardware()
>>> a.add_reg("reg1",10,2)
>>> a.add_reg("reg2",20,3)
>>> a.reg1
read:10, 2
(4660, 4660)
>>> a.reg2
read:20, 3
(4660, 4660, 4660)
>>> a.reg1=(10, 11)
write:10, 2 val=(10, 11)
>>> a.reg2=(10, 11, 12)
write:20, 3 val=(10, 11, 12)
I'll appreciate any suggestions whether the above solution is reasonable.
One problem (or feature?) which I can see, is that registers are defined "per class", not "per instance". It may be helpful, if we have a few devices with the same register sets, but may be misleading, if we want to use "my_hardware" class to access different devices with different register sets connected to the same bus.
update 2
I have found a solution, which allows to define derived classes describing particular devices, and define registers per class:
def hw_write (first,size,val):
print "write:"+str(first)+", "+str(size)+"val="+str(val)
def hw_read (first,size):
print "read:"+str(first)+", "+str(size)
return (0x1234,)*size
class my_hardware(object):
def __init__(self, base_address):
self.ba = base_address
return
@classmethod
def add_reg(myclass,name,first,size):
setattr(myclass,name,property(lambda self : hw_read(self.ba+first,size), lambda self, x: hw_write(self.ba+first,size,x)))
Below is a sample session demonstrating correct operation:
>>> class dev1(my_hardware):
... pass
...
>>> class dev2(my_hardware):
... pass
...
>>> dev1.add_reg("reg1",10,2)
>>> dev2.add_reg("reg2",15,3)
>>> a=dev1(100)
>>> b=dev2(200)
>>> a.reg1
read:110, 2
(4660, 4660)
>>> a.reg2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dev1' object has no attribute 'reg2'
>>> b.reg1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'dev2' object has no attribute 'reg1'
>>> b.reg2
read:215, 3
(4660, 4660, 4660)
As you can see, register reg1 is defined in devices of class "dev1", while register reg2 is defined in devices of class dev2. Of course in this case we also need a base address to be passed to each device, and therefore I had to add "base_address" to the constructor.
Thanks, Wojtek