~ruther/stm32h747i-disco-usb-image-viewer

8313b61dab467a7d9823e32aca9603f804ff3ca8 — Rutherther 3 months ago b41c1c4
feat: implement fmc sdram control
3 files changed, 471 insertions(+), 44 deletions(-)

A include/fmc.h
A src/fmc.c
M src/main.c
A include/fmc.h => include/fmc.h +70 -0
@@ 0,0 1,70 @@
#include <stm32h7xx.h>
#include <stdbool.h>
#include <stdint.h>

#ifndef FMC_H
#define FMC_H

typedef enum {
  SDRAM_BANK1 = 0,
  SDRAM_BANK2 = 1,
} sdram_bank_t;

typedef struct {
  uint32_t* base;
  uint32_t allocated;
} fmc_sdram_t;

typedef struct {
  FMC_Bank1_TypeDef *bank1;
  FMC_Bank2_TypeDef *bank2;
  FMC_Bank3_TypeDef *bank3;
  FMC_Bank5_6_TypeDef *bank5_6;

  fmc_sdram_t sdram[2];
} fmc_t;

typedef struct {
  uint8_t startup_delay_us;
  uint32_t max_sd_clock_hz;
  uint32_t refresh_period_ns;
  uint8_t mode_register_to_active;
  uint8_t exit_self_refresh;
  uint8_t active_to_precharge;
  uint8_t row_cycle;
  uint8_t row_precharge;
  uint8_t row_to_column;
} fmc_sdram_timing_t;

typedef struct {
  uint32_t clock_frequency;
  uint8_t column_bits;
  uint8_t row_bits;
  uint8_t memory_data_width;
  uint8_t internal_banks;
  uint8_t cas_latency;
  bool write_protection;
  bool read_burst;
  uint8_t read_pipe_delay_cycles;
} fmc_sdram_configuration_t;

typedef enum {
  SDRAM_NORMAL_MODE,
  SDRAM_CLK_ENABLE,
  SDRAM_PRECHARGE_ALL,
  SDRAM_AUTOREFRESH,
  SDRAM_LOAD_MODE,
  SDRAM_SELFREFRESH,
  SDRAM_POWERDOWN,
} fmc_sdram_cmd_type_t;

void fmc_init(fmc_t* fmc);

void fmc_sdram_configure(fmc_t *fmc, sdram_bank_t target_bank,
                         fmc_sdram_configuration_t config,
                         fmc_sdram_timing_t timing,
                         uint16_t mode_register);
void fmc_sdram_send_command(fmc_t* fmc, uint16_t bank, fmc_sdram_cmd_type_t cmd, uint16_t argument);
void* fmc_sdram_allocate(fmc_t* fmc, sdram_bank_t bank, uint32_t size);

#endif // FMC_H

A src/fmc.c => src/fmc.c +192 -0
@@ 0,0 1,192 @@
// NOTE: most of the code here is translated from a Rust crate
// https://github.com/stm32-rs/stm32-fmc

#include "fmc.h"
#include "registers.h"
#include "stm32h747xx.h"
#include "delay.h"

void fmc_init(fmc_t *fmc) {
  fmc->bank1 = FMC_Bank1_R;
  fmc->bank2 = FMC_Bank2_R;
  fmc->bank3 = FMC_Bank3_R;
  fmc->bank5_6 = FMC_Bank5_6_R;

  fmc->sdram[0].allocated = 0;
  fmc->sdram[0].base = (uint32_t*)0xC0000000;
  fmc->sdram[1].allocated = 0;
  fmc->sdram[1].base = (uint32_t*)0xD0000000;
}

uint32_t max(uint32_t a, uint32_t b) {
  return (a > b) ? a : b;
}

