mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
243 lines
8.1 KiB
C
243 lines
8.1 KiB
C
/*
|
|
* Copyright (C) 2014 Hamburg University of Applied Sciences
|
|
* 2014 PHYTEC Messtechnik GmbH
|
|
* 2015 Eistec AB
|
|
* 2016 Freie Universität Berlin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @brief SPI bus scaler computation
|
|
*
|
|
* This helper tool calculates the needed SPI scaler values for a given APB bus
|
|
* clock speed. The result of the computation must be placed in a board's
|
|
* periph_conf.h for quick reference by the SPI drivers.
|
|
*
|
|
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
|
|
* @author Johann Fischer <j.fischer@phytec.de>
|
|
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
|
|
|
|
/**
|
|
* @brief Targeted SPI bus speed values (pre-defined by RIOT)
|
|
*/
|
|
static uint32_t targets[] = { 100000, 400000, 1000000, 5000000, 10000000 };
|
|
|
|
/**
|
|
* @brief Helper function for finding optimal baud rate scalers.
|
|
*
|
|
* Find the prescaler and scaler settings that will yield a clock frequency
|
|
* as close as possible (but not above) the target frequency, given the module
|
|
* runs at module_clock Hz.
|
|
*
|
|
* Hardware properties (Baud rate configuration):
|
|
* Possible prescalers: 2, 3, 5, 7
|
|
* Possible scalers: 2, 4, 6 (sic!), 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768
|
|
*
|
|
* SCK baud rate = (f_BUS/PBR) x [(1+DBR)/BR]
|
|
*
|
|
* where PBR is the prescaler, BR is the scaler, DBR is the Double BaudRate bit.
|
|
*
|
|
* @note We are not using the DBR bit because it may affect the SCK duty cycle.
|
|
*
|
|
* @param module_clock Module clock frequency (e.g. F_BUS)
|
|
* @param target_clock Desired baud rate
|
|
* @param closest_prescaler pointer where to write the optimal prescaler index.
|
|
* @param closest_scaler pointer where to write the optimal scaler index.
|
|
*
|
|
* @return The actual achieved frequency on success
|
|
* @return Less than 0 on error.
|
|
*/
|
|
|
|
static long find_closest_baudrate_scalers(const uint32_t module_clock, const long target_clock,
|
|
uint8_t *closest_prescaler, uint8_t *closest_scaler)
|
|
{
|
|
uint8_t i;
|
|
uint8_t k;
|
|
long freq;
|
|
static const uint8_t num_scalers = 16;
|
|
static const uint8_t num_prescalers = 4;
|
|
static const int br_scalers[16] = {
|
|
2, 4, 6, 8, 16, 32, 64, 128,
|
|
256, 512, 1024, 2048, 4096, 8192, 16384, 32768
|
|
};
|
|
static const int br_prescalers[4] = {2, 3, 5, 7};
|
|
|
|
long closest_frequency = -1;
|
|
|
|
/* Test all combinations until we arrive close to the target clock */
|
|
for (i = 0; i < num_prescalers; ++i) {
|
|
for (k = 0; k < num_scalers; ++k) {
|
|
freq = module_clock / (br_scalers[k] * br_prescalers[i]);
|
|
|
|
if (freq <= target_clock) {
|
|
/* Found closest lower frequency at this prescaler setting,
|
|
* compare to the best result */
|
|
if (closest_frequency < freq) {
|
|
closest_frequency = freq;
|
|
*closest_scaler = k;
|
|
*closest_prescaler = i;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (closest_frequency < 0) {
|
|
/* Error, no solution found, this line is never reachable with current
|
|
* hardware settings unless a _very_ low target clock is requested.
|
|
* (scaler_max * prescaler_max) = 229376 => target_min@100MHz = 435 Hz*/
|
|
return -1;
|
|
}
|
|
|
|
return closest_frequency;
|
|
}
|
|
|
|
/**
|
|
* @brief Helper function for finding optimal delay scalers.
|
|
*
|
|
* Find the prescaler and scaler settings that will yield a delay timing
|
|
* as close as possible (but not shorter than) the target delay, given the
|
|
* module runs at module_clock Hz.
|
|
*
|
|
* Hardware properties (delay configuration):
|
|
* Possible prescalers: 1, 3, 5, 7
|
|
* Possible scalers: 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536
|
|
*
|
|
* delay = (1/f_BUS) x prescaler x scaler
|
|
*
|
|
* Because we want to do this using only integers, the target_freq parameter is
|
|
* the reciprocal of the delay time.
|
|
*
|
|
* @param module_clock Module clock frequency (e.g. F_BUS)
|
|
* @param target_freq Reciprocal (i.e. 1/t [Hz], frequency) of the desired delay time.
|
|
* @param closest_prescaler pointer where to write the optimal prescaler index.
|
|
* @param closest_scaler pointer where to write the optimal scaler index.
|
|
*
|
|
* @return The actual achieved frequency on success
|
|
* @return Less than 0 on error.
|
|
*/
|
|
static long find_closest_delay_scalers(const uint32_t module_clock, const long target_freq,
|
|
uint8_t *closest_prescaler, uint8_t *closest_scaler)
|
|
{
|
|
uint8_t i;
|
|
uint8_t k;
|
|
long freq;
|
|
int prescaler;
|
|
int scaler;
|
|
static const uint8_t num_scalers = 16;
|
|
static const uint8_t num_prescalers = 4;
|
|
|
|
long closest_frequency = -1;
|
|
|
|
/* Test all combinations until we arrive close to the target clock */
|
|
for (i = 0; i < num_prescalers; ++i) {
|
|
for (k = 0; k < num_scalers; ++k) {
|
|
prescaler = (i * 2) + 1;
|
|
scaler = (1 << (k + 1)); /* 2^(k+1) */
|
|
freq = module_clock / (prescaler * scaler);
|
|
|
|
if (freq <= target_freq) {
|
|
/* Found closest lower frequency at this prescaler setting,
|
|
* compare to the best result */
|
|
if (closest_frequency < freq) {
|
|
closest_frequency = freq;
|
|
*closest_scaler = k;
|
|
*closest_prescaler = i;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (closest_frequency < 0) {
|
|
/* Error, no solution found, this line is never reachable with current
|
|
* hardware settings unless a _very_ low target clock is requested.
|
|
* (scaler_max * prescaler_max) = 458752 */
|
|
return -1;
|
|
}
|
|
|
|
return closest_frequency;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
uint32_t modclk;
|
|
int i;
|
|
|
|
if (argc != 2) {
|
|
printf("usage: %s <module clock>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
modclk = atoi(argv[1]);
|
|
if (modclk == 0) {
|
|
printf("error: invalid input value\n");
|
|
return 1;
|
|
}
|
|
|
|
printf("\nCalculating SPI clock scalers for a module clock of: %iHz\n\n",
|
|
(int)modclk);
|
|
|
|
puts("static const uint32_t spi_clk_config[] = {");
|
|
|
|
for (i = 0; i < ARRAY_SIZE(targets); i++) {
|
|
uint8_t tmp, ptmp;
|
|
long res;
|
|
/* bus clock */
|
|
res = find_closest_baudrate_scalers(modclk, targets[i], &ptmp, &tmp);
|
|
if (res < 0) {
|
|
puts("error: no applicable bus clock scalers could be found!");
|
|
return 1;
|
|
}
|
|
puts(" (");
|
|
printf(" SPI_CTAR_PBR(%i) | SPI_CTAR_BR(%i) | /* -> %iHz */\n",
|
|
(int)ptmp, (int)tmp, (int)res);
|
|
|
|
/* t_csc: chip select to fist clock signal delay */
|
|
if (find_closest_delay_scalers(modclk, targets[i], &ptmp, &tmp) < 0) {
|
|
puts("error: no applicable delay values for t_csc found\n");
|
|
return 1;
|
|
}
|
|
printf(" SPI_CTAR_PCSSCK(%i) | SPI_CTAR_CSSCK(%i) |\n", (int)ptmp, (int)tmp);
|
|
|
|
/* t_asc: delay after last clock signal to release of chip select */
|
|
if (find_closest_delay_scalers(modclk, targets[i], &ptmp, &tmp) < 0) {
|
|
puts("error: no applicable delay values for t_asc found\n");
|
|
return 1;
|
|
}
|
|
printf(" SPI_CTAR_PASC(%i) | SPI_CTAR_ASC(%i) |\n", (int)ptmp, (int)tmp);
|
|
|
|
/* t_psc: delay between release and next assertion of chip select */
|
|
if (find_closest_delay_scalers(modclk, targets[i], &ptmp, &tmp) < 0) {
|
|
puts("error: no applicable delay values for t_csc found\n");
|
|
return 1;
|
|
}
|
|
printf(" SPI_CTAR_PDT(%i) | SPI_CTAR_DT(%i)\n", (int)ptmp, (int)tmp);
|
|
|
|
if (i == ARRAY_SIZE(targets) - 1) {
|
|
puts(" )");
|
|
}
|
|
else {
|
|
puts(" ),");
|
|
}
|
|
}
|
|
puts("};");
|
|
|
|
return 0;
|
|
}
|