STM32 performance test or how fast you can serve input signal

Finally, i decided to try stm32, before i wrote firmwares only for AVR mega family (from now, when i say AVR MCU, i mean AVR mega family MCU), and was scared by tonns of code that you need for simply led blinking on STM32 MCU.
Now i can say, that programming of STM32 not so hard as it looks first time. After i understand logical structure of MCU core and how it work, it become easy.
OK, it was prelude.

Every time when i see comparison between AVR and STM32/STM8 MCU’s i faced with next arguments:
STM MCU’s have lower price, when they have more RAM, FLASH, GPIO pins and work frequency. Looks sweet. Today i want to tell the story about work frequency.

More than the operating frequency of the MCU, the faster it can handle events. STM32 MCU’s have maximum work frequency more than 20MHz (72MHz for STM32F103 that i have), when AVR MCU’s have only 20MHz. Is 72MHz a lot of or not? Two week ago i wanted to know, is it enough ~200MHz of STM32F4 to handle 10MSPS ADC on not? Two weeks ago i think ‘may be’. Now i think ‘not enought’.

Last Friday evening i blow the dust from oscilloscope and made little research.
My basic code poll pin, by software, configured for input and set same signal on output pin, the code is there:

while(1) {
         if( GPIOB->IDR & GPIO_IDR_IDR10 ) {
                        GPIOB->BSRR = GPIO_BSRR_BS8;
         } else {
                        GPIOB->BSRR = GPIO_BSRR_BR8;
         }
}

How many time MCU need to set same level on GPIO pin as on input pin?  May be 140nS (near 10 clocks on 72MHz) will enought?
stm32_delay_O0

Nope. 480ns. It near 34 cycles. The period of 480ns has frequency of 2MHz, may be i forgot to change speed of gpio port?

stm32_debug_toggle_pin_50MHz

Nope. May be i forgot to switch on external quartz or forgot to configure PLL?

stm32_debug_pll

Nope. Hmm, may be optimization can help? Let’s try to switch on -O3:

stm32_delay_O3

Much better, 170 ns. MCU need near 12 cycles, to detect signal and change state of one GPIO speed. Do not forget, MCU just pooling one pin and change state of another, it do not do valuable work.Okay, but what the maximum speed you can get, if you will just change the state of GPIO pin?
Is it possible to hit theoretical maximum of 50MHz (max clock speed of GPIO ports on STM32F103):

stm32_toggle_pin

Just 6MHz. Let me switch on optimization for you:
stm32_toggle_pin_serial_0
Looks like there only 36MHz, let’s look on the code:

stm32_debug_toggle_pin_O3
I can’t see why they can’t reach 50MHz. If you look on disassembler view, you can see, that MCU just store different values on constant address.
In fairness i must to say, if you set cursors on oscilloscope in different manner, you can achieve 50MHz:

stm32_toggle_pin_serial_1
It is hard to say what way of measurement is wrong, i think the second, because first time it is easy to see time of 2 periods. Without a doubt, the second way STM advertising department would have liked more.
BONUS:
A picture where is while cycle occurs:
stm32_toggle_pin_serial_burst

8 comments on “STM32 performance test or how fast you can serve input signal

  • It also depends on the exact chip. As an example, with a F303 and using Timers 1/8 I can clock them at 144Mhz (Using the PLL) and at a 50% duty cycle get 72Mhz. With the F103 using Timer 1 your max clock is 72Mhz and so at a 50% duty cycle get 31Mhz.
    Why only use software? If your going to upgrade to a STM32 chip, take advantage of the peripherals. Different chips have different designs, obviously the STM32 is not designed to toggle a pin fast, it has it timer peripherals to do it instead.
    I will admit I cheat a little, I get a STM32F303C8/B/C(About $5), a Blue Pill dev board, and using hot air, swap the F103 for the F303. Your VDDA is not super clean, but it works.

    Reply
    • Ivan says:

      The main idea was ‘How fast you can change pin state by by software?’, in case of some event which can’t be handled by HW periphery. My expectation was ‘If MCU has one-instruction-way for altering pin state, then it can go do it with core frequency’, but it can’t. I think, that in my case it’s a limitation of bus speed.
      If you need some sort of PWM or just trigger PIN as fast as possible, timer is your best friend.

      Reply
  • Marek says:

    You must to change GPIO speed, it means, that pin can switch from 0 to 1 faster, then you may get higher switching frequency. You can change GPIO speed by set GPIO_InitTypeDef.Speed on GPIO_SPEED_FAST or GPIO_SPEED_HIGH or GPIO_SPEED_FREQ_VERY_HIGH. It depends on type of procesor. You can set speed of pin by CubeMX too.

    Reply
  • These test are not relevant because you are ignoring key peripherals. If you want set some output pin as reaction to setting some input pin, you should use triggered timer and you can reach better performance. The same for “toggling”, with timer cou can reach half of CPU frequency. And the same for 10MSPS ADC, you can manage in/out using DMA and Timer … it is neccesary to use relevant peripheral to get best performance in task…

    Reply
    • Ivan says:

      Right you are, for now I partially agree with you, but idea was to benchmark how fast pin state could by changed by software.

      Reply
      • Kyrre says:

        You can actually precalculate pulse patterns and have the DMA send them to a port.
        By using a DMA read of 32 bits but writing to a 8 bit port you keep the bus mostly free as well to do other stuff. The sneaky thing here is to pass it through for example the SPI loopback adapter to take advantage of the FIFO for buffering and rate control.

        Reply
        • Ivan says:

          Yup, but as stated above, idea was to test raw software performance, without involving any periphery. I’m sorry if it’s unclear from original article.

          Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>