开源改变世界

修复了对 sys.execute 中标志的原子访问。 #436

推推 grbl 2年前 (2023-01-22) 130次浏览

对话

修复了对 sys.execute 中标志的原子访问。 #436
贡献者

这似乎修复了导致 Grbl 在某些操作期间挂起的错误,
尤其是慢跑。

#define bit_true(x,掩码) (x |= 掩码)
#define bit_false(x,掩码) (x &= ~掩码)
#define bit_toggle(x,掩码) (x ^= 掩码)
#define bit_true(x,mask) {uint8_t sreg = SREG; cli(); (x) |= (掩码); SREG = sreg; }
成员

这段代码不是我很熟悉的东西。我不知道运行这组代码意味着什么。一般来说,我不喜欢 Grbl 特定处理器的代码,除非出于移植原因很容易变通。我不是其他处理器的专家,所以我不能说是否所有其他微控制器都可以做这样的事情。但是,如果他们可以,我不会感到惊讶。

此外,cli() 命令:我不确定这会对系统中的其他所有内容产生什么影响。cli() 命令是否有可能清除或跳过串行中断、步进脉冲中断或引脚更改中断?

修复了对 sys.execute 中标志的原子访问。 #436

这似乎是一种在内存字上强制执行原子读取-修改-写入的有效方法。Atmel Mega 数据表2549P–AVR–10/2012有与此类似的示例。(参见第 7.8 节的示例)。

一种更快、更有效的方法可能是利用芯片的通用 IO 寄存器(第 9.3 节),它们具有原子设置位和清除位指令。使用此功能将避免必须禁用中断。
如果对 cpu_map.h 进行此更改:

#ifdef CPU_MAP_ATMEGA328P // (Arduino Uno) Officially supported by Grbl.
#define SYS_EXEC GPIOR0
...
#else //non atmel platforms
#define SYS_EXEC sys.execute
...
#endif

并将 sys.execute 替换为代码中其他任何地方的 SYS_EXEC,avr-gcc 编译器将在代码具有or的任何地方生成原子sbi和指令。cbiSYS_EXEC |= BITSYS_EXEC &= ~BIT

修复了对 sys.execute 中标志的原子访问。 #436
成员

嗯.. 仍然不清楚当 cli() 处于活动状态时其他中断会发生什么。如果步骤 ISR 与 cli() 同时触发,它是否排队等待全局中断标志重新启用?还是完全跳过它直到下一步 ISR?如果是这样,那么这将是一个交易破坏者。

我认为有更好的方法可以做到这一点,而不必弄乱全局中断标志。很可能只是通过引入一个新的 ISR-only 标志变量,主程序将从中读取。

修复了对 sys.execute 中标志的原子访问。 #436

不同优先级的中断将在再次使能中断时处理。他们将根据优先级开火。当调用 isr 时,中断被禁用,并在处理例程退出时被启用。因此,使用 cli/sei 提供原子访问应该是安全的。在 ISR 内部中断已经被禁用,所以访问多字节变量是安全的。

希尔森凯蒂尔

2014 年 6 月 27 日 18:33,“Sonny Jeon” notifications@github.com写道:

嗯.. 仍然不清楚当 cli() 处于活动状态时其他中断会发生什么。如果步骤 ISR 与 cli() 同时触发,它是否排队等待全局中断标志重新启用?还是完全跳过它直到下一步 ISR?如果是这样,那么这将是一个交易破坏者。

我认为有更好的方法可以做到这一点,而不必弄乱全局中断标志。很可能只是通过引入一个新的 ISR-only 标志变量,主程序将从中读取。


直接回复此电子邮件或在 GitHub 上查看。

修复了对 sys.execute 中标志的原子访问。 #436

链接文档的第 19 页:

类似地,如果在全局中断允许位被清除时发生一个或多个中断条件
,相应的中断标志将被设置并记住,直到
全局中断允许位被设置,然后将按优先级顺序执行。

但我同意,您不必这样做,至少在大多数情况下不必这样做。

修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

关于可移植性:每个平台都有自己的方式来确保对变量的原子访问。此外,可移植性可能会受到有关并发性和内存访问的其他特定于平台的因素的影响——例如,8 位访问可能是原子的,但 16 位可能不是。或者,您可能必须插入内存屏障以确保线程之间的缓存一致性(尽管这主要用于具有缓存的多处理器系统)。即使没有特定于平台的代码,平台相关的错误也会发生,就像在这种情况下一样。

