哪种方式在微控制器中初始化寄存器更快?
Which way is faster in initializing registers in a microcontroller?
在为 PIC 微控制器编写固件时,我想到了这个问题。
我知道有两种方法可以初始化微控制器中的寄存器。举个例子,如果我们将端口初始化为输出,一种方法是编写如下命令,它将 1
分配给 TRISx 寄存器
中的每个位
方法一
TRISX = 0xFF;
同样的事情可以通过单独分配位来完成。
方法二
_TRISX0 = 1;
_TRISX1 = 1;
_TRISX2 = 1;
...
_TRISX7 = 1;
我的问题是,它会被编译器视为相同吗?完成这两个操作所花费的时间是否相同?还是方法 1 需要一个时钟周期而方法 2 需要 8 个时钟周期(我的意思是慢 ~8 倍)?
我尝试阅读 X16 compiler guide 但找不到任何提示。
一件事是,您没有提供 C 代码来说明这些位实际上是如何被引用的。但是假设它是通过 bit-fields.
的联合和结构
最好的方法是实际检查编译器生成的 ASM。您确实需要了解您的硬件架构,但您仍然需要查看生成的 ASM 才能真正了解。
要仅分配一个位,请说 _TRISX0=1; vs TRISX = 0x01;,取决于 arch 和编译器,编译器可能会生成比整个寄存器更高效(更少的周期,可能更少的指令)的代码,仅用于单个位赋值。
至少有一个这样的 MCU/DSP 处理器和编译器,来自 TI,我知道这是真的。
如果您有多个 (>1) 语句,您的方法 2,具有单独的位分配,您的 one-liner 寄存器分配可能会更有效或同样有效:如果编译器推断 - 错误或不 - 所有这些位分配按顺序分配给同一个寄存器,它可以用 one-liner 替换它们,就像你在方法 1.
中那样
我没有特别想到 PIC。我建议您在需要时检查任何 MCU 的 ASM。
硬件寄存器始终是 volatile
合格的,不允许编译器优化包含 volatile
访问的代码。因此,如果您向他们写入 8 次,那么您将获得 8 次写入。这当然比 1 写慢得多。
此外,连续多次写入寄存器是非常糟糕的做法,就好像它们是 RAM 中的临时变量一样。硬件寄存器往往具有各种微妙的方式 side-effects。它们可以具有 "write-once" 属性或仅接受某些模式下的写入。通过分几步给他们写信,你养成了一种习惯,因为不正确的寄存器设置会导致各种疯狂、微妙的问题。
正确的做法是只写入一次寄存器,或者尽可能少地写入。
例如,您可能认为示例中的数据方向寄存器非常愚蠢,没有 side-effects。但是 GPIO 硬件通常需要一些时间来切换端口电路,从写入数据方向寄存器到访问 I/O 端口。因此,多次写入可能会不必要地停止端口。
假设REGISTER
是memory-mapped,volatile
限定的硬件寄存器的名字,那么...
不要这样做:
MASK1 = calculation();
REGISTER |= MASK1;
MASK2 = calculation();
REGISTER |= MASK2;
这样做:
uintx_t reg_val=0; // temp variable in RAM
MASK1 = calculation();
reg_val |= MASK1;
MASK2 = calculation();
reg_val |= MASK2;
REGISTER = reg_val; // single write to the actual register
这将取决于处理器指令集和编译器。以PIC18F45K20为例,sdcc
编译器编译如下
TRISDbits.TRISD0 = 1;
至
BSF _TRISDbits, 0
编译时
TRISD = 0xFF;
至
MOVLW 0xff
MOVWF _TRISD
所以在这种情况下设置单个位会更快,因为它不涉及在工作寄存器中放置临时值。
然而,并非所有指令集都包含 BSF
指令,并且某些体系结构不需要为后一个任务使用工作寄存器。
P.S。以上示例基于 sdcc 编译器的输出,但我想 xc8
和 xc16
编译器会产生类似的结果。
P.P.S。检查生成的程序集时,请记住某些指令比其他指令消耗更多的处理器周期。有关详细信息,请参阅数据表。
在为 PIC 微控制器编写固件时,我想到了这个问题。
我知道有两种方法可以初始化微控制器中的寄存器。举个例子,如果我们将端口初始化为输出,一种方法是编写如下命令,它将 1
分配给 TRISx 寄存器
方法一
TRISX = 0xFF;
同样的事情可以通过单独分配位来完成。
方法二
_TRISX0 = 1;
_TRISX1 = 1;
_TRISX2 = 1;
...
_TRISX7 = 1;
我的问题是,它会被编译器视为相同吗?完成这两个操作所花费的时间是否相同?还是方法 1 需要一个时钟周期而方法 2 需要 8 个时钟周期(我的意思是慢 ~8 倍)?
我尝试阅读 X16 compiler guide 但找不到任何提示。
一件事是,您没有提供 C 代码来说明这些位实际上是如何被引用的。但是假设它是通过 bit-fields.
的联合和结构最好的方法是实际检查编译器生成的 ASM。您确实需要了解您的硬件架构,但您仍然需要查看生成的 ASM 才能真正了解。
要仅分配一个位,请说 _TRISX0=1; vs TRISX = 0x01;,取决于 arch 和编译器,编译器可能会生成比整个寄存器更高效(更少的周期,可能更少的指令)的代码,仅用于单个位赋值。 至少有一个这样的 MCU/DSP 处理器和编译器,来自 TI,我知道这是真的。
如果您有多个 (>1) 语句,您的方法 2,具有单独的位分配,您的 one-liner 寄存器分配可能会更有效或同样有效:如果编译器推断 - 错误或不 - 所有这些位分配按顺序分配给同一个寄存器,它可以用 one-liner 替换它们,就像你在方法 1.
中那样我没有特别想到 PIC。我建议您在需要时检查任何 MCU 的 ASM。
硬件寄存器始终是 volatile
合格的,不允许编译器优化包含 volatile
访问的代码。因此,如果您向他们写入 8 次,那么您将获得 8 次写入。这当然比 1 写慢得多。
此外,连续多次写入寄存器是非常糟糕的做法,就好像它们是 RAM 中的临时变量一样。硬件寄存器往往具有各种微妙的方式 side-effects。它们可以具有 "write-once" 属性或仅接受某些模式下的写入。通过分几步给他们写信,你养成了一种习惯,因为不正确的寄存器设置会导致各种疯狂、微妙的问题。
正确的做法是只写入一次寄存器,或者尽可能少地写入。
例如,您可能认为示例中的数据方向寄存器非常愚蠢,没有 side-effects。但是 GPIO 硬件通常需要一些时间来切换端口电路,从写入数据方向寄存器到访问 I/O 端口。因此,多次写入可能会不必要地停止端口。
假设REGISTER
是memory-mapped,volatile
限定的硬件寄存器的名字,那么...
不要这样做:
MASK1 = calculation();
REGISTER |= MASK1;
MASK2 = calculation();
REGISTER |= MASK2;
这样做:
uintx_t reg_val=0; // temp variable in RAM
MASK1 = calculation();
reg_val |= MASK1;
MASK2 = calculation();
reg_val |= MASK2;
REGISTER = reg_val; // single write to the actual register
这将取决于处理器指令集和编译器。以PIC18F45K20为例,sdcc
编译器编译如下
TRISDbits.TRISD0 = 1;
至
BSF _TRISDbits, 0
编译时
TRISD = 0xFF;
至
MOVLW 0xff
MOVWF _TRISD
所以在这种情况下设置单个位会更快,因为它不涉及在工作寄存器中放置临时值。
然而,并非所有指令集都包含 BSF
指令,并且某些体系结构不需要为后一个任务使用工作寄存器。
P.S。以上示例基于 sdcc 编译器的输出,但我想 xc8
和 xc16
编译器会产生类似的结果。
P.P.S。检查生成的程序集时,请记住某些指令比其他指令消耗更多的处理器周期。有关详细信息,请参阅数据表。