1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
/*
* Aug 2016 Karl Palsson <karlp@tweak.net.au>
*/
#include <stdlib.h>
#include <libopencm3/stm32/flash.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/pwr.h>
#include <libopencm3/stm32/rcc.h>
/* Disco board is b6 */
#define LEDPORT GPIOB
#define LEDPIN GPIO6
#ifndef ARRAY_LENGTH
#define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0]))
#endif
/*
* A set of valid clock configurations with HSI as the source.
* thankfully, L1 allows apb1/apb2 to reach full ahb speed in all cases.
*/
struct rcc_clock_scale valid_hsi_clocks[] = {
{
/* 8Mhz, hsi/div2, max perf for range 2 with 0 ws */
.hpre = RCC_CFGR_HPRE_SYSCLK_DIV2,
.ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV,
.ppre2 = RCC_CFGR_PPRE2_HCLK_NODIV,
.voltage_scale = PWR_SCALE2,
.flash_config = FLASH_ACR_LATENCY_0WS,
.ahb_frequency = 8e6,
.apb1_frequency = 8e6,
.apb2_frequency = 8e6,
},
{
/* Slowest hsi possible */
.hpre = RCC_CFGR_HPRE_SYSCLK_DIV512,
.ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV,
.ppre2 = RCC_CFGR_PPRE1_HCLK_NODIV,
.voltage_scale = PWR_SCALE3,
.flash_config = FLASH_ACR_LATENCY_0WS,
.ahb_frequency = 31250,
.apb1_frequency = 31250,
.apb2_frequency = 31250,
},
{
/* Fastest HSI possible, range 1, 0ws */
.hpre = RCC_CFGR_HPRE_SYSCLK_NODIV,
.ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV,
.ppre2 = RCC_CFGR_PPRE1_HCLK_NODIV,
.voltage_scale = PWR_SCALE1,
.flash_config = FLASH_ACR_LATENCY_0WS,
.ahb_frequency = 16e6,
.apb1_frequency = 16e6,
.apb2_frequency = 16e6,
},
{
/* highest perf for range 3 with 0 wait states */
.hpre = RCC_CFGR_HPRE_SYSCLK_DIV8,
.ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV,
.ppre2 = RCC_CFGR_PPRE1_HCLK_NODIV,
.voltage_scale = PWR_SCALE3,
.flash_config = FLASH_ACR_LATENCY_0WS,
.ahb_frequency = 2e6,
.apb1_frequency = 2e6,
.apb2_frequency = 2e6,
},
{
/* Fastest HSI for range 2, 0ws */
.hpre = RCC_CFGR_HPRE_SYSCLK_NODIV,
.ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV,
.ppre2 = RCC_CFGR_PPRE1_HCLK_NODIV,
.voltage_scale = PWR_SCALE1,
.flash_config = FLASH_ACR_LATENCY_0WS,
.ahb_frequency = 16e6,
.apb1_frequency = 16e6,
.apb2_frequency = 16e6,
},
{
/* highest perf for range 3 */
.hpre = RCC_CFGR_HPRE_SYSCLK_DIV4,
.ppre1 = RCC_CFGR_PPRE1_HCLK_NODIV,
.ppre2 = RCC_CFGR_PPRE1_HCLK_NODIV,
.voltage_scale = PWR_SCALE3,
.flash_config = FLASH_ACR_LATENCY_1WS,
.ahb_frequency = 4e6,
.apb1_frequency = 4e6,
.apb2_frequency = 4e6,
},
};
int shuffled[ARRAY_LENGTH(valid_hsi_clocks)];
/**
* blink led count times, with vile hack * 1000 asm nops
*/
static void hack_blink(int count, int hack)
{
for (int i = 0; i < count; i++) {
gpio_toggle(LEDPORT, LEDPIN);
for (int k = 0; k < hack * 1000; k++) {
__asm__("nop");
}
}
}
/* Stack overflow, wiki, whathaveyou */
/* FIXME oh yeah, did I mention that rand() is always zero? */
static void shuffle_fisher_yates(int *array, size_t n)
{
if (n > 1) {
size_t i;
for (i = 0; i < n - 1; i++) {
size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
int t = array[j];
array[j] = array[i];
array[i] = t;
}
}
}
static void run_clock_set(void)
{
shuffle_fisher_yates(shuffled, ARRAY_LENGTH(shuffled));
struct rcc_clock_scale *clock;
for (size_t i = 0; i < ARRAY_LENGTH(shuffled); i++) {
clock = &valid_hsi_clocks[i];
rcc_clock_setup_hsi(clock);
/** blink quickly/slowly as we run through these */
hack_blink(10, clock->ahb_frequency / (500 * 1000));
hack_blink(4, clock->ahb_frequency / (200 * 1000));
}
}
int main(void)
{
/* Allow leds on any port */
RCC_AHBENR |= 0xff;
gpio_mode_setup(LEDPORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, LEDPIN);
/* blink twice with slow msi reset clock */
hack_blink(4, 60);
/* ok, now, "randomly" sort the list of clocks to try them out */
for (size_t i = 0; i < ARRAY_LENGTH(shuffled); i++) {
shuffled[i] = i;
}
while (1) {
run_clock_set();
}
return 0;
}
|