我试图在某些asm宏中使用整数模板参数,但我遇到了替换问题。下面是我正在做的(非常)简化版本:
template<int X> void delay() {
asm __volatile__(
" .macro fl_delay dtime, reg=r0 \n\t"
" .if (\\dtime > 0) \n\t"
" .set dcycle, (\\dtime / 3) \n\t"
" .set dwork, (dcycle * 3) \n\t"
" .set drem, (\\dtime - dwork) \n\t"
" .rept (drem) \n\t"
" nop \n\t"
" .endr \n\t"
" .if dcycle >= 0 \n\t"
" mov \\reg, #dcycle \n\t"
" loop_\\@: \n\t"
" sub \\reg, #1 \n\t"
" bne loop_\\@ \n\t"
" .endif \n\t"
" .endif \n\t"
" .endm \n\t"
" fl_delay %[X], r0 \n\t"
: [X] "I" (X)
:
: "r0");
}
宏基本上允许我做循环计数准确的延迟。所以,例如,如果在我的asm代码中我写道:
" fl_delay 13, r0"
它将延迟完全 13个周期(使用寄存器r0作为循环计数器)。这部分在一堆测试中非常有效。当我想让我的宏的参数取决于模板参数(上面的X)时,问题出现了。如果我(其他地方)写delay<13>();
,生成的函数的asm看起来像:
; fl_delay macro definition here
fl_delay #13, r0
问题是,gnu as的表达式解析器不喜欢#。我怀疑问题是宏中的位,比方说,我做.set dcycle, (\\dtime / 3)
。 \\dtime
将替换为#13
,使该行.set dcycle, (#13 / 3)
(我已经能够通过在某些测试代码中放置该确切的行来验证,因为错误消息是相同的)。
所以,我有两个问题需要找到答案:
我不相信我有办法滥用C预处理器来执行此操作,因为它会在任何模板/ C ++解析完成之前运行并进行替换。
(我正在编写的是一些具有非常严格的时序要求的代码,具体取决于所讨论的硬件 - 对于不同类型的硬件,协议几乎相同,时间也不同。我在时钟速度很低(16-48Mhz)的硬件上运行,有时延迟将是0-3个时钟,这足够短,意味着我无法在asm中执行此操作,这就是我使用的原因.macros - 因为那时需要延迟0(因为时钟速度和硬件时序的组合)没有任何东西被发射,如果我需要延迟1,那么单个nop会被发射,等等......)
编辑:我有一个潜在的解决方案,它很难看,所以我将把这个问题留在这里以防人们有另一种选择。我的延迟有多大是有限的,所以我基本上可以做以下事情:template<int X> void delay() {
switch(X) {
case 0: asm __volatile__ (".set X, 0\n\t"); break;
case 1: asm __volatile__ (".set X, 1\n\t"); break;
case 2: asm __volatile__ (".set X, 2\n\t"); break;
case 3: asm __volatile__ (".set X, 3\n\t"); break;
case 4: asm __volatile__ (".set X, 4\n\t"); break;
case 5: asm __volatile__ (".set X, 5\n\t"); break;
case 6: asm __volatile__ (".set X, 6\n\t"); break;
}
asm __volatile__(
" .macro fl_delay dtime, reg=r0 \n\t"
" .if (\\dtime > 0) \n\t"
" .set dcycle, (\\dtime / 3) \n\t"
" .set dwork, (dcycle * 3) \n\t"
" .set drem, (\\dtime - dwork) \n\t"
" .rept (drem) \n\t"
" nop \n\t"
" .endr \n\t"
" .if dcycle >= 0 \n\t"
" mov \\reg, #dcycle \n\t"
" loop_\\@: \n\t"
" sub \\reg, #1 \n\t"
" bne loop_\\@ \n\t"
" .endif \n\t"
" .endif \n\t"
" .endm \n\t"
" fl_delay X, r0 \n\t"
:
:
: "r0");
}
void loop() { delay<5>(); }
这意味着对于delay<5>();
实例化,有一个.set X, 5
在我的asm块的其余部分之前发出。它会很难看(我的延迟可能会达到120个周期),但它会起作用。