关于中断:禁用中断以确保原子执行是受支持的标准技术。不应通过长时间禁用中断来滥用它,但对于设置/清除标志之类的短代码,它可以正常工作。如果在禁用中断的情况下触发中断,则处理程序将在重新启用中断后运行。这是在单 CPU 系统上确保原子性的最简单(但在本例中是足够的)方法。

关于不涉及平台特定代码的替代解决方案:我已经能够通过简单地将 EXEC_CYCLE_STOP 标志从 sys.execute 移到一个单独的变量中来可靠地工作。但是,我没有对主程序和中断处理程序可能修改哪些其他标志进行全面分析。如果主程序和中断处理程序都在更改同一个变量,则使用 bit_true/bit_false/bit_toggle 的其他代码可能会在其他地方发生相同的行为。

使用 GPIO 寄存器可能确实是最好的方法 – 只要这些寄存器尚未被 grbl 或标准库用于其他目的。此外,设置多个标志可能仍然不安全——据我所知,原子、单指令访问仅设置单个标志(需要通过 AVR 文档确认)。

修复了对 sys.execute 中标志的原子访问。 #436
成员

感谢大家的澄清。这打消了我的顾虑。至于设置 sys.execute 标志的其他地方,它们几乎在每个 ISR 中设置,从串行、步进器和引出线。其他人在潜在的冲突中应该非常非常罕见,除了连续剧,但我认为如果这是一个问题,我们应该已经看到了一些关于此的问题报告。

我很想看看你想出了什么@kfoltman作为具有单独变量的解决方案。与此同时,我将在本周末测试您的代码,看看它是否存在任何性能问题。正如你所说,我不应该遇到任何事情,我希望我不会。

修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

你想要我用来触发问题的 Python 脚本吗?它可能需要一些修改,但至少它是一个还算不错的起点。

修复了对 sys.execute 中标志的原子访问。 #436
成员

是的!那将是最有帮助的。我很好奇为什么这个步进 ISR 标志是导致这个特定问题的标志,而另一个 sys.execute 在其他 ISR 中写入不这样做。也许这只是关闭阶段期间一切的时间安排,或者其他原因。

修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

“在潜在冲突中非常罕见”- 可能仍然意味着偶尔会破坏工作(或什至工具),尤其是对于 PCB 等长期、复杂的工作。我希望我们能摆脱所有的比赛条件,这对我来说当然听起来不容易,但并非完全不可能。:) 请不要在实际机器上运行它,它可能会由于大量运动而损坏步进器或机械装置!

我用来测试的脚本是:

import random
import serial
import time
ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=0.001)
time.sleep(1)
outstanding = 0
data = ''
while True:
    time.sleep(0.01)
    data += ser.read()
    pos = data.find('\n')
    if pos == -1:
        line = ''
    else:
        line = data[0:pos + 1]
        data = data[pos + 1:]
    if line == '' and outstanding < 3:
        while outstanding < 3:
            ser.write("G0 Z%0.3f\n" % (0.01 * (random.random() - 0.5)))
            #ser.write("M3\n")
            outstanding += 1
        continue
    if line == 'ok\r\n':
        outstanding -= 1
    print outstanding, repr(line.rstrip())

请注意,出于某种原因,我的 arduino 克隆不会在每次打开时都重置,因此您可能必须将初始睡眠时间从 1 秒增加到 3 秒或更多。该脚本所做的是发出许多小距离的 G0 命令并等待 grbl 响应。我计算发送的命令和收到的“ok”响应之间的差异,以限制未完成请求的数量,这样输入缓冲区就不会溢出。

在未打补丁的版本中,它最终会停止接收“ok”消息。通常在一分钟左右后,具体取决于运行情况。

将 G0 替换为其他命令以验证其他地方不会出现相同的问题(探测等)可能会很有趣。

我将尝试编写另一个这样的脚本,它会发送一些位置变化(绝对或相对)并查看累积位置是否与命令一致。

修复了对 sys.execute 中标志的原子访问。 #436
成员

