device.cpp
8.23 KB
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
#include "device.h"
#include "regs/regs.h"
extern "C" {
#include <assert.h>
}
#include <ion.h>
#include "led.h"
#include "display.h"
#include "keyboard.h"
#include "battery.h"
#include "sd_card.h"
#include "backlight.h"
#include "console.h"
#include "swd.h"
#include "usb.h"
#include "bench/bench.h"
#include "base64.h"
#define USE_SD_CARD 0
// Public Ion methods
/* TODO: The delay methods 'msleep' and 'usleep' are currently dependent on the
* optimizations chosen by the compiler. To prevent that and to gain in
* precision, we could use the controller cycle counter (Systick). */
void Ion::msleep(long ms) {
for (volatile long i=0; i<8852*ms; i++) {
__asm volatile("nop");
}
}
void Ion::usleep(long us) {
for (volatile long i=0; i<9*us; i++) {
__asm volatile("nop");
}
}
uint32_t Ion::crc32(const uint32_t * data, size_t length) {
bool initialCRCEngineState = RCC.AHB1ENR()->getCRCEN();
RCC.AHB1ENR()->setCRCEN(true);
CRC.CR()->setRESET(true);
const uint32_t * end = data + length;
while (data < end) {
CRC.DR()->set(*data++);
}
uint32_t result = CRC.DR()->get();
RCC.AHB1ENR()->setCRCEN(initialCRCEngineState);
return result;
}
uint32_t Ion::random() {
bool initialRNGEngineState = RCC.AHB2ENR()->getRNGEN();
RCC.AHB2ENR()->setRNGEN(true);
RNG.CR()->setRNGEN(true);
while (RNG.SR()->getDRDY() == 0) {
}
uint32_t result = RNG.DR()->get();
RNG.CR()->setRNGEN(false);
RCC.AHB2ENR()->setRNGEN(initialRNGEngineState);
return result;
}
void Ion::Device::copySerialNumber(char * buffer) {
const unsigned char * rawUniqueID = (const unsigned char *)0x1FFF7A10;
Base64::encode(rawUniqueID, 12, buffer);
buffer[SerialNumberLength] = 0;
}
const char * Ion::serialNumber() {
static char serialNumber[Device::SerialNumberLength + 1] = {0};
if (serialNumber[0] == 0) {
Device::copySerialNumber(serialNumber);
}
return serialNumber;
}
const char * Ion::fccId() {
return "2ALWP-N0100";
}
// Private Ion::Device methods
namespace Ion {
namespace Device {
void initFPU() {
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/BABDBFBJ.html
CM4.CPACR()->setAccess(10, CM4::CPACR::Access::Full);
CM4.CPACR()->setAccess(11, CM4::CPACR::Access::Full);
// FIXME: The pipeline should be flushed at this point
}
void coreReset() {
// Perform a full core reset
CM4.AIRCR()->requestReset();
}
void jumpReset() {
uint32_t * stackPointerAddress = reinterpret_cast<uint32_t *>(0x08000000);
uint32_t * resetHandlerAddress = reinterpret_cast<uint32_t *>(0x08000004);
/* Jump to the reset service routine after having reset the stack pointer.
* Both addresses are fetched from the base of the Flash memory, just like a
* real reset would. These operations should be made at once, otherwise the C
* compiler might emit some instructions that modify the stack inbetween. */
asm volatile (
"msr MSP, %[stackPointer] ; bx %[resetHandler]"
: :
[stackPointer] "r" (*stackPointerAddress),
[resetHandler] "r" (*resetHandlerAddress)
);
}
void init() {
initClocks();
// Ensure right location of interrupt vectors
// The bootloader leaves its own after flashing
SYSCFG.MEMRMP()->setMEM_MODE(SYSCFG::MEMRMP::MemMode::MainFlashmemory);
CM4.VTOR()->setVTOR((void*) 0);
// Put all inputs as Analog Input, No pull-up nor pull-down
// Except for the SWD port (PB3, PA13, PA14)
GPIOA.MODER()->set(0xEBFFFFFF);
GPIOA.PUPDR()->set(0x24000000);
GPIOB.MODER()->set(0xFFFFFFBF);
GPIOB.PUPDR()->set(0x00000000);
for (int g=2; g<5; g++) {
GPIO(g).MODER()->set(0xFFFFFFFF); // All to "Analog"
GPIO(g).PUPDR()->set(0x00000000); // All to "None"
}
#if EPSILON_DEVICE_BENCH
bool consolePeerConnectedOnBoot = Ion::Console::Device::peerConnected();
#endif
initPeripherals();
#if EPSILON_DEVICE_BENCH
if (consolePeerConnectedOnBoot) {
Ion::Device::Bench::run();
}
#endif
}
void shutdown() {
shutdownPeripherals();
shutdownClocks();
}
void initPeripherals() {
Display::Device::init();
Backlight::Device::init();
Keyboard::Device::init();
LED::Device::init();
Battery::Device::init();
USB::Device::init();
#if USE_SD_CARD
SDCard::Device::init();
#endif
Console::Device::init();
SWD::Device::init();
}
void shutdownPeripherals(bool keepLEDAwake) {
SWD::Device::shutdown();
Console::Device::shutdown();
#if USE_SD_CARD
SDCard::Device::shutdown();
#endif
USB::Device::shutdown();
Battery::Device::shutdown();
if (!keepLEDAwake) {
LED::Device::shutdown();
}
Keyboard::Device::shutdown();
Backlight::Device::shutdown();
Display::Device::shutdown();
}
void initClocks() {
/* System clock
* Configure the CPU at 96 MHz, APB2 and USB at 48 MHz. */
/* After reset the Flash runs as fast as the CPU. When we clock the CPU faster
* the flash memory cannot follow and therefore flash memory accesses need to
* wait a little bit.
* The spec tells us that at 2.8V and over 90MHz the flash expects 3 WS. */
FLASH.ACR()->setLATENCY(3);
/* Enable prefetching flash instructions */
/* Fetching instructions increases slightly the power consumption but the
* increase is negligible compared to the screen consumption. */
FLASH.ACR()->setPRFTEN(true);
/* Set flash instruction and data cache */
FLASH.ACR()->setDCEN(true);
FLASH.ACR()->setICEN(true);
/* After reset, the device is using the high-speed internal oscillator (HSI)
* as a clock source, which runs at a fixed 16 MHz frequency. The HSI is not
* accurate enough for reliable USB operation, so we need to use the external
* high-speed oscillator (HSE). */
// Enable the HSE and wait for it to be ready
RCC.CR()->setHSEON(true);
while(!RCC.CR()->getHSERDY()) {
}
/* Given the crystal used on our device, the HSE will oscillate at 25 MHz. By
* piping it through a phase-locked loop (PLL) we can derive other frequencies
* for use in different parts of the system. Combining the default PLL values
* with a PLLM of 25 and a PLLQ of 4 yields both a 96 MHz frequency for SYSCLK
* and the required 48 MHz USB clock. */
// Configure the PLL ratios and use HSE as a PLL input
RCC.PLLCFGR()->setPLLM(25);
RCC.PLLCFGR()->setPLLQ(4);
RCC.PLLCFGR()->setPLLSRC(RCC::PLLCFGR::PLLSRC::HSE);
// 96 MHz is too fast for APB1. Divide it by two to reach 48 MHz
RCC.CFGR()->setPPRE1(RCC::CFGR::APBPrescaler::AHBDividedBy2);
/* If you want to considerably slow down the whole machine uniformely, which
* can be very useful to diagnose performance issues, just uncomment the line
* below. Note that even booting takes a few seconds, so don't be surprised
* if the screen is black for a short while upon booting. */
// RCC.CFGR()->setHPRE(RCC::CFGR::AHBPrescaler::SysClkDividedBy128);
// Enable the PLL and wait for it to be ready
RCC.CR()->setPLLON(true);
while(!RCC.CR()->getPLLRDY()) {
}
// Use the PLL output as a SYSCLK source
RCC.CFGR()->setSW(RCC::CFGR::SW::PLL);
while (RCC.CFGR()->getSWS() != RCC::CFGR::SW::PLL) {
}
// Now that we don't need use it anymore, turn the HSI off
RCC.CR()->setHSION(false);
// Peripheral clocks
// AHB1 bus
// Our peripherals are using GPIO A, B, C, D and E.
// We're not using the CRC nor DMA engines.
class RCC::AHB1ENR ahb1enr(0); // Reset value
ahb1enr.setGPIOAEN(true);
ahb1enr.setGPIOBEN(true);
ahb1enr.setGPIOCEN(true);
ahb1enr.setGPIODEN(true);
ahb1enr.setGPIOEEN(true);
ahb1enr.setDMA2EN(true);
RCC.AHB1ENR()->set(ahb1enr);
// AHB2 bus
RCC.AHB2ENR()->setOTGFSEN(true);
// AHB3 bus
RCC.AHB3ENR()->setFSMCEN(true);
// APB1 bus
// We're using TIM3 for the LEDs
RCC.APB1ENR()->setTIM3EN(true);
RCC.APB1ENR()->setPWREN(true);
// APB2 bus
class RCC::APB2ENR apb2enr(0x00008000); // Reset value
apb2enr.setADC1EN(true);
apb2enr.setSYSCFGEN(true);
#if USE_SD_CARD
apb2enr.setSDIOEN(true);
#endif
RCC.APB2ENR()->set(apb2enr);
}
void shutdownClocks(bool keepLEDAwake) {
// APB2 bus
RCC.APB2ENR()->set(0x00008000); // Reset value
// APB1
class RCC::APB1ENR apb1enr(0x00000400); // Reset value
// AHB1 bus
class RCC::AHB1ENR ahb1enr(0); // Reset value
if (keepLEDAwake) {
apb1enr.setTIM3EN(true);
ahb1enr.setGPIOBEN(true);
ahb1enr.setGPIOCEN(true);
}
RCC.APB1ENR()->set(apb1enr);
RCC.AHB1ENR()->set(ahb1enr);
RCC.AHB3ENR()->setFSMCEN(false);
}
}
}