void fmc_sdram_configure(fmc_t *fmc, sdram_bank_t target_bank, fmc_sdram_configuration_t config,
                         fmc_sdram_timing_t timing, uint16_t mode_register) {
  reg_write_bits(&fmc->bank5_6->SDCR[0],
                 (config.read_pipe_delay_cycles << FMC_SDCRx_RPIPE_Pos) |
                 (config.read_burst << FMC_SDCRx_RBURST_Pos) |
                 (2 << FMC_SDCRx_SDCLK_Pos),
                 FMC_SDCRx_RPIPE_Msk | FMC_SDCRx_RBURST_Msk | FMC_SDCRx_SDCLK_Msk);

  uint8_t internal_banks;
  switch (config.internal_banks) {
  case 2:
    internal_banks = 0;
    break;
  case 4:
    internal_banks = 1;
    break;
  default:
    // Error!
    break;
  }

  uint8_t memory_data_width;
  switch (config.memory_data_width) {
  case 8:
    memory_data_width = 0;
    break;
  case 16:
    memory_data_width = 1;
    break;
  case 32:
    memory_data_width = 2;
    break;
  default:
    // Error!
    break;
  }

  reg_write_bits(&fmc->bank5_6->SDCR[target_bank],
                 (config.write_protection << FMC_SDCRx_WP_Pos) |
                     (config.cas_latency << FMC_SDCRx_CAS_Pos) |
                     (internal_banks << FMC_SDCRx_NB_Pos) |
                     (memory_data_width << FMC_SDCRx_MWID_Pos) |
                     ((config.row_bits - 11) << FMC_SDCRx_NR_Pos) |
                     ((config.column_bits - 8) << FMC_SDCRx_NC_Pos),
                 FMC_SDCRx_WP_Msk | FMC_SDCRx_CAS_Msk |
                 FMC_SDCRx_NB_Msk | FMC_SDCRx_MWID_Msk |
                 FMC_SDCRx_NR_Msk | FMC_SDCRx_NC_Msk);

  // Timing ---- SDTR REGISTER

  // Self refresh >= ACTIVE to PRECHARGE
  uint32_t minimum_self_refresh = timing.active_to_precharge;

  // Write recovery - Self refresh
  uint32_t write_recovery_self_refresh =
      minimum_self_refresh - timing.row_to_column;
  // Write recovery - WRITE command to PRECHARGE command
  uint32_t write_recovery_row_cycle =
      timing.row_cycle - timing.row_to_column - timing.row_precharge;
  uint32_t write_recovery =
      max(write_recovery_self_refresh, write_recovery_row_cycle);

  reg_write_bits(&fmc->bank5_6->SDTR[0],
                 ((timing.row_cycle - 1) << FMC_SDTRx_TRC_Pos) |
                 ((timing.row_precharge - 1) << FMC_SDTRx_TRP_Pos),
                 FMC_SDTRx_TRC_Msk | FMC_SDTRx_TRP_Msk);

  reg_write_bits(&fmc->bank5_6->SDTR[target_bank],
                 ((timing.row_to_column - 1)) << FMC_SDTRx_TRCD_Pos |
                 ((write_recovery - 1)) << FMC_SDTRx_TWR_Pos |
                 ((minimum_self_refresh - 1)) << FMC_SDTRx_TRAS_Pos |
                 ((timing.exit_self_refresh - 1)) << FMC_SDTRx_TXSR_Pos |
                 ((timing.mode_register_to_active - 1)) << FMC_SDTRx_TMRD_Pos,
                 FMC_SDTRx_TRCD_Msk | FMC_SDTRx_TWR_Msk |
                 FMC_SDTRx_TRAS_Msk | FMC_SDTRx_TXSR_Msk |
                 FMC_SDTRx_TMRD_Msk);


  // TODO: is this right?
  reg_set_bits(&fmc->bank1->BTCR[0], FMC_BCR1_FMCEN_Msk);

  fmc_sdram_send_command(fmc, target_bank, SDRAM_CLK_ENABLE, 0);

  DELAY_US(config.startup_delay_us);

  fmc_sdram_send_command(fmc, target_bank, SDRAM_PRECHARGE_ALL, 0);
  fmc_sdram_send_command(fmc, target_bank, SDRAM_AUTOREFRESH, 8);
  fmc_sdram_send_command(fmc, target_bank, SDRAM_LOAD_MODE, mode_register);

  uint64_t refresh_counter_top = (((uint64_t)timing.refresh_period_ns
                                   * (uint64_t)timing.max_sd_clock_hz)
                                  / 1000000000) - 20;

  reg_write_bits(&fmc->bank5_6->SDRTR,
                 (refresh_counter_top << FMC_SDRTR_COUNT_Pos), FMC_SDRTR_COUNT_Msk);
}

void *fmc_sdram_allocate(fmc_t *fmc, sdram_bank_t bank, uint32_t size) {
  size = ((size + 3) / 4) * 4;

  uint32_t *data = (fmc->sdram[bank].base + fmc->sdram[bank].allocated);
  fmc->sdram[bank].allocated += size;

  return (void*)data;
}

