Skip to content

Commit bac0d1c

Browse files
committed
Add standalone SDRAM library
This allows running graphical applications on M7. About this, extra care must be take to ensure cache coherency, otherwise all kind of graphical errors could happen. Take a look at #ifdef CORE_CM7 in doom example to see where to place the invalidation and extra delays.
1 parent a0c8560 commit bac0d1c

File tree

13 files changed

+450
-14
lines changed

13 files changed

+450
-14
lines changed

libraries/ENVIE_SDRAM/SDRAM.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include "SDRAM.h"
2+
extern "C" {
3+
#include "ram_internal.h"
4+
}
5+
6+
int SDRAMClass::begin(uint32_t start_address) {
7+
8+
if (FMC_SDRAM_DEVICE->SDCMR == 0x00000000U) {
9+
printf("initializing external ram\n");
10+
bool ret = sdram_init();
11+
if (ret == false) {
12+
return 0;
13+
}
14+
/* Enable MPU for the SDRAM Memory Region to allow non-aligned
15+
accesses (hard-fault otherwise)
16+
Initially disable all access for the entire SDRAM memory space,
17+
then enable access/caching for the size used
18+
*/
19+
20+
mpu_config_start();
21+
mpu_config_region(MPU_REGION_SDRAM1, SDRAM_START_ADDRESS, MPU_CONFIG_DISABLE(0x00, MPU_REGION_SIZE_512MB));
22+
mpu_config_region(MPU_REGION_SDRAM2, SDRAM_START_ADDRESS, MPU_CONFIG_SDRAM(SDRAM_MPU_REGION_SIZE));
23+
mpu_config_end();
24+
}
25+
26+
printf("malloc_addblock: allocate %d bytes\n", SDRAM_END_ADDRESS - start_address);
27+
malloc_addblock((void*)start_address, SDRAM_END_ADDRESS - start_address);
28+
29+
return 1;
30+
}
31+
32+
void* SDRAMClass::malloc(size_t size) {
33+
return ea_malloc(size);
34+
}
35+
36+
void SDRAMClass::free(void* ptr) {
37+
ea_free(ptr);
38+
}
39+
40+
SDRAMClass SDRAM;

libraries/ENVIE_SDRAM/SDRAM.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include "ea_malloc.h"
2+
3+
#ifdef __cplusplus
4+
5+
#include "Arduino.h"
6+
7+
#define SDRAM_END_ADDRESS (0xC0800000)
8+
#define SDRAM_START_ADDRESS (0xC0000000)
9+
10+
class SDRAMClass {
11+
public:
12+
SDRAMClass() {}
13+
int begin(uint32_t start_address = SDRAM_START_ADDRESS);
14+
void* malloc(size_t size);
15+
void free(void* ptr);
16+
private:
17+
void mpu_config_start(void) {
18+
__disable_irq();
19+
}
20+
21+
void mpu_config_region(uint32_t region, uint32_t base_addr, uint32_t attr_size) {
22+
MPU->RNR = region;
23+
MPU->RBAR = base_addr;
24+
MPU->RASR = attr_size;
25+
}
26+
27+
void mpu_config_end(void) {
28+
__ISB();
29+
__DSB();
30+
__DMB();
31+
__enable_irq();
32+
}
33+
};
34+
35+
extern SDRAMClass SDRAM;
36+
37+
#endif
File renamed without changes.
File renamed without changes.

libraries/doom/malloc_freelist.c renamed to libraries/ENVIE_SDRAM/malloc_freelist.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
#include <ll.h>
7-
#include <malloc.h>
7+
#include <ea_malloc.h>
88
#include <memory.h>
99
#include <stdint.h>
1010

