Files
nuttx/sched/clock/delay.c
Matteo Golin 504ec6b2c6 arch/udelay: Make common, weak definition of up_*delay functions
Many different architectures re-implemented the exact same code for
`up_*delay` because it was originally written as architecture dependent
code. Busy-waiting can be done regardless of architecture, so this
commit moves that duplicated implementation to a common file with weak
definitions so that individual architectures (see tc32) are still able
to override the definition if needed/desired.

Default implementation is not included if ARCH_TIMER is enabled, since
it is more accurate and provides its own weak definitions to override.

Signed-off-by: Matteo Golin <matteo.golin@gmail.com>
2025-09-29 09:22:56 +08:00

161 lines
5.3 KiB
C

/****************************************************************************
* sched/clock/delay.c
*
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*
****************************************************************************/
/* NOTE: This file contains the default implementation of `up_*delay`
* functions, which perform delays by busy-waiting in a for-loop. This
* functions do not work correctly unless the value
* `CONFIG_BOARD_LOOPSPERMSEC` is configured to the correct value for the
* given board. Its value should be how many loops approximately add up to
* one millisecond of delay.
*
* All one needs to do to override the implementation is override
* `up_udelay`. `up_mdelay` and `up_ndelay` implementations are both
* dependent on `up_udelay`. If you want them to be independent, you can
* also override each `up_*delay` function separately.
*
* WARNING: These functions are not accurate. If the scheduler suspends a
* process in the middle of performing a busy-wait for say, 10ms, and then
* starts it running again, the process will have waited whatever duration
* the busy-wait was meant to be, plus those 10ms. Thus, these functions
* should really only be used in situations where there is no better
* alternative.
*
* The definitions of these functions are 'weak', so if another file linked
* into the binary provides an alternate definition of these functions, that
* definition is what will be used.
*/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/arch.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define CONFIG_BOARD_LOOPSPER100USEC ((CONFIG_BOARD_LOOPSPERMSEC+5)/10)
#define CONFIG_BOARD_LOOPSPER10USEC ((CONFIG_BOARD_LOOPSPERMSEC+50)/100)
#define CONFIG_BOARD_LOOPSPERUSEC ((CONFIG_BOARD_LOOPSPERMSEC+500)/1000)
/****************************************************************************
* Private Functions
****************************************************************************/
static void udelay_coarse(useconds_t microseconds)
{
volatile int i;
/* We'll do this a little at a time because we expect that the
* CONFIG_BOARD_LOOPSPERUSEC is very inaccurate during to truncation in
* the divisions of its calculation. We'll use the largest values that
* we can in order to prevent significant error buildup in the loops.
*/
while (microseconds > 1000)
{
for (i = 0; i < CONFIG_BOARD_LOOPSPERMSEC; i++)
{
}
microseconds -= 1000;
}
while (microseconds > 100)
{
for (i = 0; i < CONFIG_BOARD_LOOPSPER100USEC; i++)
{
}
microseconds -= 100;
}
while (microseconds > 10)
{
for (i = 0; i < CONFIG_BOARD_LOOPSPER10USEC; i++)
{
}
microseconds -= 10;
}
while (microseconds > 0)
{
for (i = 0; i < CONFIG_BOARD_LOOPSPERUSEC; i++)
{
}
microseconds--;
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_mdelay
*
* Description:
* Delay inline for the requested number of milliseconds.
* *** NOT multi-tasking friendly ***
*
****************************************************************************/
void weak_function up_mdelay(unsigned int milliseconds)
{
up_udelay(USEC_PER_MSEC * milliseconds);
}
/****************************************************************************
* Name: up_udelay
*
* Description:
* Delay inline for the requested number of microseconds.
*
* *** NOT multi-tasking friendly ***
*
****************************************************************************/
void weak_function up_udelay(useconds_t microseconds)
{
udelay_coarse(microseconds);
}
/****************************************************************************
* Name: up_ndelay
*
* Description:
* Delay inline for the requested number of nanoseconds.
*
* *** NOT multi-tasking friendly ***
*
****************************************************************************/
void weak_function up_ndelay(unsigned long nanoseconds)
{
up_udelay((nanoseconds + NSEC_PER_USEC - 1) / NSEC_PER_USEC);
}