void fmc_sdram_send_command(fmc_t *fmc, uint16_t bank, fmc_sdram_cmd_type_t cmd,
                            uint16_t argument) {

  uint32_t cmd_type, number_refresh, mode_reg;

  switch (cmd) {
  case SDRAM_NORMAL_MODE:
    cmd_type = 0;
    number_refresh = 1;
    mode_reg = 0;
    break;
  case SDRAM_CLK_ENABLE:
    cmd_type = 1;
    number_refresh = 1;
    mode_reg = 0;
    break;
  case SDRAM_PRECHARGE_ALL:
    cmd_type = 2;
    number_refresh = 1;
    mode_reg = 0;
    break;
  case SDRAM_AUTOREFRESH:
    cmd_type = 3;
    number_refresh = argument;
    mode_reg = 0;
    break;
  case SDRAM_LOAD_MODE:
    cmd_type = 4;
    number_refresh = 1;
    mode_reg = argument;
    break;
  case SDRAM_SELFREFRESH:
    cmd_type = 5;
    number_refresh = 1;
    mode_reg = 0;
    break;
  case SDRAM_POWERDOWN:
    cmd_type = 6;
    number_refresh = 0;
    mode_reg = 0;
    break;
  }

  uint32_t b1, b2;
  switch (bank) {
  case 0:
    b1 = 1;
    b2 = 0;
    break;
  case 1:
    b1 = 0;
    b2 = 1;
    break;
  }

  fmc->bank5_6->SDCMR =
    (mode_reg << FMC_SDCMR_MRD_Pos) |
    (number_refresh << FMC_SDCMR_NRFS_Pos) |
    (b1 << FMC_SDCMR_CTB1_Pos) |
    (b2 << FMC_SDCMR_CTB2_Pos) |
    (cmd_type << FMC_SDCMR_MODE_Pos);
}

M src/main.c => src/main.c +209 -44
@@ 9,12 9,17 @@
#include "exti.h"
#include "registers.h"
#include "pin.h"
#include "fmc.h"

#define LED1_GPIO GPIOI
#define LED1_PIN 12
/* #define LED2_PIN 13 */

void hard_fault_handler() {
  volatile uint32_t cfsr = SCB->CFSR;
  volatile uint32_t hfsr = SCB->HFSR;
  volatile uint32_t bfar = SCB->BFAR;

  while(1) {}
}



@@ 43,9 48,6 @@ void led_input()
void led_gpio_en()
{
  RCC->AHB4ENR |= (1 << RCC_AHB4ENR_GPIOIEN_Pos);
  volatile uint32_t dummy;
  dummy = RCC->AHB4ENR;
  dummy = RCC->AHB4ENR;
}

void exti15_10_handler(void)


@@ 58,48 60,215 @@ void exti15_10_handler(void)

