Python - 硬件处理,如何有效地将硬件寄存器实现为对象中的属性

时间:2014-09-16 10:46:36

标签: python properties hardware

我正在编写代码,该代码处理可通过某些通信接口访问的某些硬件。 我想将某些寄存器集定义为描述整个设备的对象的属性。 让我们假设我有函数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

1 个答案:

答案 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的实例连接到不同基址的机器,并且您不需要每次都设置它们的寄存器。