我正在编写代码,该代码处理可通过某些通信接口访问的某些硬件。
我想将某些寄存器集定义为描述整个设备的对象的属性。
让我们假设我有函数hw_read(address, size)
和hw_write(address, size, array_of_values)
,它们允许访问硬件。
让我们假设,我有一个长度为2的寄存器块,从地址10开始,并希望将其分配给属性" reg1"。 我可以通过以下方式完成:
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)
上面的实现不是很方便,因为我必须提供两次地址和大小。它容易出错,特别是如果我必须定义更多的寄存器。 是否有可能以这种方式实现它,我可以轻松定义 设置一些寄存器如下:
dev1=my_hardware()
dev1.define_register("reg1",10,2)
dev1.define_register("reg2",12,6)
然后通过以下方式访问它们:
dev1.reg1=(0x1234,0x3211)
print dev1.reg2
第二种方法也有很大的优势,可以从外部文本文件中读取寄存器列表(它们的名称,地址和大小)(例如也用于VHDL) 合成)。
更新 - 可能的解决方案?
在研究了与动态添加属性相关的帖子之后,我已经使用下面的代码实现了所需的功能:
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)))
以下是使用虚拟hw_read和hw_write函数的示例结果:
>>> 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)
我对以上解决方案是否合理有任何建议表示赞赏。
我能看到的一个问题(或特征?)是寄存器是按照类#34;定义的, 不是"每个实例"。如果我们有一些具有相同寄存器组的设备,它可能会有所帮助, 但如果我们想使用" my_hardware"可能会产生误导。 class,用于访问连接到同一总线的不同寄存器集的不同设备。
更新2
我找到了一个解决方案,它允许定义描述特定设备的派生类,并为每个类定义寄存器:
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)))
以下是演示正确操作的示例会话:
>>> 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)
如您所见,寄存器reg1在类&#34; dev1&#34;的设备中定义,而寄存器reg2在类dev2的设备中定义。当然在这种情况下我们还需要一个基地址传递给每个设备,因此我必须添加&#34; base_address&#34;到构造函数。
谢谢, WOJTEK
答案 0 :(得分:1)
更新(稍微更改以匹配第二个解决方案的功能):您可以使用描述符协议(http://en.wikipedia.org/wiki/Domain-specific_language)实现一个小的特定于域的语言(https://docs.python.org/3.4/howto/descriptor.html) 。作为一个例子,给定一个描述符:
class RegisterDescriptor(object):
"""
A descriptor that models reading and writing to hardware registers.
"""
def __init__(self, offset, size):
self.offset = offset
self.size = size
def __get__(self, obj, type=None):
if obj is None:
return self
return hw_read(obj.base_address + self.offset, self.size)
def __set__(self, obj, value):
if obj is None:
raise AttributeError("Cannot set attribute")
hw_write(obj.base_address + self.offset, self.size, value)
使用此描述符,您现在可以编写一个类并在语义上表示您的寄存器。再一次,例如:
class AddressableHardware(object):
"""
Base class for addressable hardware components.
Attributes:
base_address -- the base address of the device.
"""
def __init__(self, base_address):
self.base_address = base_address
class MyHardware(AddressableHardware):
"""
An example hardware device.
"""
reg1 = RegisterDescriptor(2, 4)
reg2 = RegisterDescriptor(6, 1)
def __init__(self, base_address):
AddressableHardware.__init__(self, base_address)
mh = MyHardware(0x2E)
print(mh.reg1)
mh.reg2 = b'\x00'
更新:这种方法与您的第二种解决方案不同,因为我采用了更具声明性的方法,其中每种类型的设备都有自己的类和相关方法(可能是一些辅助方法)隐藏对设备的低级访问权限并处理更多Pythonic数据类型。使用上面的描述符解决方案,最终会得到类似的结果:
class HardwareDeviceA(AddressableHardware):
reg1 = RegisterDescriptor(10, 2)
reg2 = RegisterDescriptor(20, 3)
def __init__(self, base_address):
AddressableHardware.__init__(self, base_address)
class HardwareDeviceB(AddressableHardware):
reg1 = RegisterDescriptor(10, 4)
def __init__(self, base_address):
AddressableHardware.__init__(self, base_address)
这允许您将多个设备类型A和B的实例连接到不同基址的机器,并且您不需要每次都设置它们的寄存器。