如何解决如何安全地传递最终传递给内联汇编的IO地址?
我有一个AVR项目的简短摘要:
DELETE FROM dist
WHERE hexbin1,hexbin2,source NOT IN (
SELECT hexbin1,source FROM dist INNER JOIN (
SELECT hexbin1 as h1,hexbin2 as h2,min(distance) as m
FROM dist GROUP BY hexbin1,hexbin2)
ON hexbin1==h1 AND hexbin2==h2 AND distance==m);
此块后面的想法是在短时间内(T1H_NOOP)启用特定的引脚(实际的物理微处理器引脚),然后将其关闭。上面的代码实际上可以完美地工作。
但是,在上面的代码中,确切的引脚是硬编码的:PORTB,引脚0( uint8_t high = _BV(0);
uint8_t low = ~high;
uint8_t port_value = 0;
asm volatile (
"in %0,%1 \n\t"
"or %0,%3 \n\t"
"out %1,%0 \n\t"
T1H_NOOP
"and %0,%2 \n\t"
"out %1,%0 \n\t"
T1L_NOOP
: "=r" (port_value)
: "I" (_SFR_IO_ADDR(PORTB)),"r" (low),"r" (high));
)。我想要的是传递这样的地址:
_BV(0)
只要我留在C代码中,那实际上是可行的。
struct IO_ADDR {
volatile uint8_t *port;
uint8_t pin
}
当我这么说的时候,我的意思是我已经通过模拟器运行了这个程序,并且看到销钉按预期方式工作了,再加上我将该代码段与上面的程序集配对并在硬件上运行它。因此,很明显, struct IO_ADDR addr = { .port = &PORTB,.bit = 0 };
latch(&addr);
void latch(struct IO_ADDR *addr) {
if (addr->bit >= 8) return;
*(addr->port) &= ~(_BV(addr->bit));
_delay_us(50);
}
在寻址引脚本身,而不是指针。很好。
但是,当我这样做时,出现组装错误:
*(addr->port) &= ...
此错误:
asm volatile (
"in %0,%1 \n\t"
"or %0,%3 \n\t"
"out %1,%0 \n\t"
T1H_NOOP
"and %0,%2 \n\t"
"out %1,%0 \n\t"
T1L_NOOP
: "=r" (port_value)
: "I" (_SFR_IO_ADDR(*(addr->port))),"r" (high));
如果我用/nix/store/j31yaksw2dh82by2lgz1ysgh494cz6j2-src/neopixels.c: In function 'write_value':
/nix/store/j31yaksw2dh82by2lgz1ysgh494cz6j2-src/neopixels.c:29:9: warning: asm operand 1 probably doesn't match constraints
29 | asm volatile (
| ^~~
/nix/store/j31yaksw2dh82by2lgz1ysgh494cz6j2-src/neopixels.c:29:9: error: impossible constraint in 'asm'
替换addr-> port参数,也会发生这种情况。
_SFR_IO_ADDR(addr->port)
对此进行了预处理:
SFR_IO_ADDR(*(addr->port))
在 : "I" (
# 38 "src/neopixels.c" 3 4
(((uint16_t) &(
# 38 "src/neopixels.c"
*(addr->port)
# 38 "src/neopixels.c" 3 4
)) - 0x20)
# 38 "src/neopixels.c"
)
的情况下,最终组装应该是这样的,地址在此特定硬件上为0x24(并且忽略编译器选择的确切寄存器):
PORTB
要将该特定IO地址传递给我的汇编代码,我需要做什么?
解决方法
"I"
程序集约束requires it's operand to be a constant(或者,对于-O1 / -O2,是一个常量表达式),因此很遗憾,您将无法将其作为参数传递。
经过一天的研究,我找到了这个答案:
asm volatile (
"ld %0,%1 \n\t"
"or %0,%3 \n\t"
"st %1,%0 \n\t"
T1H_NOOP
"and %0,%2 \n\t"
"st %1,%0 \n\t"
T1L_NOOP
: "=r" (port_value)
: "X" (*(addr->port)),"r" (low),"r" (high));
涉及两个关键点。其中之一是提供X
约束,而不是I
约束,这实际上意味着“操作数可以是任何东西”。这不是次优的,但是汇编器不接受一些更明显的地址(“操作数是不可重定位的内存地址”。)
此外,我从in
和out
汇编程序指令(如罗斯·里奇在上面的注释中指出的那样)切换到需要在编译时必须知道的立即地址,并转到了{{ 1}}和ld
指令,它们接受内存地址。
最后一点,我必须更改某些T1H_NOOP,T1L_NOOP,T0H_NOOP和T0L_NOOP宏中NOP指令的数量,以维持LUKS (encryption)所需的时序约束。
所有这些都非常痛苦,以至于我读了一些有关脉冲宽度调制和带有中断的计时器/时钟电路的文章,因为这感觉会比我的活动繁忙循环产生更可靠的时序。但是,添加中断处理程序会以我现在不准备处理的方式增加代码的复杂性。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。