libraries/ENVIE_SDRAM/ram_internal.c

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
/*
2+
* This file is part of the OpenMV project.
3+
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
4+
* This work is licensed under the MIT license, see the file LICENSE for details.
5+
*
6+
* SDRAM Driver.
7+
*
8+
*/
9+
#include <stdio.h>
10+
#include <stdbool.h>
11+
#include <string.h>
12+
#include "ram_internal.h"
13+
#include <stm32h7xx_hal.h>
14+
#include <stm32h7xx_hal_sdram.h>
15+
#include <stm32h7xx_hal_mdma.h>
16+
17+
#define SDRAM_TIMEOUT ((uint32_t)0xFFFF)
18+
#define SDRAM_MODEREG_BURST_LENGTH_1 ((uint16_t)0x0000)
19+
#define SDRAM_MODEREG_BURST_LENGTH_2 ((uint16_t)0x0001)
20+
#define SDRAM_MODEREG_BURST_LENGTH_4 ((uint16_t)0x0002)
21+
#define SDRAM_MODEREG_BURST_LENGTH_8 ((uint16_t)0x0004)
22+
#define SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL ((uint16_t)0x0000)
23+
#define SDRAM_MODEREG_BURST_TYPE_INTERLEAVED ((uint16_t)0x0008)
24+
#define SDRAM_MODEREG_CAS_LATENCY_2 ((uint16_t)0x0020)
25+
#define SDRAM_MODEREG_CAS_LATENCY_3 ((uint16_t)0x0030)
26+
#define SDRAM_MODEREG_OPERATING_MODE_STANDARD ((uint16_t)0x0000)
27+
#define SDRAM_MODEREG_WRITEBURST_MODE_PROGRAMMED ((uint16_t)0x0000)
28+
#define SDRAM_MODEREG_WRITEBURST_MODE_SINGLE ((uint16_t)0x0200)
29+
30+
#ifdef FMC_SDRAM_BANK
31+
32+
static void sdram_init_seq(SDRAM_HandleTypeDef
33+
*hsdram, FMC_SDRAM_CommandTypeDef *command);
34+
extern void __fatal_error(const char *msg);
35+
36+
bool sdram_init(void) {
37+
SDRAM_HandleTypeDef hsdram;
38+
FMC_SDRAM_TimingTypeDef SDRAM_Timing;
39+
FMC_SDRAM_CommandTypeDef command;
40+
static MDMA_HandleTypeDef mdma_handle;
41+
GPIO_InitTypeDef gpio_init_structure;
42+
43+
/* Enable FMC clock */
44+
__HAL_RCC_FMC_CLK_ENABLE();
45+
46+
/* Enable chosen MDMAx clock */
47+
__HAL_RCC_MDMA_CLK_ENABLE();
48+
49+
/* Enable GPIOs clock */
50+
__HAL_RCC_GPIOD_CLK_ENABLE();
51+
__HAL_RCC_GPIOE_CLK_ENABLE();
52+
__HAL_RCC_GPIOF_CLK_ENABLE();
53+
__HAL_RCC_GPIOG_CLK_ENABLE();
54+
__HAL_RCC_GPIOH_CLK_ENABLE();
55+
56+
/* Common GPIO configuration */
57+
gpio_init_structure.Mode = GPIO_MODE_AF_PP;
58+
gpio_init_structure.Pull = GPIO_PULLUP;
59+
gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
60+
gpio_init_structure.Alternate = GPIO_AF12_FMC;
61+
62+
/* GPIOD configuration */
63+
gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8| GPIO_PIN_9 | GPIO_PIN_10 |\
64+
GPIO_PIN_14 | GPIO_PIN_15;
65+
66+
67+
HAL_GPIO_Init(GPIOD, &gpio_init_structure);
68+
69+
/* GPIOE configuration */
70+
gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_7| GPIO_PIN_8 | GPIO_PIN_9 |\
71+
GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 |\
72+
GPIO_PIN_15;
73+
74+
HAL_GPIO_Init(GPIOE, &gpio_init_structure);
75+
76+
/* GPIOF configuration */
77+
gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2| GPIO_PIN_3 | GPIO_PIN_4 |\
78+
GPIO_PIN_5 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 |\
79+
GPIO_PIN_15;
80+
81+
HAL_GPIO_Init(GPIOF, &gpio_init_structure);
82+
83+
/* GPIOG configuration */
84+
gpio_init_structure.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 /*| GPIO_PIN_3 */|\
85+
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_15;
86+
HAL_GPIO_Init(GPIOG, &gpio_init_structure);
87+
88+
/* GPIOH configuration */
89+
gpio_init_structure.Pin = GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_5;
90+
HAL_GPIO_Init(GPIOH, &gpio_init_structure);
91+
92+
/* Configure common MDMA parameters */
93+
mdma_handle.Init.Request = MDMA_REQUEST_SW;
94+
mdma_handle.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER;
95+
mdma_handle.Init.Priority = MDMA_PRIORITY_HIGH;
96+
mdma_handle.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
97+
mdma_handle.Init.SourceInc = MDMA_SRC_INC_WORD;
98+
mdma_handle.Init.DestinationInc = MDMA_DEST_INC_WORD;
99+
mdma_handle.Init.SourceDataSize = MDMA_SRC_DATASIZE_WORD;
100+
mdma_handle.Init.DestDataSize = MDMA_DEST_DATASIZE_WORD;
101+
mdma_handle.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE;
102+
mdma_handle.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE;
103+
mdma_handle.Init.DestBurst = MDMA_DEST_BURST_SINGLE;
104+
mdma_handle.Init.BufferTransferLength = 128;
105+
mdma_handle.Init.SourceBlockAddressOffset = 0;
106+
mdma_handle.Init.DestBlockAddressOffset = 0;
107+
108+
mdma_handle.Instance = MDMA_Channel1;
109+
110+
/* Associate the DMA handle */
111+
__HAL_LINKDMA(&hsdram, hmdma, mdma_handle);
112+
113+
/* Deinitialize the stream for new transfer */
114+
HAL_MDMA_DeInit(&mdma_handle);
115+
116+
/* Configure the DMA stream */
117+
HAL_MDMA_Init(&mdma_handle);
118+
119+
/* NVIC configuration for DMA transfer complete interrupt */
120+
HAL_NVIC_SetPriority(MDMA_IRQn, 0x0F, 0);
121+
HAL_NVIC_EnableIRQ(MDMA_IRQn);
122+
123+
/* SDRAM device configuration */
124+
hsdram.Instance = FMC_SDRAM_DEVICE;
125+
/* Timing configuration for 90 Mhz of SD clock frequency (180Mhz/2) */
126+
/* TMRD: 2 Clock cycles */
127+
SDRAM_Timing.LoadToActiveDelay = MICROPY_HW_SDRAM_TIMING_TMRD;
128+
/* TXSR: min=70ns (6x11.90ns) */
129+
SDRAM_Timing.ExitSelfRefreshDelay = MICROPY_HW_SDRAM_TIMING_TXSR;
130+
/* TRAS */
131+
SDRAM_Timing.SelfRefreshTime = MICROPY_HW_SDRAM_TIMING_TRAS;
132+
/* TRC */
133+
SDRAM_Timing.RowCycleDelay = MICROPY_HW_SDRAM_TIMING_TRC;
134+
/* TWR */
135+
SDRAM_Timing.WriteRecoveryTime = MICROPY_HW_SDRAM_TIMING_TWR;
136+
/* TRP */
137+
SDRAM_Timing.RPDelay = MICROPY_HW_SDRAM_TIMING_TRP;
138+
/* TRCD */
139+
SDRAM_Timing.RCDDelay = MICROPY_HW_SDRAM_TIMING_TRCD;
140+
141+
#define _FMC_INIT(x, n) x ## _ ## n
142+
#define FMC_INIT(x, n) _FMC_INIT(x, n)
143+
144+
hsdram.Init.SDBank = FMC_SDRAM_BANK;
145+
hsdram.Init.ColumnBitsNumber = FMC_INIT(FMC_SDRAM_COLUMN_BITS_NUM, MICROPY_HW_SDRAM_COLUMN_BITS_NUM);
146+
hsdram.Init.RowBitsNumber = FMC_INIT(FMC_SDRAM_ROW_BITS_NUM, MICROPY_HW_SDRAM_ROW_BITS_NUM);
147+
hsdram.Init.MemoryDataWidth = FMC_INIT(FMC_SDRAM_MEM_BUS_WIDTH, MICROPY_HW_SDRAM_MEM_BUS_WIDTH);
148+
hsdram.Init.InternalBankNumber = FMC_INIT(FMC_SDRAM_INTERN_BANKS_NUM, MICROPY_HW_SDRAM_INTERN_BANKS_NUM);
149+
hsdram.Init.CASLatency = FMC_INIT(FMC_SDRAM_CAS_LATENCY, MICROPY_HW_SDRAM_CAS_LATENCY);
150+
hsdram.Init.SDClockPeriod = FMC_INIT(FMC_SDRAM_CLOCK_PERIOD, MICROPY_HW_SDRAM_CLOCK_PERIOD);
151+
hsdram.Init.ReadPipeDelay = FMC_INIT(FMC_SDRAM_RPIPE_DELAY, MICROPY_HW_SDRAM_RPIPE_DELAY);
152+
hsdram.Init.ReadBurst = (MICROPY_HW_SDRAM_RBURST) ? FMC_SDRAM_RBURST_ENABLE : FMC_SDRAM_RBURST_DISABLE;
153+
hsdram.Init.WriteProtection = (MICROPY_HW_SDRAM_WRITE_PROTECTION) ? FMC_SDRAM_WRITE_PROTECTION_ENABLE : FMC_SDRAM_WRITE_PROTECTION_DISABLE;
154+
155+
/* Initialize the SDRAM controller */
156+
if(HAL_SDRAM_Init(&hsdram, &SDRAM_Timing) != HAL_OK) {
157+
return false;
158+
}
159+
160+
sdram_init_seq(&hsdram, &command);
161+
return true;
162+
}
163+
164+
static void sdram_init_seq(SDRAM_HandleTypeDef
165+
*hsdram, FMC_SDRAM_CommandTypeDef *command)
166+
{
167+
/* Program the SDRAM external device */
168+
__IO uint32_t tmpmrd =0;
169+
170+
/* Step 3: Configure a clock configuration enable command */
171+
command->CommandMode = FMC_SDRAM_CMD_CLK_ENABLE;
172+
command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK;
173+
command->AutoRefreshNumber = 1;
174+
command->ModeRegisterDefinition = 0;
175+
176+
/* Send the command */
177+
HAL_SDRAM_SendCommand(hsdram, command, 0x1000);
178+
179+
/* Step 4: Insert 100 ms delay */
180+
HAL_Delay(100);
181+
182+
/* Step 5: Configure a PALL (precharge all) command */
183+
command->CommandMode = FMC_SDRAM_CMD_PALL;
184+
command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK;
185+
command->AutoRefreshNumber = 1;
186+
command->ModeRegisterDefinition = 0;
187+
188+
/* Send the command */
189+
HAL_SDRAM_SendCommand(hsdram, command, 0x1000);
190+
191+
/* Step 6 : Configure a Auto-Refresh command */
192+
command->CommandMode = FMC_SDRAM_CMD_AUTOREFRESH_MODE;
193+
command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK;
194+
command->AutoRefreshNumber = MICROPY_HW_SDRAM_AUTOREFRESH_NUM;
195+
command->ModeRegisterDefinition = 0;
196+
197+
/* Send the command */
198+
HAL_SDRAM_SendCommand(hsdram, command, 0x1000);
199+
200+
/* Step 7: Program the external memory mode register */
201+
tmpmrd = (uint32_t)FMC_INIT(SDRAM_MODEREG_BURST_LENGTH, MICROPY_HW_SDRAM_BURST_LENGTH) |
202+
SDRAM_MODEREG_BURST_TYPE_SEQUENTIAL |
203+
FMC_INIT(SDRAM_MODEREG_CAS_LATENCY, MICROPY_HW_SDRAM_CAS_LATENCY) |
204+
SDRAM_MODEREG_OPERATING_MODE_STANDARD |
205+
SDRAM_MODEREG_WRITEBURST_MODE_SINGLE;
206+
207+
command->CommandMode = FMC_SDRAM_CMD_LOAD_MODE;
208+
command->CommandTarget = FMC_SDRAM_CMD_TARGET_BANK;
209+
command->AutoRefreshNumber = 1;
210+
command->ModeRegisterDefinition = tmpmrd;
211+
212+
/* Send the command */
213+
HAL_SDRAM_SendCommand(hsdram, command, 0x1000);
214+
215+
/* Step 8: Set the refresh rate counter
216+
RefreshRate = 64 ms / 8192 cyc = 7.8125 us/cyc
217+
RefreshCycles = 7.8125 us * 90 MHz = 703
218+
According to the formula on p.1665 of the reference manual,
219+
we also need to subtract 20 from the value, so the target
220+
refresh rate is 703 - 20 = 683.
221+
*/
222+
#define REFRESH_COUNT (MICROPY_HW_SDRAM_REFRESH_RATE * 90000 / 8192 - 20)
223+
HAL_SDRAM_ProgramRefreshRate(hsdram, REFRESH_COUNT);
224+
}
225+
226+
bool __attribute__((optimize("O0"))) sdram_test(bool fast) {
227+
uint8_t const pattern = 0xaa;
228+
uint8_t const antipattern = 0x55;
229+
uint8_t *const mem_base = (uint8_t*)sdram_start();
230+
231+
/* test data bus */
232+
for (uint8_t i = 1; i; i <<= 1) {
233+
*mem_base = i;
234+
if (*mem_base != i) {
235+
printf("data bus lines test failed! data (%d)\n", i);
236+
__asm__ volatile ("BKPT");
237+
}
238+
}
239+
240+
/* test address bus */
241+
/* Check individual address lines */
242+
for (uint32_t i = 1; i < HW_SDRAM_SIZE; i <<= 1) {
243+
mem_base[i] = pattern;
244+
if (mem_base[i] != pattern) {
245+
printf("address bus lines test failed! address (%p)\n", &mem_base[i]);
246+
__asm__ volatile ("BKPT");
247+
}
248+
}
249+
250+
/* Check for aliasing (overlaping addresses) */
251+
mem_base[0] = antipattern;
252+
for (uint32_t i = 1; i < HW_SDRAM_SIZE; i <<= 1) {
253+
if (mem_base[i] != pattern) {
254+
printf("address bus overlap %p\n", &mem_base[i]);
255+
__asm__ volatile ("BKPT");
256+
}
257+
}
258+
259+
/* test all ram cells */
260+
if (!fast) {
261+
for (uint32_t i = 0; i < HW_SDRAM_SIZE; ++i) {
262+
mem_base[i] = pattern;
263+
if (mem_base[i] != pattern) {
264+
printf("address bus test failed! address (%p)\n", &mem_base[i]);
265+
__asm__ volatile ("BKPT");
266+
}
267+
}
268+
} else {
269+
memset(mem_base, pattern, HW_SDRAM_SIZE);
270+
}
271+
272+
return true;
273+
}
274+
275+
#endif // FMC_SDRAM_BANK

0 commit comments

Comments
 (0)