Blame view

build3/ion/src/device/device.cpp 5.93 KB
6663b6c9   adorian   projet complet av...
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
  #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"
  
  #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::reset() {
    CM4.AIRCR()->requestReset();
  }
  
  static inline char hex(uint8_t d) {
    if (d > 9) {
      return 'A'+d-10;
    }
    return '0'+d;
  }
  
  const char * Ion::serialNumber() {
    static char serialNumber[25] = {0};
    if (serialNumber[0] == 0) {
      uint8_t * rawUniqueID = (uint8_t *)0x1FFF7A10;
      for (int i=0; i<12; i++) {
        uint8_t d = *rawUniqueID++;
        serialNumber[2*i] = hex(d >> 4);
        serialNumber[2*i+1] = hex(d & 0xF);
      }
      serialNumber[24] = 0;
    }
    return serialNumber;
  }
  
  // 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 init() {
    initClocks();
  
    // 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"
    }
  
    bool consolePeerConnectedOnBoot = Ion::Console::Device::peerConnected();
  
    initPeripherals();
  
    if (consolePeerConnectedOnBoot) {
      Ion::Device::Bench::run();
    }
  }
  
  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() {
    SWD::Device::shutdown();
    Console::Device::shutdown();
  #if USE_SD_CARD
    SDCard::Device::shutdown();
  #endif
    USB::Device::shutdown();
    Battery::Device::shutdown();
    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);
  
    /* We're using the high-speed internal oscillator as a clock source. It runs
     * at a fixed 16 MHz frequency, but by piping it through the PLL we can derive
     * faster oscillations. Combining default values and a PLLQ of 4 can provide
     * us with a 96 MHz frequency for SYSCLK. */
    RCC.PLLCFGR()->setPLLQ(4);
    RCC.PLLCFGR()->setPLLSRC(RCC::PLLCFGR::PLLSRC::HSI);
    // 96 MHz is too fast for APB1. Divide it by two to reach 48 MHz
    RCC.CFGR()->setPPRE1(RCC::CFGR::AHBRatio::DivideBy2);
  
    // Enable the PLL and wait for it to be ready
    RCC.CR()->setPLLON(true);
    while(!RCC.CR()->getPLLRDY()) {
    }
  
    // Last but not least, use the PLL output as a SYSCLK source
    RCC.CFGR()->setSW(RCC::CFGR::SW::PLL);
    while (RCC.CFGR()->getSWS() != RCC::CFGR::SW::PLL) {
    }
  
    // 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);
  
    // APB1 bus
    // We're using TIM3
    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);
  
    RCC.AHB3ENR()->setFSMCEN(true);
  }
  
  void shutdownClocks() {
    // APB2 bus
    RCC.APB2ENR()->set(0x00008000); // Reset value
  
    // AHB1
    RCC.APB1ENR()->set(0x00000400);
  
    // AHB1 bus
    RCC.AHB1ENR()->set(0); // Reset value
  
    RCC.AHB3ENR()->setFSMCEN(false);
  }
  
  }
  }