@kfoltman: 谢谢!消除所有竞争条件一直是当务之急,很多时候需要从头开始重写固件。我很高兴你能发现这个。我猜这个特殊问题在安装新的规划器和步进器算法时变得更加明显。您无法将较旧的 Grbl 固件推送到接近此的任何位置。我一直将此类问题归因于未受保护的计划程序缓冲区,但也许其中有一点是这个原子标志设置问题。

无论如何,因为人们已经为非常非常长的工作(数百万行)运行 Grbl,所以我认为正常操作不会有崩溃的风险。这可能是由于代码的编写方式导致它对其余原子标志写入的问题不敏感。但是,这并不是说我们不需要解决这个问题。我非常非常高兴我们可能已经识别出它并采取了重要措施来修复它。(主要是,我很高兴这不是状态机的问题,因为我很难找到它的任何问题。我很困惑。)

修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

周末我一直在玩“固定”版本——看起来不错,没有遇到任何错误/挂起/可疑行为。

修复了对 sys.execute 中标志的原子访问。 #436
成员

@kfoltman: 对不起!我无法在周末调查这个。我一直忙于其他工作和家庭事务。请给我一周的时间来深入研究这个问题。这是美国的假期周末,所以我想我会抽出一些时间。

修复了对 sys.execute 中标志的原子访问。 #436

@kfoltman:我无法在空运行时引起任何挂起。今晚我会在我的机器上测试。
也许我们应该将 bit_true/false 重命名为 bit_xxx_atomic,因为在某些情况下不需要额外的努力,例如“bit_false(value_words,bit(WORD_P));”

@chamnit:对 sys.execute 的所有更改现在都通过宏,所以这是一件好事 – 无论更改是否解决挂起。

顺便说一句,UGS 似乎不再在状态更新中出现问题!

修复了对 sys.execute 中标志的原子访问。 #436
成员

@mschorer: 感谢举报!我个人不使用 UGS,所以我没有看到状态更新问题,但这进一步验证了这个错误修复。状态更新通过串行中断使用相同的标志机制。同样的问题可能会间歇性地错过状态更新。

另外,我同意专用的 bit_true_atomic 宏。我们在其他不需要原子访问的地方使用它。浏览并更新它们对我来说并不难。

让我们知道您的测试情况如何!

修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

@mschorer我同意 _atomic 后缀,对于局部变量,使用原子版本没有任何意义。然而,也许原来的 bit_true / bit_false 也应该重命名为 bit_true_nonatomic / bit_false_nonatomic – 这样他们就不会被不熟悉代码的新人意外地用在错误的上下文中。至少根据我的经验,这些偏执的“防御性做法”往往效果很好,即使我用它们来防止或发现我自己将来的错误时也是如此:)

修复了对 sys.execute 中标志的原子访问。 #436

@kfoltman+@chamnit:我仍然可以在慢跑时让机器“保持”/失速。:-(
但它更难实现 – 所以我认为它一开始是固定的……

如果它对您有用并且对我更好:听起来像是(另一种)竞争条件。
请注意,它使用的是修改后的 0.9e – 我的微时序肯定会与您的略有不同!在这种情况下,从稍微不同的角度拍摄 grbl 可能是一件好事。

修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

好的,很高兴知道!我还不能用我的设置触发它,但我打算铣削一些 PCB,所以我有很好的动机在尝试之前查看任何剩余的崩溃。:)

您可以发布您的修改和设置吗?我认为这个问题应该可以在没有物理机器的情况下重现,我可以尝试调整我的测试脚本中的时间并让它长时间运行。

修复了对 sys.execute 中标志的原子访问。 #436

@kfoltman:代码在 github 的 mschorer/grbl/tree/dev 中。
我今晚可以发布设置…

  • 顺便说一句,我还没有测试流式 gcode .. 只是手动慢跑。
  • 当 gcode 队列已满时,停顿可能不是问题 – 流媒体将始终尝试使其充满……慢跑不会。我看到不同的慢跑结果取决于我何时按下按钮:我几乎可以肯定在减速阶段停止它。
修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

你用什么来编译它?该分支上的 Makefile 在 OBJECTS 中缺少 status.o 和 i2c_master.o – 我添加了它并运行了我的脚本,并进行了一些修改,但无法重现该问题。

修复了对 sys.execute 中标志的原子访问。 #436

