如何编写严格符合AVR中断服务例程的方法

huangapple go评论54阅读模式
英文:

How to write strictly conforming interrupt service routines (on AVR)

问题

C标准规定,信号处理程序只能访问sig_atomic_t类型的对象(除了无锁原子操作)。访问其他类型的对象将产生未定义行为。中断服务例程类似于信号处理程序,因此在编写ISR时必须遵守此限制。

avr-libc的signal.h存在问题,因为它没有声明sig_atomic_t类型,尽管它声明了一个具有该名称的变量,类型为volatile signed char。这可能本来应该是一个typedef

但是,如果您希望ISR访问例如uint32_t类型的systick计数器,那将导致未定义行为。

avr-gcc是否有任何特殊预防措施或从标准中排除的情况,以便编写严格符合标准的ISR呢?

英文:

The C Standard mandates that a signal handler may access only objects of type sig_atomic_t (besides lock-free atomics). Accesses to objects of other types produce UB. An interrupt service routine resembles a signal handler, so this restriction must be obeyed when writing ISRs.

avr-libc's signal.h is broken in that it does not declare type sig_atomic_t, though it does declare a variable of that name, of type volatile signed char. Presumably, that was meant to be a typedef.

But if you want an ISR to access, say, a systick counter of type uint32_t then that would produce UB.

Are there any special precautions / exceptions from the standard that avr-gcc imposes, so that it would be possible to write a strictly conforming ISR?

答案1

得分: 2

如何编写严格符合 AVR 中断服务例程

由于您标记了问题为 avr-gcc,我将假定在其余部分中使用 GCC<sup>1</sup>。由于两个原因,以高性能方式编写符合规茅的中断服务例程是不可能的:

  1. 没有一种方法可以以符合规茅的方式编写中断服务例程。avr-gcc 使用属性来实现这一点。

  2. avr-gcc 既不实现原子类型,也不实现原子内建函数(后者当然是特定于编译器的,因此您可以在符合规范的讨论中忽略它们)。即使 avr-gcc 实现了原子类型,使用 libatomic 也会被大多数 AVR 开发者视为不合适,因为它会消耗大量资源。甚至编译器可以执行的简单原子访问也没有被实现。例如:

    int var;
    _Atomic int aaa;
    
    int get_var_atomic(void)
    {
        int v;
        __atomic_load(&amp;var, &amp;v, __ATOMIC_SEQ_CST);
        return v;
    }
    
    int get_var_atomic_n(void)
    {
        return __atomic_load_n(&amp;var, __ATOMIC_SEQ_CST);
    }
    
    int get_aaa(void)
    {
        return aaa;
    }
    

    所有三个函数将转发到 __atomic_load_2,这在 avr-gcc v13 中不存在,相应的指令也不存在,或者更准确地说:在 libatomic 中有通用版本,但 AVR 后端禁用了 libatomic。

    因此,您能做的最接近的方法是使用这些原语来实现原子操作,还要提供这些函数。由于其本质,这些函数无法内联。因此,唯一合理的方法是在编译器后端本身实现这一点,这在 ./gcc/config/avr/avr.md 中的某处<sup>2,3</sup>。


<sup>1</sup> 因此 avr-gccavr-g++ 和 AVR-LibC。不清楚其他编译器品牌如 IAR 的情况如何。

<sup>2</sup> 您可以使用普通的非原子加载和存储代码发射器,但对于简单情况,您必须获取一个 8 位的临时寄存器 Rx,以便执行以下操作:

in Rx, SREG
cli
<vanilla-code>
out SREG, Rx

  这里明显不能使用 __tmp_reg__

<sup>3</sup> 我刚刚因为全局关闭中断而受到了责备。因此,如果您想避免这种情况,您将不得不采用 libatomic 的方式并分配信号量。不用说,与通常的方法相比,这是极其昂贵的。

英文:

> How to write strictly conforming interrupt service routines (on AVR)

As you tagged the question avr-gcc, I'll assume GCC<sup>1</sup> in the remainder. It's not possible to do it in a performing way due to 2 reasons:

  1. There is no way to write an ISR in a conforming way. avr-gcc uses attributes to implement this.

  2. avr-gcc implements neither atomic types nor atomic builtins (where the latter are compiler specific of course, so you can ignore them for conforming discussion). Even if avr-gcc implemented atomic types, using libatomic would be considered inappropriate due to its resource consumption by most AVR folks. And even the simple atomic accesses that could be performed by the compiler without resorting to lib code are not implemented. Take for example:

    int var;
    _Atomic int aaa;
    
    int get_var_atomic (void)
    {
        int v;
        __atomic_load (&amp;var, &amp;v, __ATOMIC_SEQ_CST);
        return v;
    }
    
    int get_var_atomic_n (void)
    {
         return __atomic_load_n (&amp;var, __ATOMIC_SEQ_CST);
    }
    
    int get_aaa (void)
    {
        return aaa;
    }
    

    All three functions will forward to __atomic_load_2, which does not exists (as of avr-gcc v13), and respective insns don't exist, either. Or more precisely: there are generic versions in libatomic, but bachend avr has libatomic disabled.

    So the closest you can do is to use these primitives to implement atomic magic, and also to provide these functions. By its very nature, these functions cannot be inlined. So the only reasonable approach is to implement this in the compiler backend itself, which is somewhere in ./gcc/config/avr/avr.md<sup>2,3</sup>.


<sup>1 </sup>Hence avr-gcc or avr-g++ and AVR-LibC. Dunno how the situation is for other compiler brands like IAR.

<sup>2 </sup>You can use the vanilla non-atomic loads and stores code emitters, but you'll have to get an 8-bit tmp reg Rx so you can

in Rx, SREG
cli
&lt;vanilla-code&gt;
out SREG, Rx

for the simple cases. __tmp_reg__ can't be used here for obvious reasons.

<sup>3 </sup>I just got ranted for globally switching off IRQs. So if you want to avoid that, you'll have to go the libatomic way and allocate semaphores. No need so say that this is extremely expensive compared to the usual approaches.

huangapple
  • 本文由 发表于 2023年2月9日 00:21:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/75388742.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定