- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我目前正在努力处理一段非常简单的代码,它表明 ARM GCC 的 1 级优化器以某种方式破坏了一个简单的公式。
这使用标准编译器设置 (O1) 在最新的 Atmel 6.2 Studio 上运行。
Atmel 工具链\ARM GCC\Native\4.8.1426\arm-gnu-toolchain
代码非常少:
volatile uint32_t g_timing_tick_ms=0;
void SysTick_Handler(void)
{
g_timing_tick_ms++;
}
inline uint32_t get_millis()
{
return g_timing_tick_ms;
}
uint32_t get_micros()
{
return (g_timing_tick_ms * 1000 + (1000 - SysTick->VAL/84));
}
uint8_t timer_expired(timer_ *t)
{
uint32_t cur_us = get_micros();
uint32_t dt = cur_us - t->last_systick_us;
t->last_systick_us = cur_us;
if (t->elapsed <= dt)
{
// <--------- dt is regularly a huge value (around 0xfffffe00)
// this happens because t->last_systick_us sometimes is bigger than cur_us (overflow)
// however get_micros() is without such an error, cur_us ALWAYS increases and the
// variables are not modified outside this function which is called every 500us.
t->elapsed = t->interval;
return 1;
}
t->elapsed -= dt;
return 0;
};
get_millis
从每毫秒调用一次的 Systick 计时器返回毫秒数。
系统定时器为24位,以84mhz的速率递减计数。get_micros
() 使用此 systick 值并计算自上次重置以来经过的微秒,然后加上毫秒 *1000。
这工作得很好,我找不到更快的方法来获取当前微秒作为时间戳。
第三个函数显示了一个偶发问题,有时存储在 t->last_systick_us 中的值(直接来自 get_micros() )比它应该的大。确切地说,最后三个十进制值总是 986 (20065986,1000986)。
该值大约 1000us 太高,十进制数末尾始终带有 986。
每隔一些电话就会发生这种情况。
解决方法:
1)改变:
uint32_t dt = cur_us - t->last_systick_us; --->
volatile uint32_t dt = cur_us - t->last_systick_us;
将此变量更改为 volatile 解决了这个问题,这导致了编译器以一种糟糕的方式处理它的想法。该变量不是静态的,它是本地变量,没有任何东西可以从外部对其进行修改,volatile 是一种浪费,但可以解决数学问题。2)改变
uint32_t get_micros() ----->内联 uint32_t get_micros()这也解决了这个问题,但是这也不是一个好的解决方法,因为编译器不必将其内联。所以这可能会在未来的某个时候适得其反。
3)在值更改之前将任何调试写入或类似内容添加到计时器函数中也可以修复它,具体取决于代码。
这似乎是 gcc-ARM 核心编译器中的错误,优化器以某种方式破坏了数学。我可以提供 asm,我不知道 ARM ASM,但我注意到它在靠近 get_micro() 公式的部分删除了一个“sub”。
我不认为我这里有代码错误,它太简单了(而且效果很好)。此外,解决方案表明这不是编码错误,在函数中添加或删除内联除了优化之外应该没有任何区别。
也许有人知道该怎么做,经历过/解决了类似的行为。我正处于完全删除优化器的边缘,但这可能会花费相当多的性能。
更新
当我意识到可能的原因并且我认为是这种情况时,我正准备准备 asm 差异(并通读)。
我认为这是一个竞争条件,Systick 的中断还没有触发,但是 systick 定时器溢出了。
结果是大约 1000us 的误差(随着定时器每 84ns 滴答一次,误差会小一些。
这将导致错误,不可预测,并且通过更改代码,循环会发生变化,通过更改循环,它可能会以导致竞争条件稍后出现的方式对齐代码。
我进行了调试,并且可以验证问题在 Systick 重新加载后不久就发生了。
很抱歉在编译器错误中做出过快的猜测。
最佳答案
问题是由于竞争条件引起的,编译器没有错误。我不完全确定,但我认为这是 SAM3x8e ARM 实现(或一般的 Cortex M3)中的弱点,或者他们没有考虑使用 IRQ 和 Systick 值的人。
无论我尝试过什么修复或代码,我总是遇到以下两种情况之一:在 get_micros() 计算期间触发中断Systick 在 get_micros() 期间溢出,但没有触发中断。
get_micros() 读取一个旧的毫秒/Systick 值和一个新的 systick/毫秒变量,导致近 1 毫秒的错误。
有人可能会考虑添加 NVIC_DisableIRQ(SysTick_IRQn);一开始有帮助。它没有,它在 ASF 中没有记录,但 NVIC 不处理 Systick 启用/禁用,IRQn 为负,不会对异常产生任何影响。
有趣的是,NVIC 用于在 Atmels 驱动程序代码中设置优先级,但可能也没有效果。
另一个有趣的方面是 atmel 在它自己的一些源代码示例中使用了这个调用。(在浪费了 6 个小时之后就没那么有趣了)
我尝试用 __disable_irq() 保护代码但没有产生积极影响,同样的竞争条件发生了(定时器被改变但 Systick 值还没有超过)
我试过这个:
if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) micro_us+=1000;
这会读取系统控制寄存器并检查自上次读取后计数器是否溢出。
根据 atmel sam3x8ek 数据表,它应该这样做。
但是,读取此寄存器的两个未记录的副作用:
1) 它启动另一个 Systick 中断!
2) 它将计数标志值重置为 0
数据表中对这两个操作只字未提,2) 没用但不会成为问题 1) 是个阻碍。
禁用 IRQ 的唯一方法是在系统处理程序中,SCB->SHCSR。
但是,如果发生这种情况(硬故障),则会导致崩溃。
可能的解决方案:禁用 systick 的时钟,等待挂起的 IRQ 发生,然后继续。这将确保读取值和读取中断同步,并且会引入一个小的定时错误并在函数本身中花费额外的时间。
经过大约 4-5 小时的调试和与错误或未记录的功能作斗争后,我想出的最佳解决方案是以下代码:
uint32_t get_micros()
{
//__disable_irq(); // does not affect systick
static uint32_t last_value;
volatile uint32_t timestamp = g_timing_tick_ms; // set to volatile to make sure the compiler does not optimize here
volatile uint32_t val = SysTick->VAL;
uint32_t micro_us = (timestamp * 1000 + (1000 - val/84));
if (last_value > micro_us) micro_us+=1000; // Hack: race condition only causes a 1ms delay, this solves it
last_value = micro_us;
//if (SysTick->VAL > val ) micro_us+=1000; // undocmented, causes VAL reset to 0
//if (NVIC_GetPendingIRQ(SysTick_IRQn)) micro_us+=1000; // asf undocumented, does not handle systick (system handler)
//if (SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) micro_us+=1000; // triggerd undocumented systick interrupt
return micro_us; // hardcoded auf 84mhz
}
我希望这可以节省别人我不得不花费的时间。
它引入了一个新变量并保留了最后一个值的运行副本,如果时间开始倒流,它会将值增加 1 毫秒(误差始终为 1 毫秒)。
如果这看起来不够干净:
我能想到的唯一精益解决方案是停止系统时钟并改用计时器。
定时器有更好的文档记录(至少对于基本使用而言)并且它们工作可靠。SAM3 带有 9 个定时器。
关于c - 更新 : SAM3X problems with systick handler and accessing systick value for microsecond resolution,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24592145/
我希望以这样的方式对元素进行动画处理,使其每 1 微秒仅向下移动 1 像素,即 0.001 毫秒。 var microsec = 0.001 $("div").animate({top: "+=1px
我正在用 python 编写脚本,我需要知道我的代码中两点之间相隔了多少毫秒。 程序启动时我有一个全局变量是这样的: from datetime import datetime a=datetime.
我知道有毫秒 (Sleep(milli)) 但是我找不到一个用于微型.. 最佳答案 VS 11 开发预览包括处理线程的标准库部分。所以现在你可以说: std::this_thread::sleep_f
In [25]: datetime.fromtimestamp(15179052980380002/10000000) Out[25]: datetime.datetime(2018, 2, 6, 1
我在我的 centos 7 服务器中调用这个函数。 我发现std::this_thread::sleep_for(chrono::nanoseconds(1))实际上 sleep 了一毫秒,有什么解释
在 libc++ 中,std::chrono::microseconds 定义为 std::chrono::duration 对于 64 位平台,long long 定义导致了与 int64_t 的许
我需要在32位系统的android系统的linux内核中以微秒为单位获取64位的时间戳值 是否有直接的内核导出函数调用来实现它? 我看到 64 位的 jiffies,并且有将 64 位的 jiffie
我想使用 Perl 和/或 Python 来实现以下 JavaScript 伪代码: var c=0; function timedCount() { c=c+1; print("c=" +
我正在使用 unsigned long 来收集性能数据,同时按如下方式分析应用程序 unsigned long time_taken = (unsigned long) ((finish_time_i
我是第一次接触ApacheIoTDB,注意到时间序列时间戳的默认单位是毫秒。我想知道能不能改成纳秒或其他……我怎样才能做到这一点呢?。文件本应包括它,但我还没有找到它。
我是第一次接触ApacheIoTDB,注意到时间序列时间戳的默认单位是毫秒。我想知道能不能改成纳秒或其他……我怎样才能做到这一点呢?。文件本应包括它,但我还没有找到它。
我有一个Future,我想知道它的状态。我想到的是这样的代码: try { // Is that a good idea? Counting on exceptions looks weird
刚刚在我的 Windows (8) 工作站和 AIX 上测试了这段代码: public static void main(String[] args) { System.out
我目前正在努力处理一段非常简单的代码,它表明 ARM GCC 的 1 级优化器以某种方式破坏了一个简单的公式。 这使用标准编译器设置 (O1) 在最新的 Atmel 6.2 Studio 上运行。 A
更新:Timestamp 的构造函数的“秒的分数”参数实际上需要纳秒……我猜是百分之一秒,我的低值被四舍五入了。留题引用.... 我正在努力使用 Oracle 的 C++ 库 - OCCI。总结: 创
我们在 BigQuery 中使用带有标准 SQL 的简单选择查询。 select expiration_date FROM cards 但是,它返回以下错误, 错误:无法返回相对于 Unix 纪元的无
我在从 MySQL 5.7 表中检索 datetime(6) 字段时遇到问题。如果我直接从 mysql 客户端运行它,它可以工作: mysql> SELECT arrival from table1
我是一名优秀的程序员,十分优秀!