nuttx-apps/examples/calib_udelay/calib_udelay_main.c

312 lines
8.1 KiB
C
Raw Normal View History

/****************************************************************************
* apps/examples/calib_udelay/calib_udelay_main.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef CONFIG_EXAMPLES_CALIB_UDELAY_NUM_MEASUREMENTS
# define CONFIG_EXAMPLES_CALIB_UDELAY_NUM_MEASUREMENTS 3
#endif
#ifndef CONFIG_EXAMPLES_CALIB_UDELAY_NUM_RESULTS
# define CONFIG_EXAMPLES_CALIB_UDELAY_NUM_RESULTS 20
#endif
#define DELAY_TEST_ITERS 100000
/****************************************************************************
* Private Types
****************************************************************************/
struct measurement_s
{
int count;
uint64_t nsecs;
};
/****************************************************************************
* Private Functions
****************************************************************************/
static uint64_t gettime_nsecs(void)
{
struct timespec ts;
uint64_t nsecs;
clock_gettime(CLOCK_MONOTONIC, &ts);
nsecs = ts.tv_sec;
nsecs *= 1000 * 1000 * 1000;
nsecs += ts.tv_nsec;
return nsecs;
}
static int compare_measurements(const void *va, const void *vb)
{
const struct measurement_s *a = va;
const struct measurement_s *b = vb;
if (a->nsecs == b->nsecs)
{
return 0;
}
else if (a->nsecs < b->nsecs)
{
return -1;
}
else
{
return 1;
}
}
static void __attribute__((noinline)) calib_udelay_test(uint32_t count)
{
volatile int i;
while (count > 0)
{
for (i = 0; i < DELAY_TEST_ITERS; i++)
{
}
count--;
}
}
static void perform_measurements(int loop_count,
FAR struct measurement_s *measurements,
int num_measurements)
{
int n;
memset(measurements, 0, sizeof(*measurements) * num_measurements);
for (n = 0; n < num_measurements; n++)
{
measurements[n].nsecs = gettime_nsecs();
calib_udelay_test(loop_count);
measurements[n].nsecs = gettime_nsecs() - measurements[n].nsecs;
measurements[n].count = loop_count;
}
qsort(measurements, num_measurements, sizeof(measurements[0]),
compare_measurements);
}
static double getx(FAR struct measurement_s *point)
{
return point->count * (double)DELAY_TEST_ITERS;
}
static double gety(struct measurement_s *point)
{
return point->nsecs;
}
static int linreg(FAR struct measurement_s *point, int num_points,
FAR double *m, FAR double *b, FAR double *r2)
{
double sumx = 0.0;
double sumx2 = 0.0;
double sumxy = 0.0;
double sumy = 0.0;
double sumy2 = 0.0;
double x;
double y;
double denom;
double inv_denom;
int i;
for (i = 0; i < num_points; i++)
{
x = getx(point + i);
y = gety(point + i);
sumx += x;
sumx2 += x * x;
sumxy += x * y;
sumy += y;
sumy2 += y * y;
}
denom = num_points * sumx2 - sumx * sumx;
if (denom == 0)
{
*m = 0;
*b = 0;
if (r2)
{
*r2 = 0;
}
return ERROR;
}
inv_denom = 1.0 / denom;
*m = ((num_points * sumxy) - (sumx * sumy)) * inv_denom;
*b = ((sumy * sumx2) - (sumx * sumxy)) * inv_denom;
if (r2)
{
double term1 = ((num_points * sumxy) - (sumx * sumy));
double term2 = ((num_points * sumx2) - (sumx * sumx));
double term3 = ((num_points * sumy2) - (sumy * sumy));
double term23 = (term2 * term3);
*r2 = 1.0;
if (fabs(term23) > 1e-10)
{
*r2 = (term1 * term1) / term23;
}
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* calib_udelay_main
****************************************************************************/
int main(int argc, FAR char *argv[])
{
const int num_measurements = CONFIG_EXAMPLES_CALIB_UDELAY_NUM_MEASUREMENTS;
const int num_results = CONFIG_EXAMPLES_CALIB_UDELAY_NUM_RESULTS;
const int min_timer_resolution_steps = 3;
const int calibration_step_multiplier = 10;
struct measurement_s measurements[num_measurements];
struct measurement_s result;
struct measurement_s results[num_results];
double iters_per_nsec;
double iters_per_msec;
uint64_t timer_resolution;
double slope_m;
double slope_b;
double slope_r2;
int min_step;
int loop_count;
double duration;
int i;
printf("\n");
sched_lock();
printf("Calibrating timer for main calibration...\n");
usleep(200 * 1000);
/* Find out timer resolution. */
loop_count = 0;
do
{
loop_count++;
perform_measurements(loop_count, measurements, num_measurements);
result = measurements[0];
}
while (result.nsecs == 0);
timer_resolution = result.nsecs;
/* Find first loop count where timer steps five times. */
do
{
loop_count++;
perform_measurements(loop_count, measurements, num_measurements);
result = measurements[0];
}
while (result.nsecs < timer_resolution * min_timer_resolution_steps);
min_step = result.count;
/* Get calibration slope for loop function. */
for (duration = 0, loop_count = min_step, i = 0; i < num_results; i++,
loop_count += min_step * calibration_step_multiplier)
{
duration += (double)num_measurements * loop_count *
timer_resolution * min_timer_resolution_steps / min_step;
}
printf("Performing main calibration for udelay."
"This will take approx. %.3f seconds.\n", duration * 1e-9);
usleep(200 * 1000);
for (loop_count = min_step, i = 0; i < num_results; i++,
loop_count += min_step * calibration_step_multiplier)
{
perform_measurements(loop_count, measurements, num_measurements);
results[i] = measurements[0];
}
if (linreg(results, num_results, &slope_m, &slope_b, &slope_r2) == OK)
{
printf("Calibration slope for udelay:\n"
" Y = m*X + b, where\n"
" X is loop iterations,\n"
" Y is time in nanoseconds,\n"
" b is base overhead,\n"
" m is nanoseconds per loop iteration.\n\n"
" m = %.08f nsec/iter\n"
" b = %.08f nsec\n\n"
" Correlation coefficient, R² = %.4f\n\n",
slope_m, slope_b, slope_r2);
iters_per_nsec = (1.0 / slope_m);
iters_per_msec = iters_per_nsec * 1000 * 1000;
printf("Without overhead, %.8f iterations per nanosecond and %.2f "
"iterations per millisecond.\n\n",
iters_per_nsec, iters_per_msec);
printf("Recommended setting for CONFIG_BOARD_LOOPSPERMSEC:\n"
" CONFIG_BOARD_LOOPSPERMSEC=%.0f\n", ceil(iters_per_msec));
}
else
{
printf("cannot solve\n");
}
sched_unlock();
return 0;
}