delays for ARM - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: September 28, 2019, 04:49:54 pm
content: i am writing a library to talk to the hitachi LCD controller. I see that i would need to assert signal for some ns, even at 48MHz that is hardly worthy of doing any timer based go aways and come back's as it's a couple of clock cycles.There is i see no standard delay function for ARM. I expect i need to write my own. Anything to be aware of? I take it I need to run some sort of loop that just goes round in circles "x" amount of times based on F_CPU.

author: tggzzz
author_rank: Super Contributor
timestamp: September 28, 2019, 04:58:49 pm
content: Quote from: Simon on September 28, 2019, 04:49:54 pmIf there are caches or interrupts, they could introduce jitter.

author: Simon
author_rank: Unknown
timestamp: September 28, 2019, 05:07:57 pm
content: Well I don't have a problem with the display data transaction being delayed by interupts. It's a case of a signal needs asserting for a minimum time like 200ns (10 ticks at 48MHz) but if that has to be delayed by an interrupt so be it.

author: Kjelt
author_rank: Super Contributor
timestamp: September 28, 2019, 06:28:26 pm
content: Quote from: Simon on September 28, 2019, 04:49:54 pmYes:1 code that has no real functionality has a large chance to becremoved if you set/enable the optimizer compiler options.To prevent this read or increment some "volatile" declared variable in your loop.Always check the resulting assembly after setting optimizer options, compikers can be very smart these days2 Portability. your loop is tuned to your current cpu frequency.Sometimes you want to lower the power consumption by lowering peripheral and/or cpu frequency and you run into problems. Or for portability in another project with other faster cpu the cpu clock differs. Anyway, The neat way of doing this is setting your cpu speed depending on some sort of #define value . You can use this value in your loop to determine the length to wait. Edit: see you already will use that3 use an rtos or own os that does something usefull in the mean time your peripheral has to wait on the slow secondary hardware to become ready.

author: Simon
author_rank: Unknown
timestamp: September 28, 2019, 06:50:15 pm
content: Well with a 20.8 ns clock a 1µs delay is 48 cycles. what can be done in 48 cycles? other delays are 450ns, 195ns and as low as 20ns.Page 49 of the attached.

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 28, 2019, 06:52:35 pm
content: At the end of this thread:https://www.eevblog.com/forum/programming/while-(youre-down-there-)/two of us gave a solution for what you need.

author: Simon
author_rank: Unknown
timestamp: September 28, 2019, 06:55:11 pm
content: obviously at lower clocks the lower delays are not even a problem.

author: Simon
author_rank: Unknown
timestamp: September 28, 2019, 06:55:55 pm
content: Quote from: SiliconWizard on September 28, 2019, 06:52:35 pmThank you.

author: Jeroen3
author_rank: Super Contributor
timestamp: September 28, 2019, 07:47:51 pm
content: Quote from: Simon on September 28, 2019, 04:49:54 pmThat won't be very precise, and might not compile as intended everywhere. Not that you need it to be, you probably need to have "at least some ns".Aside from delays being an ugly design pattern. You should put a timer in countdown mode running of system clock. This way your delays are precise, reentrant and scalable if you change system prescaler for energy savings.Also, you can wait with WFI.You can use DWT->CYCCNT as suggested above, but it's not enabled by default since uses the DWT, a debug feature, and is missing on the M0.Plus you'd need to consider rollovers, and will have jitter from event to comparison.

author: SiliconWizard
author_rank: Super Contributor
timestamp: September 28, 2019, 08:13:32 pm
content: Quote from: Jeroen3 on September 28, 2019, 07:47:51 pmThe required initialization is mentioned in our code examples. Obviously on parts that don't have DWT, it won't be available.Roll-over with classic *unsigned* arithmetic is actually a non-problem, as long as it doesn't roll-over more than once (which is impossible with the above code, unless it gets interrupted for a very long time, which would make your delay loop completely borked anyway (a 32-bit counter @100MHz will span over 42s). Something that's nice to know. Keep your variables all unsigned here.Consider for instance a roll-over, with a start value of 0xFFFFFFF0 and an end value of say, 0x100. Now what is the 32-bit, unsigned difference (0x100 - 0xFFFFFFF0) ? 0x110. Which is still right.The only problem, as I said, is if it rolls over more than once.Jitter? Yes it will never be cycle accurate, but still much more predictable and easy to use than an hand-written delay loop in assembly or worse, in C (again unless your target is so simple that you can really write code with a cycle-accurate execution, which is not really possible on most ARM CPUs.)

author: ataradov
author_rank: Super Contributor
timestamp: September 28, 2019, 08:35:33 pm
content: Here are busy-wait delay functions I use all the time:Code:__attribute__((noinline, section(".ramfunc")))static void delay_ms(int ms){uint32_t cycles = ms * F_CPU / 3 / 1000;asm volatile ("1: sub %, %, #1 \n"" bne 1b \n":  "+r"(cycles));}__attribute__((noinline, section(".ramfunc")))void delay_cycles(uint32_t cycles){cycles /= 4;asm volatile ("1: sub %, %, #1 \n"" nop \n"" bne 1b \n":  "+l"(cycles));}

author: westfw
author_rank: Super Contributor
timestamp: September 29, 2019, 08:42:08 am
content: At what frequency do ARM Cortex chips start inserting wait-states or other non-deterministic behavior on RAM (.ramfunc, especially)?The 120MHz SAMD51s have Data Cache, but it's not really documented very well when it comes into play...Different CM-n also have different bus structures between CPU and RAM vs Flash...

author: ataradov
author_rank: Super Contributor
timestamp: September 29, 2019, 08:45:38 am
content: There are no wait states at 120 MHz in D51. Data cache is only useful for the flash.Those delays have to be adjusted for the actual core performance. They are not ideal, but they are simple and generally good enough to generate small delays often needed when talking to external devices, like LCDs.

author: Kjelt
author_rank: Super Contributor
timestamp: September 29, 2019, 08:46:25 am
content: Quote from: ataradov on September 28, 2019, 08:35:33 pmThat is also IMO a good approach although portability can become an issue and colleague "c only " programmers might object.

author: Simon
author_rank: Unknown
timestamp: September 29, 2019, 09:06:21 am
content: The idea of a counter based delay is nice but the systic counter while listed on the SAMC datasheet refers me to the arm datasheet for the cpu which I need to find again and the last time I looked at it it was 10 pages of nothing.

author: ataradov
author_rank: Super Contributor
timestamp: September 29, 2019, 09:26:03 am
content: You need to look for Architecture Reference Manual (armv6m in this case), not the core documentation.SysTick is good, but better used for the actual system time than delays.

author: westfw
author_rank: Super Contributor
timestamp: September 29, 2019, 10:10:32 am
content: Quotehttp://infocenter.arm.com/help/topic/com.arm.doc.dui0662b/Babieigh.htmlSysTick is usually used for the relatively slow system tick timer, like the millisecond tick used by Arduino.It's a really simple 24bit down-counter that you can set up with a reload value ("N") that will then cause an interrupt every N clock cycles. So on your 48MHz system, you'd set the reload value to 4800, and you'd get an interrupt every 1ms.I've seen some examples that use SysTick to do a delay, but they usually monopolize it and prevent it from being used more normally. One of my examples linked in the earlier thread showed how to use a free-running systick to do "cylces" delays, as long as the number of cycles was somwhat smaller than the reload value that was being used...

author: Simon
author_rank: Unknown
timestamp: September 29, 2019, 10:19:50 am
content: i can just check the timer value and see if the difference is long enough, it still means that interupts may extend the delay but this is not a problem and if the delay is long enough the interrupt can happen in the mean time with no change to the delay. It's just that really short delays any code will take longer than the delay itself. The 60ns delay is just 3 cycles so it's more like I need a NOP in C for that. the 20ns delay can be ignored as the time it takes to toggle a pin with 20ns tics should cause a delay that long anyway.this is M0+ core

author: Simon
author_rank: Unknown
timestamp: September 29, 2019, 12:25:07 pm
content: Quote from: westfw on September 29, 2019, 10:10:32 amSo that stuff from arm basically complements the Atmel/Microchip datasheet and will be valid on any M0+ core device?Yes I usually do timing the same as you with a timer and compare the value of the timer. A rollovor is not a problem, you just check that the value you expect to be higher is not infact lower than the other valu and if it is add 1 to the left side of it, the subtraction that calculates the ticks elapsed will then work.I fact I have an idea for the fastest possible code. Take the second value and add a 1 to it anyway (if it's a 32 bit processor and a sub 32 bit conter this still means calculations happen in one operation), then carry out the subtraction and only use the bits that correspond to the counter. If the counter did roll over the left most digit will now be 0 anyway. If the left most digit is 1 then the counter did not roll over but ignoring it will make the result right again.It sort of guarantees the code overhead to always be the same but still takes some cycles. Really the only way to have small delays in the order of 2-3 ticks is NOP I guess.

author: ataradov
author_rank: Super Contributor
timestamp: September 29, 2019, 03:39:44 pm
content: Quote from: Simon on September 29, 2019, 12:25:07 pmYes, the hierarchy of documents from less detailed to more detailed:1. DDI0419C ARM V6M Architecture Reference Manual2. DDI0484C Cortex-M0+ Technical Reference Manual3. Device datasheet.Less detailed about a specific device, of course. ARM ARM is a huge documents and contains all the details you need about actual instruction set architecture.The first one describes an abstract architecture. The second one decries a specific core implemented according to that architecture. And the last one describes a specific deice using that core.To get a full understanding of the device architecture you have to read (or rather consult when needed) all of them.There are also separate documents describing debug subsystems.And then there are integration guides, which may be an interesting read, but mostly they are for people actually putting the core into the silicon.

author: techman-001
author_rank: Frequent Contributor
timestamp: October 02, 2019, 02:13:57 am
content: Quote from: westfw on September 29, 2019, 10:10:32 amSystick is very handy, I used it to calibrate my in-lined Arm Thumb Machine Code Forth blocking delay. This is similar to inlined Assembly used with C.: 105ns ( u -- ) \ Range 105.2 ns to 451.8640 seconds @48 MHz clock[$3e01 h,$46c0 h,$d1fc h,] drop;For accurate millisecond delays it's called by the "ms" Word.: ms ( u -- )9505 * 105ns;To generate the Machine Code I write the delay in Assembler..syntax unified.cpu cortex-m0.thumb.textVector_Table: .word 0x20000000 @ stack pointer value when stack is empty 0x20000000ResetVector: .word loop +1 @ Reset Handlerloop: subs r6,r6,#1 @ subtract 1 from r6. delay number will be in R6 as that's the Forth TOSnopbne loop @ when r6 = 0, exit loopCompile it with arm-none-eabi-*, test it live on the actual CPU with GDB over SWD, Then paste the code from the *.list file into the "105ns" word above.00000008 <loop>:8: 3e01 subs r6, #1a: 46c0 nop ; (mov r8, r8)c: d1fc bne.n 8 <loop