void main()
{
  clocks_wait_ready(CLOCK_HSI);

  // Clock gating
  RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN;
  RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN |
                  RCC_AHB4ENR_GPIOCEN | RCC_AHB4ENR_GPIODEN |
                  RCC_AHB4ENR_GPIOEEN | RCC_AHB4ENR_GPIOFEN |
                  RCC_AHB4ENR_GPIOGEN | RCC_AHB4ENR_GPIOHEN |
                  RCC_AHB4ENR_GPIOIEN | RCC_AHB4ENR_GPIOJEN;

  systick_configure();

  // Clocks section
  // Enable hsi48 for usb
  RCC->CR |= RCC_CR_HSI48ON;
  while ((RCC->CR & RCC_CR_HSI48RDY) == 0);

  // TODO: pll, system clock switch... too complicated it seems.
  //  Getting hard faults, apparently because of too low voltage
  // Has to be enabled before vco can be changed
  /* PWR->CR3 |= PWR_CR3_LDOEN; */
  /* PWR->CR3 &= ~PWR_CR3_LDOEN; */
  /* reg_write_bits_pos(&PWR->D3CR, 2, PWR_D3CR_VOS_Pos, 3); */

  /* /\* SYSCFG->PWRCR |= SYSCFG_PWRCR_ODEN; *\/ */

  /* while((PWR->CSR1 & PWR_CSR1_ACTVOSRDY) == 0); */
  /* /\* while((PWR->D3CR & PWR_D3CR_VOSRDY) == 0); *\/ */

  /* reg_write_bits(&FLASH->ACR, FLASH_ACR_LATENCY_3WS, FLASH_ACR_LATENCY_Msk); */

  /* // HSI is 64 MHz, not divided */
  /* // Diving by 32 to put to PLL -> 2 MHz */
  /* // DIVN = 360 -> F_VCO = 720 MHz */
  /* // DIVP = 2 -> pll1_p is 360 MHz */
  /* // DIVQ = 8 -> pll1_q is 90 MHz */
  /* // DIVR = 8 -> pll1_r is 90 MHz */
  /* clocks_pll_configure(CLOCK_PLL1, 32, PLL_SOURCE_HSI, */
  /*                      360, 2, 8, 8); */
  /* clocks_pll_enable(CLOCK_PLL1); */
  /* clocks_pll_wait_ready(CLOCK_PLL1, 300); */
  /* clocks_system_clock_source(CLOCK_SOURCE_PLL_1_P_CK, */
  /*                            1, 1, 2, 300); */
  clocks_enable(CLOCK_HSI48);
  clocks_wait_ready(CLOCK_HSI48);

  // Clock gating
  RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN;
  volatile uint32_t dummy;
  dummy = RCC->APB4ENR;
  dummy = RCC->APB4ENR;
  RCC->AHB4ENR |= RCC_AHB4ENR_GPIOAEN | RCC_AHB4ENR_GPIOBEN | RCC_AHB4ENR_GPIOCEN | RCC_AHB4ENR_GPIOHEN | RCC_AHB4ENR_GPIOIEN | RCC_AHB4ENR_GPIOJEN;
  dummy = RCC->AHB4ENR;
  dummy = RCC->AHB4ENR;
  clocks_enable(CLOCK_HSE);
  clocks_wait_ready(CLOCK_HSE);

  // HSE 25 MHz -> divide by 25 -> 1 MHz
  // HSE / 25 * 400 = FVCO = 400 MHz
  // PLL2 R = 200 MHz
  reg_set_bits(&RCC->PLLCFGR, RCC_PLLCFGR_PLL3VCOSEL_Msk);
  clocks_pll_configure(CLOCK_PLL2, 25, PLL_SOURCE_HSE, 400, 2, 2, 2);
  clocks_pll_enable(CLOCK_PLL2);
  clocks_pll_wait_ready(CLOCK_PLL2, 300);

  fmc_t fmc;
  // select pll 2 r as fmc clock
  reg_write_bits(&RCC->D1CCIPR, 2 << (RCC_D1CCIPR_FMCSEL_Pos), RCC_D1CCIPR_FMCSEL_Msk);
  RCC->AHB3ENR |= RCC_AHB3ENR_FMCEN;
  fmc_init(&fmc);

  #define BURST_LENGTH_1 0x0000
  #define BURST_TYPE_SEQUENTIAL 0x0000
  #define CAS_LATENCY_3 0x0030
  #define OPERATING_MODE_STANDARD 0x0000
  #define WRITEBURST_MODE_SINGLE 0x0200
  uint16_t sdram_mode_register = BURST_LENGTH_1 | BURST_TYPE_SEQUENTIAL |
                                 CAS_LATENCY_3 | OPERATING_MODE_STANDARD |
                                 WRITEBURST_MODE_SINGLE;

  fmc_sdram_timing_t sdram_timing = {
    .startup_delay_us =  100,        // 100 µs
    .max_sd_clock_hz =  100000000,   // 100 MHz
    .refresh_period_ns =  15625,     // 64ms / (4096 rows) = 15625ns
    .mode_register_to_active =  2,   // tMRD = 2 cycles
    .exit_self_refresh =  7,         // tXSR = 70ns
    .active_to_precharge =  4,       // tRAS = 42ns
    .row_cycle =  7,                 // tRC = 70ns
    .row_precharge =  2,             // tRP = 18ns
    .row_to_column =  2,             // tRCD = 18ns
  };
  fmc_sdram_configuration_t sdram_config = {
    .column_bits =  9,
    .row_bits =  12,
    .memory_data_width =  32, // 32-bit
    .internal_banks =  4,     // 4 internal banks
    .cas_latency =  3,        // CAS latency = 3
    .write_protection =  false,
    .read_burst =  true,
    .read_pipe_delay_cycles =  0,
  };

  {
    pin_t pin_addr[12];
    pin_t pin_data[32];
    pin_t pin_ba[2];
    pin_t pin_nbl[4];
    pin_t pin_sdcke1;
    pin_t pin_sdclk;
    pin_t pin_sdncas;
    pin_t pin_sdne1;
    pin_t pin_sdras;
    pin_t pin_sdnwe;

    // A0-A11
    pin_init(&pin_addr[0], GPIOF, 0);
    pin_init(&pin_addr[1], GPIOF, 1);
    pin_init(&pin_addr[2], GPIOF, 2);
    pin_init(&pin_addr[3], GPIOF, 3);
    pin_init(&pin_addr[4], GPIOF, 4);
    pin_init(&pin_addr[5], GPIOF, 5);
    pin_init(&pin_addr[6], GPIOF, 12);
    pin_init(&pin_addr[7], GPIOF, 13);
    pin_init(&pin_addr[8], GPIOF, 14);
    pin_init(&pin_addr[9], GPIOF, 15);
    pin_init(&pin_addr[10], GPIOG, 0);
    pin_init(&pin_addr[11], GPIOG, 1);
    // BA0-BA1
    pin_init(&pin_ba[0], GPIOG, 4);
    pin_init(&pin_ba[1], GPIOG, 5);
    // D0-D31
    pin_init(&pin_data[0], GPIOD, 14);
    pin_init(&pin_data[1], GPIOD, 15);
    pin_init(&pin_data[2], GPIOD, 0);
    pin_init(&pin_data[3], GPIOD, 1);
    pin_init(&pin_data[4], GPIOE, 7);
    pin_init(&pin_data[5], GPIOE, 8);
    pin_init(&pin_data[6], GPIOE, 9);
    pin_init(&pin_data[7], GPIOE, 10);
    pin_init(&pin_data[8], GPIOE, 11);
    pin_init(&pin_data[9], GPIOE, 12);
    pin_init(&pin_data[10], GPIOE, 13);
    pin_init(&pin_data[11], GPIOE, 14);
    pin_init(&pin_data[12], GPIOE, 15);
    pin_init(&pin_data[13], GPIOD, 8);
    pin_init(&pin_data[14], GPIOD, 9);
    pin_init(&pin_data[15], GPIOD, 10);
    pin_init(&pin_data[16], GPIOH, 8);
    pin_init(&pin_data[17], GPIOH, 9);
    pin_init(&pin_data[18], GPIOH, 10);
    pin_init(&pin_data[19], GPIOH, 11);
    pin_init(&pin_data[20], GPIOH, 12);
    pin_init(&pin_data[21], GPIOH, 13);
    pin_init(&pin_data[22], GPIOH, 14);
    pin_init(&pin_data[23], GPIOH, 15);
    pin_init(&pin_data[24], GPIOI, 0);
    pin_init(&pin_data[25], GPIOI, 1);
    pin_init(&pin_data[26], GPIOI, 2);
    pin_init(&pin_data[27], GPIOI, 3);
    pin_init(&pin_data[28], GPIOI, 6);
    pin_init(&pin_data[29], GPIOI, 7);
    pin_init(&pin_data[30], GPIOI, 9);
    pin_init(&pin_data[31], GPIOI, 10);

    pin_init(&pin_nbl[0], GPIOE, 0);
    pin_init(&pin_nbl[1], GPIOE, 1);
    pin_init(&pin_nbl[2], GPIOI, 4);
    pin_init(&pin_nbl[3], GPIOI, 5);

    pin_init(&pin_sdcke1, GPIOH, 7);
    pin_init(&pin_sdclk, GPIOG, 8);
    pin_init(&pin_sdncas, GPIOG, 15);
    pin_init(&pin_sdne1, GPIOH, 6);
    pin_init(&pin_sdras, GPIOF, 11);
    pin_init(&pin_sdnwe, GPIOH, 5);

    // A0-A11
    pin_into_alternate_highspeed(&pin_addr[0], 12);
    pin_into_alternate_highspeed(&pin_addr[1], 12);
    pin_into_alternate_highspeed(&pin_addr[2], 12);
    pin_into_alternate_highspeed(&pin_addr[3], 12);
    pin_into_alternate_highspeed(&pin_addr[4], 12);
    pin_into_alternate_highspeed(&pin_addr[5], 12);
    pin_into_alternate_highspeed(&pin_addr[6], 12);
    pin_into_alternate_highspeed(&pin_addr[7], 12);
    pin_into_alternate_highspeed(&pin_addr[8], 12);
    pin_into_alternate_highspeed(&pin_addr[9], 12);
    pin_into_alternate_highspeed(&pin_addr[10], 12);
    pin_into_alternate_highspeed(&pin_addr[11], 12);
    // BA0-BA1
    pin_into_alternate_highspeed(&pin_ba[0], 12);
    pin_into_alternate_highspeed(&pin_ba[1], 12);
    // D0-D31
    pin_into_alternate_highspeed(&pin_data[0], 12);
    pin_into_alternate_highspeed(&pin_data[1], 12);
    pin_into_alternate_highspeed(&pin_data[2], 12);
    pin_into_alternate_highspeed(&pin_data[3], 12);
    pin_into_alternate_highspeed(&pin_data[4], 12);
    pin_into_alternate_highspeed(&pin_data[5], 12);
    pin_into_alternate_highspeed(&pin_data[6], 12);
    pin_into_alternate_highspeed(&pin_data[7], 12);
    pin_into_alternate_highspeed(&pin_data[8], 12);
    pin_into_alternate_highspeed(&pin_data[9], 12);
    pin_into_alternate_highspeed(&pin_data[10], 12);
    pin_into_alternate_highspeed(&pin_data[11], 12);
    pin_into_alternate_highspeed(&pin_data[12], 12);
    pin_into_alternate_highspeed(&pin_data[13], 12);
    pin_into_alternate_highspeed(&pin_data[14], 12);
    pin_into_alternate_highspeed(&pin_data[15], 12);
    pin_into_alternate_highspeed(&pin_data[16], 12);
    pin_into_alternate_highspeed(&pin_data[17], 12);
    pin_into_alternate_highspeed(&pin_data[18], 12);
    pin_into_alternate_highspeed(&pin_data[19], 12);
    pin_into_alternate_highspeed(&pin_data[20], 12);
    pin_into_alternate_highspeed(&pin_data[21], 12);
    pin_into_alternate_highspeed(&pin_data[22], 12);
    pin_into_alternate_highspeed(&pin_data[23], 12);
    pin_into_alternate_highspeed(&pin_data[24], 12);
    pin_into_alternate_highspeed(&pin_data[25], 12);
    pin_into_alternate_highspeed(&pin_data[26], 12);
    pin_into_alternate_highspeed(&pin_data[27], 12);
    pin_into_alternate_highspeed(&pin_data[28], 12);
    pin_into_alternate_highspeed(&pin_data[29], 12);
    pin_into_alternate_highspeed(&pin_data[30], 12);
    pin_into_alternate_highspeed(&pin_data[31], 12);

    pin_into_alternate_highspeed(&pin_nbl[0], 12);
    pin_into_alternate_highspeed(&pin_nbl[1], 12);
    pin_into_alternate_highspeed(&pin_nbl[2], 12);
    pin_into_alternate_highspeed(&pin_nbl[3], 12);

    pin_into_alternate_highspeed(&pin_sdcke1, 12);
    pin_into_alternate_highspeed(&pin_sdclk, 12);
    pin_into_alternate_highspeed(&pin_sdncas, 12);
    pin_into_alternate_highspeed(&pin_sdne1, 12);
    pin_into_alternate_highspeed(&pin_sdras, 12);
    pin_into_alternate_highspeed(&pin_sdnwe, 12);

    fmc_sdram_configure(&fmc, SDRAM_BANK2, sdram_config, sdram_timing, sdram_mode_register);
  }

  uint32_t* sdram_data = (uint32_t*)fmc_sdram_allocate(&fmc, SDRAM_BANK2, 1024);

  for (uint32_t i = 0; i < 1024/4; i++) {
    *(sdram_data + i) = i;
  }

  // Pins init
  pin_t led1;


@@ 118,8 287,6 @@ void main()
  exti_external_interrupt(&exti_wkup, EXTI_GPIOC);
  exti_rising_interrupt(&exti_wkup);
  exti_enable_interrupt(&exti_wkup);
  /* NVIC_SetPriority(EXTI15_10_IRQn, 2); */
  /* NVIC_EnableIRQ(EXTI15_10_IRQn); */

  __enable_irq();



@@ 154,10 321,8 @@ void main()
  pin_into_alternate_highspeed(&ulpi_d6, 10);
  pin_into_alternate_highspeed(&ulpi_d7, 10);

  // TODO: ?
  pin_into_input_highspeed(&otg_hs_overcurrent);

  // TODO: why can't I send string descriptors?
  void *usb_dev = usb_device_init(USB_OTG_HS1, &USB_CLASS_CDC_ACM,
                                  0x1234, 0x1111, u"Frantisek Bohacek",
                                  u"Display", 1, NULL);

Do not follow this link