Skip to content

Instantly share code, notes, and snippets.

@Klafyvel
Created June 30, 2022 18:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Klafyvel/c933a8609427c81e98c5f3498dc439df to your computer and use it in GitHub Desktop.
Save Klafyvel/c933a8609427c81e98c5f3498dc439df to your computer and use it in GitHub Desktop.
/*
Timing 16bits fixed-point multiplications. The technique used is described
here : https://forum.arduino.cc/t/timing-the-little-things/47247
this should print on an arduino Uno:
loops: 39 clocks: 156
*/
#include <FixedPoints.h>
#include <FixedPointsCommon.h>
SFixed<0, 15> a,b;
void setup() {
Serial.begin(9600);
a = SFixed<0,15>(0.25);
b = SFixed<0,15>(-0.5);
// This call is here so that the compiler does not inline the fixed_mul function
a = a*b;
}
void loop()
{
unsigned long loops = 0;
// TCNT0 is the timer used to compute milliseconds and drive PWM0.
// It is an 8 bit value that increments every 64 clock cycles and
// rolls over from 255 to 0.
//
// We repeatedly run the test code as the timer goes from 156 through 255
// which gives use 64*100 clock cycles.
//
// In practice this works for timing operations that take from 1 to
// hundreds of clock cycles. The results get a little chunky after that
// since the last one will have gone a fair bit past the end period.
//
while( TCNT0 != 155); // wait for 155 to start
while( TCNT0 == 155); // wait until 155 ends
cli(); // turn off interrupts
while( TCNT0 > 150 ) { // that 150 acknowledges we may miss 0
// vvvvvv---- your code to be timed
a = a*b;
// ^^^^^^---- your code to be timed
loops++;
}
sei(); // turn interrupts back on
Serial.print("loops: ");
Serial.print(loops,DEC);
Serial.print(" clocks: ");
Serial.print( (int) (( 100UL*64UL) / loops) - 8 /* empty loop cost */, DEC);
Serial.println();
delay(500);
}
/*
Timing 8bits fixed-point multiplications. The technique used is described
here : https://forum.arduino.cc/t/timing-the-little-things/47247
this should print on an arduino Uno:
loops: 291 clocks: 13
*/
#include <FixedPoints.h>
#include <FixedPointsCommon.h>
SFixed<0, 7> a,b;
void setup() {
Serial.begin(9600);
a = SFixed<0,7>(0.25);
b = SFixed<0,7>(-0.5);
// This call is here so that the compiler does not inline the fixed_mul function
a = a*b;
}
void loop()
{
unsigned long loops = 0;
// TCNT0 is the timer used to compute milliseconds and drive PWM0.
// It is an 8 bit value that increments every 64 clock cycles and
// rolls over from 255 to 0.
//
// We repeatedly run the test code as the timer goes from 156 through 255
// which gives use 64*100 clock cycles.
//
// In practice this works for timing operations that take from 1 to
// hundreds of clock cycles. The results get a little chunky after that
// since the last one will have gone a fair bit past the end period.
//
while( TCNT0 != 155); // wait for 155 to start
while( TCNT0 == 155); // wait until 155 ends
cli(); // turn off interrupts
while( TCNT0 > 150 ) { // that 150 acknowledges we may miss 0
// vvvvvv---- your code to be timed
a = a*b;
// ^^^^^^---- your code to be timed
loops++;
}
sei(); // turn interrupts back on
Serial.print("loops: ");
Serial.print(loops,DEC);
Serial.print(" clocks: ");
Serial.print( (int) (( 100UL*64UL) / loops) - 8 /* empty loop cost */, DEC);
Serial.println();
delay(500);
}
/*
Timing 16bits fixed-point multiplications. The technique used is described
here : https://forum.arduino.cc/t/timing-the-little-things/47247
The fixed point multiplication is adapted from the AVR Instruction-set manual.
this should print on an arduino Uno:
loops: 100 clocks: 56
*/
typedef uint16_t sFixed;
sFixed fixed_mul(sFixed a, sFixed b) {
sFixed result;
asm (
// We need a register that's always zero
"clr r2" "\n\t"
"fmuls %B[a],%B[b]" "\n\t" // Multiply the MSBs
"movw %A[result],__tmp_reg__" "\n\t" // Save the result
"fmul %A[a],%A[b]" "\n\t" // Multiply the LSBs
"adc %A[result],r2" "\n\t" // Do not forget the carry
"movw r18,__tmp_reg__" "\n\t" // The result of the LSBs multipliplication is stored in temporary registers
"fmulsu %B[a],%A[b]" "\n\t" // First crossed product
// This will be reported onto the MSBs of the temporary registers and the LSBs
// of the result registers. So the carry goes to the result's MSB.
"sbc %B[result],r2" "\n\t"
// Now we sum the cross product
"add r19,__tmp_reg__" "\n\t"
"adc %A[result],__zero_reg__" "\n\t"
"adc %B[result],r2" "\n\t"
"fmulsu %B[b],%A[a]" "\n\t" // Second cross product, same as first.
"sbc %B[result],r2" "\n\t"
"add r19,__tmp_reg__" "\n\t"
"adc %A[result],__zero_reg__" "\n\t"
"adc %B[result],r2" "\n\t"
"clr __zero_reg__" "\n\t"
:
[result]"+r"(result):
[a]"a"(a),[b]"a"(b):
"r2","r18","r19"
);
return result;
}
sFixed a,b;
void setup() {
Serial.begin(9600);
a = 0x7333;
b = 0x80FF;
// This call is here so that the compiler does not inline the fixed_mul function
a = fixed_mul(a, b);
}
void loop()
{
unsigned long loops = 0;
// TCNT0 is the timer used to compute milliseconds and drive PWM0.
// It is an 8 bit value that increments every 64 clock cycles and
// rolls over from 255 to 0.
//
// We repeatedly run the test code as the timer goes from 156 through 255
// which gives use 64*100 clock cycles.
//
// In practice this works for timing operations that take from 1 to
// hundreds of clock cycles. The results get a little chunky after that
// since the last one will have gone a fair bit past the end period.
//
while( TCNT0 != 155); // wait for 155 to start
while( TCNT0 == 155); // wait until 155 ends
cli(); // turn off interrupts
while( TCNT0 > 150 ) { // that 150 acknowledges we may miss 0
// vvvvvv---- your code to be timed
a = fixed_mul(a,b);
// ^^^^^^---- your code to be timed
loops++;
}
sei(); // turn interrupts back on
Serial.print("loops: ");
Serial.print(loops,DEC);
Serial.print(" clocks: ");
Serial.print( (int) (( 100UL*64UL) / loops) - 8 /* empty loop cost */, DEC);
Serial.println();
delay(500);
}
/*
Timing 8bits fixed-point multiplications. The technique used is described
here : https://forum.arduino.cc/t/timing-the-little-things/47247
this should print on an arduino Uno:
loops: 355 clocks: 10
*/
typedef uint8_t sFixed;
sFixed fixed_mul(sFixed a, sFixed b) {
sFixed result;
asm (
"fmuls %[a],%[b]" "\n\t"
"mov %[result],__zero_reg__" "\n\t"
"clr __zero_reg__" "\n\t"
:
[result]"+r"(result):
[a]"a"(a),[b]"a"(b)
);
return result;
}
sFixed a,b;
void setup() {
Serial.begin(9600);
a = 0x73;
b = 0x81;
// This call is here so that the compiler does not inline the fixed_mul function
a = fixed_mul(a, b);
Serial.println(a, HEX);
}
void loop()
{
unsigned long loops = 0;
// TCNT0 is the timer used to compute milliseconds and drive PWM0.
// It is an 8 bit value that increments every 64 clock cycles and
// rolls over from 255 to 0.
//
// We repeatedly run the test code as the timer goes from 156 through 255
// which gives use 64*100 clock cycles.
//
// In practice this works for timing operations that take from 1 to
// hundreds of clock cycles. The results get a little chunky after that
// since the last one will have gone a fair bit past the end period.
//
while( TCNT0 != 155); // wait for 155 to start
while( TCNT0 == 155); // wait until 155 ends
cli(); // turn off interrupts
while( TCNT0 > 150 ) { // that 150 acknowledges we may miss 0
// vvvvvv---- your code to be timed
a = fixed_mul(a,b);
// ^^^^^^---- your code to be timed
loops++;
}
sei(); // turn interrupts back on
Serial.print("loops: ");
Serial.print(loops,DEC);
Serial.print(" clocks: ");
Serial.print( (int) (( 100UL*64UL) / loops) - 8 /* empty loop cost */, DEC);
Serial.println();
delay(500);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment