既然硬件设计工程师的重点已经从逻辑门和总线转移到了系统设计,我们再来审视一下任何处理器系统中最常用到的寄存器设计。寄存器接口允许高速访问资源,其访问的效率对系统的性能有很大的影响。
寄存器的结构与访问
设计工程师应该精心选择硬件寄存器大小,使处理器能最有效地进行硬件访问。一般来说,总是采用系统内部整数访问方式。寄存器应该被译码为连续的组(没有地址空档),这样可以加速指针或阵列索引对寄存器的访问。任何可写的寄存器也应该是以同样的格式可读,这样可以避免使用本地存储器来缓存这些寄存器值。
控制一个子系统的寄存器应该以相同的结构形式在一起分组,使软件能使用通用的驱动程序对它们进行访问。当设计中需要多个同一类型的子系统时这点尤其重要。
为了避免被编码成独立进程的软件任务之间发生冲突,独立的子系统不能在系统处理器访问期间共享可写寄存器。这些“独立”的软件进程在访问共享寄存器时会产生竞争,除非在系统代码中使用不可中断的读/写驱动程序。根据操作系统的不同,多个进程共享寄存器甚至可能会产生功能调用的额外开销。访问共享寄存器的同时还有执行其它进程的做法是错误的,也是软件设计的通病,会导致间歇性的系统故障,影响集成和测试系统软件的进度。
系统A违反了很多上文提到的原则,如采用只写寄存器,共享控制和状态寄存器,以及没有为每个轴提供公共的寄存器映射。系统A必须用专门的驱动程序来缓冲写输出数据,移位并屏蔽轴驱动与位置信息,并防止轴驱动寄存器内容被为每个轴任务编写的代码所影响。系统B由于分离并重组了与每个轴有关的寄存器,因此能克服这些问题。
寄存器复位内容
硬件设计工程师应仔细考虑系统的复位状态。硬件设计通常采用启动程序来取得系统启动后的控制权,并将系统初始化到一个安全的状态。系统复位后应将硬件置于一个确定的安全状态,并且硬件应持续保持安全状态直到系统软件初始化完成为止。代码也应在软件控制下复位硬件以帮助调试、自检和原始代码的开发。
系统A不控制驱动寄存器的复位内容,需要代码的介入来将所有三个轴的驱动寄存器设置为零。这种结构会产生严重的系统设计问题,因为处理器通常是保持在复位状态,直到FPGA和ASIC加电并得到配置后处理器才正常工作。如果开发人员使用仿真器,那么在集成过程中系统A还会出现另外的问题:被仿真器控制的处理器在系统加电后可能需要很长的初始化时间才能正常工作。在软件取得控制权之前系统A和B的轴都处于随机驱动状态。
系统B在加电后会将所有轴驱动寄存器设为零,它对轴驱动设置的控制并不依赖于启动时间。因为系统B没有隐藏的状态机,因此在本设计中没有必要考虑增加额外的软件复位寄存器。
寄存器域设计
大多数资源接口所包含的数据项并不正好适合一个寄存器。这种情况下,硬件设计工程师必须将一个寄存器分成若干域。合理的域结构对系统性能来说非常重要,与寄存器接口设计有相似的影响。有效的域接口设计规则类似于寄存器设计规则,但设计工程师还需要特别注意域的顺序与放置,还要对寄存器中未用到一些字节作一定的处理。
1. 寄存器的域
域被定义为寄存器中若干位的子集,主要用于报告或控制资源的一个功能要素。在硬件设计中最常用的域类型有:1. 布尔域:真或假,通常是一位;2. 多位状态域和控制域:多位用于报告或控制内部相关功能;3. 列举状态域和控制域:多个位的集合,其中每个位代表了一种不同的硬件状态;4. 数字域:多个位组合在一起用来代表一定的数量值。
从软件使用者角度看,最有效的域结构是每个寄存器只用一个域。这种理想的软件结构可能导致硬件实现效率低,因此一个好的系统设计需要在软硬件设计之间作出折衷,在每个寄存器中应放置多个域。
下文将着重讨论一个寄存器中假设存在多个域的情况,不过,当对资源的某个特殊参数进行的有效访问将严重影响系统软件性能时,硬件设计工程师仍应该考虑使用单个域的寄存器。
2. 域结构
前文提到的用于寄存器的结构概念同样也适合于寄存器内部的域。一个寄存器应该只包含属于设计中同一功能要素的域,并且该寄存器中的所有可写域都应该是可读的。
那些包含有属于多个功能要素的域的寄存器同样需要特殊驱动程序支持,这样才能使多个进程安全地访问每个域。而配置为“只写”功能的域需要分配影子内存来保存寄存器域中的前一状态值。硬件设计工程师原来设想的简单的“屏蔽/写”操作现在变成了繁杂的多步功能调用,首先必须禁止中断和任务切换,然后读本地存储器,屏蔽输入输出值,再进行硬件寄存器写,最后开放中断和多任务切换。如果寄存器中所有域能得到有效安排,通过一个软件任务就能访问全部域的话,上述情况就能得到有效避免。
由于系统A将属于不相关功能的多个域组合放在一个寄存器中,因此它需要使用特殊的驱动程序。而系统B则遵循“单个寄存器内的域按任务进行组织”的原则,将每个域放置在属于自己的专用寄存器中,因此能高效地访问资源中的每个轴参数。
3. 十六进制数字对齐
硬件设计工程师还应该明白针对处理器和软件开发环境进行对齐约束。如果将域放置在错误的地址上而超出字的边界,将迫使软件设计工程师只能按块访问每个域,进而增加访问复杂性,降低访问的速度。在调试过程中,用零值填充域是非常有用的,可以使每个域的最低位对齐十六进制数字(4位)的边界:当在逻辑分析仪、调试仪或仿真器上显示寄存器情况时,十六进制数字对齐会有助于域值的可视化提取。系统A的寄存器域是没有对齐的,因此从原始的十六进制数据中提取域值很困难。由于控制域没有对齐,在查错时屏蔽测试输入也十分困难。而系统B的所有域都是按十六进制偶数数字对齐,因此通过寄存器读可以很容易地确定每个域的状态,并且能方便地将某个域设为指定值。
4. 域位置的分配与顺序
寄存器内域的设置也会严重影响软件实现的效率。布尔域和多位域通常与位置无关,但当列举域和数字域被放置在寄存器的最低位(LSB)时对它们的访问效率通常是最高的(LSB的实际位数取决于处理器类型,位0不一定是LSB)。将域配置在寄存器的LSB中可以有效地消除对域内容屏蔽后的移位操作,也使测试设备或进行可视化检查的调试仪访问寄存器时能更容易地识别域值。
系统A中用于轴2和轴3的域值在使用前必须要求软件进行屏蔽和移位。而系统B则将所有数字域配置在寄存器的LSB中,从而能完成更有效的访问。系统B的集成性也更好,资源寄存器的十六进制数据能真正分离成正确的域值。
5. 未用数据位
寄存器中的未用位同样也会影响软件实现的效率。所有未用位应回归为零,并且写入操作时无需对它们作特殊的处理,这样可以避免不必要的屏蔽与清除操作。这个规则的唯一一个例外是包含数字域为2的补码的寄存器,并且在寄存器中剩余的最高位(MSB)没有用的情况。在这种情况下,使硬件实现符号将域的MSB扩展到未用位就非常有用。以这种方式扩展的数字域能够被处理器直接访问,因为带符号的数值无需软件符号的扩展。当对特殊的数字域变量的访问速度严重影响整体系统性能时,将该类型的域与“单个寄存器单个域”结合起来考虑将非常有用。由于无需屏蔽或符号扩展,这些域能以内部数据访问的方式直接访问。
当系统A中需要从寄存器提取域值时,要求软件对每个数字域值进行符号扩展,而系统B允许通过对寄存器的内部整数访问直接访问域值。
6. 域类型选择
域类型的正确选择也能极大地提高软件实现效率。在打开或关闭独立资源功能时布尔域是最有效的。要注意的是,只有当寄存器是可读写时单位域才容易编码。如果硬件寄存器对域的访问有限制,就需要专门的缓冲器(有可能再加上一个专门的驱动程序)来保存当前的内容。限制性访问同时也会限制一些编程构造的使用,如位域(bit fiELD),从而影响系统代码的可读性,且无助于减少编程错误。
当表达资源状态的数据需要占用一定范围的值时数字域就很有用。当一个域能保持正值和负值使用时,带符号的表达式通常需要更多的软件工作。另外,还要避免在数字域中对其它数据进行编码(如利用域符号表示一个不相关的资源状态)。
从硬件实现来看,多位域更有效,但在写入系统代码时会增加代码的复杂度。列举类型通常能更好地反映资源中相关功能的实际可用性,可以有效防止冲突功能的采用(如将存储器块切换到本地总线上)。列举类型还应提供这样的可选项:无条件允许切换之间存在“停放带”,无条件允许系统软件中存在“先中断再实现”的代码切换。
系统A中对轴驱动域的“只写”访问使软件对目标域的访问效率很低,必须用RAM保存写过程中不作修改的过去的轴内容。系统B中由于每个寄存器都只有一个域并允许读写操作,因此不存在这样的问题。