我将 eclipse 与来自http://www.baeyens.it/eclipse/的 V2 avr-arduino 插件一起使用- 编译工作正常。但很好!
正如我所说:慢跑时我得到了摊位。还没有测试流媒体……这取决于我何时发送慢跑命令:加速、巡航或减速。减速是最糟糕的…

修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

@mschorer你能在某处发布你编译的 grbl.hex 和机器配置吗?也许它更棘手(依赖于编译器版本,或者仅在某些加速度或最大速度范围内发生)。不太可能但并非不可能。

我目前正在运行一个不同的脚本,一旦状态返回接近上一个移动结束的位置,它就会发出一个新的点动命令 (G91 G0)。令人恼火的是,还没有崩溃。另外,我假设您使用的是 UGS 0.7?我的脚本无法触发的前端/grbl 交互中可能存在一些微妙的时间。

修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

这是我的第二个测试脚本——尝试了不同的阈值和延迟值,以及不同的加速设置,但还没有一次崩溃。

import random
import serial
import time

class SerialLineReader:
    def __init__(self):
        self.ser = serial.Serial('/dev/ttyACM0', 115200, timeout=0)
        self.data = ''
    def write(self, data):
        self.ser.write(data)
        self.ser.flush()
    def writeln(self, data):
        self.write(data + "\n")
    def poll(self):
        buf = self.ser.read()
        self.data += buf
        while True:
            pos = self.data.find('\n')
            if pos != -1:
                line = self.data[0:pos]
                self.data = self.data[pos + 1:]
                if line.endswith('\r'):
                    line = line[:-1]
                return line
            else:
                return None

s = SerialLineReader()
while True:
    line = s.poll()
    if line is None:
        time.sleep(0.01)
    elif line.find("0.9e") != -1:
        break

unconfirmed = False
up = False
moving = False
outstanding = 0
waiting_for_status = False
waitcond = None
while True:
    line = s.poll()
    if line is None:
        if not waiting_for_status:
            time.sleep(0.01)
            waiting_for_status = True
            s.write("?")
        if not unconfirmed and not moving:
            moving = True
            unconfirmed = True
            up = not up
            if up:
                waitcond = lambda mov, x, y, z: z >= 0.9
                s.writeln("G91 G0 Z1")
            else:
                waitcond = lambda mov, x, y, z: z <= 0.1
                s.writeln("G91 G0 Z-1")
        continue
    if line.startswith("<"):
        line=line[1:-1].replace("MPos:", "").replace("WPos:", "").split(",")[0:4]
        if waitcond and waitcond(line[0], float(line[1]), float(line[2]), float(line[3])):
            moving = False
        print unconfirmed, line
        waiting_for_status = False
        continue
    if line == 'ok':
        unconfirmed = False
        print "Confirmed!"
修复了对 sys.execute 中标志的原子访问。 #436

做了一点 git 魔术,新结帐,重新编译……比以往任何时候都更好。不能让它失速?然而??
hex + settings + pinout 相关定义是@ http://www.alrightythen.de /demos/grbl
UGS 是 1.0.7

修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

.hex 是魔法前版本还是魔法后版本?:)

修复了对 sys.execute 中标志的原子访问。 #436
贡献者作者

(你做 git 魔法之前的版本对我来说更有趣,从“能够用脚本重现问题”的角度来看)

修复了对 sys.execute 中标志的原子访问。 #436

十六进制是“post”。
抱歉,我没有版本存档。最新的应该是最伟大的。
我希望能在周末抽出时间进行更多测试……我会及时通知大家。

camnit 添加了一个引用此拉取请求的提交 2014 年 7 月 7 日

修复了对 sys.execute 中标志的原子访问。 #436 chamnit 合并提交a20d3e9 到 grbl :开发 2014 年 7 月 7 日
免费注册 在 GitHub 上加入此对话。已有帐户? 登录评论
标签
还没有
项目

还没有

发展

成功合并此拉取请求可能会关闭这些问题。

还没有

5人参加
修复了对 sys.execute 中标志的原子访问。 #436修复了对 sys.execute 中标志的原子访问。 #436修复了对 sys.execute 中标志的原子访问。 #436修复了对 sys.execute 中标志的原子访问。 #436修复了对 sys.execute 中标志的原子访问。 #436

喜欢 (0)