The clock frequency means how fast you can run the system clock. Each assembler instruction takes a number of clock cycles to carried out, as documented in the assembler manual. And each C expression will in turn boil down to multiple assembler instructions.
Here is an example, where I made everything volatile
and disabled optimization, just to ensure that directly corresponding assembler gets generated:
volatile int a=1, b=1;
volatile int c = a + b;
Compiling this with gcc for ARM32 gives me roughly something like this for c = a + b
:
ldr r2, [r7, #12]
ldr r3, [r7, #8]
add r3, r3, r2
str r3, [r7, #4]
movs r3, #0
Each of these instructions take a certain amount of clock ticks. So we'll have to look up each and every instruction and count the ticks.
But lets for simplicity's sake imagine an example where they all take 3 clock ticks:
- There is 5 instructions, 5x3 = 15.
t=1/f
so at 72MHz each clock tick takes 13.88ns.
- 15 * 13.88*10^-9 and we end up with 208.33ns to execute the code.
In reality this is much more complex, in particular there might be timing overhead when loading code into registers from RAM or flash. To counter that, CPUs implement things like pipelining, which means that it starts to load several instructions at once. From Cortex M4, simpler forms of branch prediction might also be used. Beyond M4, data and/or instruction cache memories that act as a fast access buffer between the CPU and the slower memories.