我正在将一些代码从M3移植到M4,M4使用3个NOP来提供串行输出时钟变化之间的非常短的延迟。M3指令集将NOP的时间定义为1个周期。我注意到M4中的NOP不一定会延迟任何时间。我知道,我将需要禁用编译器优化,但我正在寻找一个低层次的命令,将给予我可靠的,可重复的时间。在实践中,在这种特殊情况下,串行是非常偶尔使用,可能会非常慢,但我仍然想知道最好的方式来获得周期级延迟。
gfttwv5a1#
如果您需要这种非常短但确定性的“至少”延迟,也许您可以考虑使用nop以外的其他指令,这些指令具有确定性的非零延迟。所描述的The Cortex-M4 NOP不一定是耗时的。您可以将其替换为and reg, reg,或者在上下文中粗略地等效于nop的值。或者,在切换GPIO时,您也可以重复I/O指令本身以强制执行状态的最小长度(例如,如果您的GPIO写入指令至少需要5 ns,则重复五次以获得至少25 ns)。如果你在C程序中插入nops,这甚至可以在C中很好地工作(只要重复对端口的写入,如果它应该是volatile,编译器不会删除重复的访问)。当然,这仅适用于非常短的延迟,否则对于短延迟,如其他人所提到的,等待一些定时源的忙碌循环将工作得更好(它们至少需要对定时源进行采样、设置目标并经历一次等待循环所需的时钟)。
nop
and reg, reg
volatile
vwhgwdsa2#
使用 * 周期计数寄存器 *(DWT_CYCCNT)获得高精度定时!
参见下面的stopwatch_delay(ticks)和支持代码,该代码使用STM32的DWT_CYCCNT寄存器,该寄存器专门用于计算实际时钟节拍,地址为0xE 0001004。参见main,其中使用STOPWATCH_START/STOPWATCH_STOP来测量stopwatch_delay(ticks)实际花费的时间,使用CalcNanosecondsFromStopwatch(m_nStart, m_nStop)。修改 * ticks * 输入以进行调整
stopwatch_delay(ticks
main
STOPWATCH_START
STOPWATCH_STOP
stopwatch_delay(ticks)
CalcNanosecondsFromStopwatch(m_nStart, m_nStop)
ticks
uint32_t m_nStart; //DEBUG Stopwatch start cycle counter value uint32_t m_nStop; //DEBUG Stopwatch stop cycle counter value #define DEMCR_TRCENA 0x01000000 /* Core Debug registers */ #define DEMCR (*((volatile uint32_t *)0xE000EDFC)) #define DWT_CTRL (*(volatile uint32_t *)0xe0001000) #define CYCCNTENA (1<<0) #define DWT_CYCCNT ((volatile uint32_t *)0xE0001004) #define CPU_CYCLES *DWT_CYCCNT #define CLK_SPEED 168000000 // EXAMPLE for CortexM4, EDIT as needed #define STOPWATCH_START { m_nStart = *((volatile unsigned int *)0xE0001004);} #define STOPWATCH_STOP { m_nStop = *((volatile unsigned int *)0xE0001004);} static inline void stopwatch_reset(void) { /* Enable DWT */ DEMCR |= DEMCR_TRCENA; *DWT_CYCCNT = 0; /* Enable CPU cycle counter */ DWT_CTRL |= CYCCNTENA; } static inline uint32_t stopwatch_getticks() { return CPU_CYCLES; } static inline void stopwatch_delay(uint32_t ticks) { uint32_t end_ticks = ticks + stopwatch_getticks(); while(1) { if (stopwatch_getticks() >= end_ticks) break; } } uint32_t CalcNanosecondsFromStopwatch(uint32_t nStart, uint32_t nStop) { uint32_t nDiffTicks; uint32_t nSystemCoreTicksPerMicrosec; // Convert (clk speed per sec) to (clk speed per microsec) nSystemCoreTicksPerMicrosec = CLK_SPEED / 1000000; // Elapsed ticks nDiffTicks = nStop - nStart; // Elapsed nanosec = 1000 * (ticks-elapsed / clock-ticks in a microsec) return 1000 * nDiffTicks / nSystemCoreTicksPerMicrosec; } void main(void) { int timeDiff = 0; stopwatch_reset(); // ============================================= // Example: use a delay, and measure how long it took STOPWATCH_START; stopwatch_delay(168000); // 168k ticks is 1ms for 168MHz core STOPWATCH_STOP; timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop); printf("My delay measured to be %d nanoseconds\n", timeDiff); // ============================================= // Example: measure function duration in nanosec STOPWATCH_START; // run_my_function() => do something here STOPWATCH_STOP; timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop); printf("My function took %d nanoseconds\n", timeDiff); }
字符串
a0zr77ik3#
对于任何可靠的计时,我总是建议使用通用计时器。您的部件可能有一个计时器,它能够提供足够高的时钟频率,以给予您的计时需求。对于串行,您是否有理由不能使用相应的串行外设?据我所知,大多数Cortex M3/M4都提供USARTS,I2C和SPI,其中多个还提供SDIO,这应该可以满足大多数需求。如果不可能,this stackoverflow question/answer使用Cortex M3/M4上的周期计数器(如果可用)进行详细说明。您可以获取周期计数器,并向其添加一些,然后轮询它,但我不认为您可以使用此方法实现低于~8个周期的最小延迟。
5lhxktic4#
好吧,首先你必须从RAM运行,而不是闪存,因为闪存的时间将是缓慢的,一个NOP可以采取许多周期。gpio访问至少也需要几个时钟,所以你可能不需要/不想让nops只在gpio上磅。循环结束时的分支也会很明显。你应该写一些指令到ram和分支到它,看看你能多快摆动gpio。底线虽然是,如果你是在这样一个紧张的预算,你的串行时钟是如此接近你的处理器时钟的速度,它很可能你不会得到这个工作与这个处理器。提高处理器中的pll不会改变闪存速度,它会使它变得更糟(相对于处理器时钟)sram应该缩放,但如果你有余量留在你的处理器时钟和功率预算,以支持,然后重复实验在sram与更快的处理器时钟速度。
4条答案
按热度按时间gfttwv5a1#
如果您需要这种非常短但确定性的“至少”延迟,也许您可以考虑使用
nop
以外的其他指令,这些指令具有确定性的非零延迟。所描述的The Cortex-M4 NOP不一定是耗时的。
您可以将其替换为
and reg, reg
,或者在上下文中粗略地等效于nop
的值。或者,在切换GPIO时,您也可以重复I/O指令本身以强制执行状态的最小长度(例如,如果您的GPIO写入指令至少需要5 ns,则重复五次以获得至少25 ns)。如果你在C程序中插入nops,这甚至可以在C中很好地工作(只要重复对端口的写入,如果它应该是volatile
,编译器不会删除重复的访问)。当然,这仅适用于非常短的延迟,否则对于短延迟,如其他人所提到的,等待一些定时源的忙碌循环将工作得更好(它们至少需要对定时源进行采样、设置目标并经历一次等待循环所需的时钟)。
vwhgwdsa2#
使用 * 周期计数寄存器 *(DWT_CYCCNT)获得高精度定时!
参见下面的
stopwatch_delay(ticks
)和支持代码,该代码使用STM32的DWT_CYCCNT寄存器,该寄存器专门用于计算实际时钟节拍,地址为0xE 0001004。参见
main
,其中使用STOPWATCH_START
/STOPWATCH_STOP
来测量stopwatch_delay(ticks)
实际花费的时间,使用CalcNanosecondsFromStopwatch(m_nStart, m_nStop)
。修改 *
ticks
* 输入以进行调整字符串
a0zr77ik3#
对于任何可靠的计时,我总是建议使用通用计时器。您的部件可能有一个计时器,它能够提供足够高的时钟频率,以给予您的计时需求。对于串行,您是否有理由不能使用相应的串行外设?据我所知,大多数Cortex M3/M4都提供USARTS,I2C和SPI,其中多个还提供SDIO,这应该可以满足大多数需求。
如果不可能,this stackoverflow question/answer使用Cortex M3/M4上的周期计数器(如果可用)进行详细说明。您可以获取周期计数器,并向其添加一些,然后轮询它,但我不认为您可以使用此方法实现低于~8个周期的最小延迟。
5lhxktic4#
好吧,首先你必须从RAM运行,而不是闪存,因为闪存的时间将是缓慢的,一个NOP可以采取许多周期。gpio访问至少也需要几个时钟,所以你可能不需要/不想让nops只在gpio上磅。循环结束时的分支也会很明显。你应该写一些指令到ram和分支到它,看看你能多快摆动gpio。
底线虽然是,如果你是在这样一个紧张的预算,你的串行时钟是如此接近你的处理器时钟的速度,它很可能你不会得到这个工作与这个处理器。提高处理器中的pll不会改变闪存速度,它会使它变得更糟(相对于处理器时钟)sram应该缩放,但如果你有余量留在你的处理器时钟和功率预算,以支持,然后重复实验在sram与更快的处理器时钟速度。