STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
emmc_stm32.c
Go to the documentation of this file.
1/*
2 * emmc_port_t on the STM32H723's SDMMC1 for U9 (THGBMTG5D1LBAIL).
3 *
4 * NOT YET COMPILED (the STM32 build comes with PR #165). On the first build,
5 * put the three DMA buffers below in the RAM_D1 section
6 * (EMMC-ARCHITECTURE.md section 7, item 5), and verify:
7 * HAL_MMC_GetCardExtCSD() exists in FW_H7 V1.13.0, and the generated
8 * SDMMC1_IRQHandler calls HAL_MMC_IRQHandler(&hmmc1).
9 *
10 * Datasheet facts this file relies on (KIOXIA rev 1.0, 2021-10-07):
11 * - Identification at <= 400 kHz; data <= 26 MHz until CMD6 selects
12 * high-speed timing (p. 17). We stay in legacy timing: 129 MHz / (2 x 3)
13 * = 21.5 MHz x 4 bits ~ 10 MB/s, far above the ~60 kB/s the log needs.
14 * - HS200/HS400 need VCCQ = 1.8 V; this board's VCCQ is 3V3, so 52 MHz
15 * SDR/DDR is the ceiling anyway (pp. 2, 12).
16 * - RST_n_FUNCTION (EXT_CSD[162]) ships 0: the RST_n pin is ignored. PA8
17 * is released anyway, in case someone enables it (one-time setting).
18 * tRSCA = 200 us from RST_n high to the first command (p. 26).
19 * - Avoid repeated CMD0/CMD1 init loops: read disturb (p. 28). start() is
20 * called once per power-up by the log.
21 *
22 * STM32 facts (DS13313): SDMMC1 is an AXI bus master with its own DMA
23 * (IDMA, section 3.41, Figure 3), so its buffers must be in AXI SRAM
24 * (0x2400_0000), not DTCM; and the Cortex-M7 D-cache must be cleaned before
25 * a DMA write and invalidated after a DMA read, on 32-byte lines.
26 */
27#include "emmc_stm32.h"
28
29#include <string.h>
30
31#include "board.h"
32#include "cmsis_os2.h"
33#include "main.h"
34
35extern MMC_HandleTypeDef hmmc1;
36
37#define LEGACY_MAX_HZ 26000000UL
38#define AXI_SRAM_START 0x24000000UL
39#define AXI_SRAM_END 0x24050000UL /* 320 KB with the default TCM split */
40#define LOG_CHUNKS 4U /* 64 KB: ~1 s of card stall at ~60 kB/s */
41
42/* DMA buffers. They must be in AXI SRAM: CubeMX's GCC script puts .bss in
43 * DTCM, which the SDMMC IDMA cannot reach. BOARD_DMA_BUFFER (board.h) puts
44 * them in the .dma_buffers NOLOAD section in RAM_D1; start() still checks
45 * the addresses. NOLOAD is fine: none of the three is read before it is
46 * written. */
49static uint32_t ext_csd[EMMC_BLOCK_SIZE / 4U] BOARD_DMA_BUFFER;
50
54static volatile bool transfer_ok;
56static uint32_t card_issues;
57static bool card_known;
58
59/* ---- helpers ---------------------------------------------------------- */
60
61static bool dma_reachable(const void* p, uint32_t bytes) {
62 uintptr_t a = (uintptr_t)p;
63 return (a & 31U) == 0U && a >= AXI_SRAM_START && a + bytes <= AXI_SRAM_END;
64}
65
66/* Cache maintenance only when the D-cache is on. CubeMX leaves it off on
67 * this board, and there DCIMVAC/DCCMVAC on AXI SRAM raised an imprecise
68 * BusFault on the rev 1 board (CFSR 0x400, first DMA read at mount). */
69static bool dcache_on(void) {
70 return (SCB->CCR & SCB_CCR_DC_Msk) != 0U;
71}
72
73static uint32_t divider_for(uint32_t kernel_hz, uint32_t max_hz) {
74 /* SDMMC_CK = kernel / (2 x CLKDIV) for CLKDIV > 0 (RM0468). */
75 return (kernel_hz + 2U * max_hz - 1U) / (2U * max_hz);
76}
77
78/* Keep SDMMC_CK within legacy timing whatever CubeMX or HAL left in
79 * CLKDIV. The .ioc has ClockDiv = 0, which is bypass: 129 MHz. */
80static void clamp_clock(void) {
82 uint32_t div = READ_REG(hmmc1.Instance->CLKCR) & SDMMC_CLKCR_CLKDIV;
83 uint32_t hz = div == 0U ? kernel : kernel / (2U * div);
84 if (hz > LEGACY_MAX_HZ) {
85 MODIFY_REG(hmmc1.Instance->CLKCR, SDMMC_CLKCR_CLKDIV,
86 divider_for(kernel, LEGACY_MAX_HZ));
87 }
88}
89
90static emmc_status_t wait_transfer_state(uint32_t deadline) {
91 while (HAL_MMC_GetCardState(&hmmc1) != HAL_MMC_CARD_TRANSFER) {
92 if ((int32_t)(osKernelGetTickCount() - deadline) >= 0) {
93 return EMMC_ERR_TIMEOUT;
94 }
95 osDelay(1U);
96 }
97 return EMMC_OK;
98}
99
100static emmc_status_t finish(uint32_t budget_ms) {
101 uint32_t deadline = osKernelGetTickCount() + budget_ms;
102 if (osSemaphoreAcquire(transfer_done, budget_ms) != osOK) {
103 (void)HAL_MMC_Abort(&hmmc1);
104 return EMMC_ERR_TIMEOUT;
105 }
106 if (!transfer_ok) {
107 return EMMC_ERR_IO;
108 }
109 /* Writes: the card is busy programming until it is back in TRANSFER. */
110 return wait_transfer_state(deadline);
111}
112
113/* ---- the port --------------------------------------------------------- */
114
115static emmc_status_t port_start(void* context, uint32_t* block_count) {
116 (void)context;
117 if (!dma_reachable(chunks, sizeof chunks) ||
118 !dma_reachable(scratch, sizeof scratch)) {
119 return EMMC_ERR_PARAM; /* linker put the buffers in DTCM */
120 }
122 osDelay(1U); /* >= tRSCA 200 us */
123
124 if (hmmc1.State == HAL_MMC_STATE_RESET) {
125 /* main.c did not bring it up (CubeMX "do not generate call"). */
127 hmmc1.Instance = SDMMC1;
128 hmmc1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING;
129 hmmc1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
130 hmmc1.Init.BusWide = SDMMC_BUS_WIDE_4B;
131 hmmc1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
132 hmmc1.Init.ClockDiv = divider_for(kernel, LEGACY_MAX_HZ);
133 if (HAL_MMC_Init(&hmmc1) != HAL_OK) {
134 return EMMC_ERR_IO;
135 }
136 } else if (hmmc1.State != HAL_MMC_STATE_READY) {
137 return EMMC_ERR_NOT_READY;
138 }
139 clamp_clock();
140 /* Hardware flow control: SDMMC_CK stops while the FIFO is full, so a
141 * polling read preempted by a higher-priority task (the sensors start
142 * with recording) cannot overrun (seen on the rev 1 board: EXT_CSD read
143 * failed with HAL_MMC_ERROR_RX_OVERRUN, 0x20). RM0468 SDMMC_CLKCR
144 * HWFC_EN. */
145 SET_BIT(hmmc1.Instance->CLKCR, SDMMC_CLKCR_HWFC_EN);
146
147 /* Polling read of EXT_CSD: once, before any DMA, at bring-up. */
148 if (HAL_MMC_GetCardExtCSD(&hmmc1, ext_csd, 100U) != HAL_OK) {
149 return EMMC_ERR_IO;
150 }
151 emmc_card_parse(hmmc1.CID, (const uint8_t*)ext_csd, &card);
153 card_known = true;
154
155 HAL_MMC_CardInfoTypeDef info;
156 if (HAL_MMC_GetCardInfo(&hmmc1, &info) != HAL_OK ||
157 info.BlockSize != EMMC_BLOCK_SIZE) {
158 return EMMC_ERR_IO;
159 }
160 *block_count = info.LogBlockNbr;
161 return EMMC_OK;
162}
163
164static emmc_status_t port_read(void* context, uint32_t lba, void* buffer,
165 uint32_t count, uint32_t budget_ms) {
166 (void)context;
167 uint32_t bytes = count * EMMC_BLOCK_SIZE;
168 if (count == 0U || !dma_reachable(buffer, bytes)) {
169 return EMMC_ERR_PARAM;
170 }
171 (void)osSemaphoreAcquire(transfer_done, 0U); /* drop a stale release */
172 if (HAL_MMC_ReadBlocks_DMA(&hmmc1, buffer, lba, count) != HAL_OK) {
173 return EMMC_ERR_IO;
174 }
175 emmc_status_t st = finish(budget_ms);
176 if (dcache_on()) {
177 SCB_InvalidateDCache_by_Addr(buffer, (int32_t)bytes);
178 }
179 return st;
180}
181
182static emmc_status_t port_write(void* context, uint32_t lba, const void* buffer,
183 uint32_t count, uint32_t budget_ms) {
184 (void)context;
185 uint32_t bytes = count * EMMC_BLOCK_SIZE;
186 if (count == 0U || !dma_reachable(buffer, bytes)) {
187 return EMMC_ERR_PARAM;
188 }
189 if (dcache_on()) {
190 SCB_CleanDCache_by_Addr((uint32_t*)(uintptr_t)buffer, (int32_t)bytes);
191 }
193 if (HAL_MMC_WriteBlocks_DMA(&hmmc1, (uint8_t*)(uintptr_t)buffer, lba,
194 count) != HAL_OK) {
195 return EMMC_ERR_IO;
196 }
197 return finish(budget_ms);
198}
199
200static void port_lock(void* context) {
201 (void)context;
203}
204
205static void port_unlock(void* context) {
206 (void)context;
208}
209
212
213/* ---- HAL callbacks (weak in stm32h7xx_hal_mmc.c) ------------------------ */
214
215void HAL_MMC_TxCpltCallback(MMC_HandleTypeDef* hmmc) {
216 if (hmmc == &hmmc1) {
217 transfer_ok = true;
219 }
220}
221
222void HAL_MMC_RxCpltCallback(MMC_HandleTypeDef* hmmc) {
224}
225
226void HAL_MMC_ErrorCallback(MMC_HandleTypeDef* hmmc) {
227 if (hmmc == &hmmc1) {
228 transfer_ok = false;
230 }
231}
232
233/* ---- public ------------------------------------------------------------- */
234
245
247 return &flight_log;
248}
249
250const emmc_card_info_t* emmc_stm32_card(uint32_t* issues) {
251 if (!card_known) {
252 return NULL;
253 }
254 if (issues) {
255 *issues = card_issues;
256 }
257 return &card;
258}
259
260void emmc_stm32_writer_task(void* argument) {
261 (void)argument;
262 for (;;) {
264 if (emmc_log_backlog(&flight_log) == 0U) {
265 osDelay(10U); /* nothing sealed: check again in 10 ms */
266 }
267 }
268}
#define EMMC_RST_Pin
Definition main.h:145
#define EMMC_RST_GPIO_Port
Definition main.h:146
#define BOARD_DMA_BUFFER
Definition board.h:36
static board_sleep_info_t info
Definition board_sleep.c:14
osStatus_t osSemaphoreRelease(osSemaphoreId_t semaphore_id)
Release a Semaphore token up to the initial maximum count.
Definition cmsis_os2.c:2011
void * osMutexId_t
Definition cmsis_os2.h:220
osSemaphoreId_t osSemaphoreNew(uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr)
Create and Initialize a Semaphore object.
Definition cmsis_os2.c:1894
void * osSemaphoreId_t
Definition cmsis_os2.h:223
@ osOK
Operation completed successfully.
Definition cmsis_os2.h:199
osStatus_t osSemaphoreAcquire(osSemaphoreId_t semaphore_id, uint32_t timeout)
Acquire a Semaphore token or timeout if no tokens are available.
Definition cmsis_os2.c:1970
uint32_t osKernelGetTickCount(void)
Get the RTOS kernel tick count.
Definition cmsis_os2.c:491
#define osWaitForever
Wait forever timeout value.
Definition cmsis_os2.h:173
osMutexId_t osMutexNew(const osMutexAttr_t *attr)
Create and Initialize a Mutex object.
osStatus_t osMutexAcquire(osMutexId_t mutex_id, uint32_t timeout)
Acquire a Mutex or timeout if it is locked.
osStatus_t osMutexRelease(osMutexId_t mutex_id)
Release a Mutex that was acquired by osMutexAcquire.
osStatus_t osDelay(uint32_t ticks)
Wait for Timeout (Time Delay).
Definition cmsis_os2.c:1094
uint32_t emmc_card_check(const emmc_card_info_t *info)
Definition emmc_card.c:34
void emmc_card_parse(const uint32_t cid[4], const uint8_t ext_csd[512], emmc_card_info_t *out)
Definition emmc_card.c:5
emmc_status_t emmc_log_service(emmc_log_t *log, uint32_t now_ms)
Definition emmc_log.c:424
emmc_status_t emmc_log_init(emmc_log_t *log, const emmc_port_t *port, void *context, const emmc_log_config_t *config, emmc_log_chunk_t *chunks, uint8_t chunk_count, uint8_t *scratch)
Definition emmc_log.c:177
uint8_t emmc_log_backlog(const emmc_log_t *log)
Definition emmc_log.c:252
emmc_log_config_t emmc_log_default_config(void)
Definition emmc_log.c:167
#define EMMC_BLOCK_SIZE
Definition emmc_port.h:20
emmc_status_t
Definition emmc_port.h:22
@ EMMC_ERR_NOT_READY
Definition emmc_port.h:26
@ EMMC_ERR_IO
Definition emmc_port.h:24
@ EMMC_ERR_PARAM
Definition emmc_port.h:27
@ EMMC_ERR_TIMEOUT
Definition emmc_port.h:25
@ EMMC_OK
Definition emmc_port.h:23
#define LOG_CHUNKS
Definition emmc_stm32.c:40
static emmc_status_t port_read(void *context, uint32_t lba, void *buffer, uint32_t count, uint32_t budget_ms)
Definition emmc_stm32.c:164
static emmc_card_info_t card
Definition emmc_stm32.c:55
const emmc_card_info_t * emmc_stm32_card(uint32_t *issues)
Definition emmc_stm32.c:250
#define AXI_SRAM_START
Definition emmc_stm32.c:38
void HAL_MMC_TxCpltCallback(MMC_HandleTypeDef *hmmc)
Definition emmc_stm32.c:215
#define AXI_SRAM_END
Definition emmc_stm32.c:39
static uint32_t card_issues
Definition emmc_stm32.c:56
static emmc_status_t port_write(void *context, uint32_t lba, const void *buffer, uint32_t count, uint32_t budget_ms)
Definition emmc_stm32.c:182
static emmc_status_t finish(uint32_t budget_ms)
Definition emmc_stm32.c:100
void HAL_MMC_ErrorCallback(MMC_HandleTypeDef *hmmc)
Definition emmc_stm32.c:226
static emmc_log_t flight_log
Definition emmc_stm32.c:51
static volatile bool transfer_ok
Definition emmc_stm32.c:54
void HAL_MMC_RxCpltCallback(MMC_HandleTypeDef *hmmc)
Definition emmc_stm32.c:222
static osSemaphoreId_t transfer_done
Definition emmc_stm32.c:53
void emmc_stm32_writer_task(void *argument)
Definition emmc_stm32.c:260
emmc_status_t emmc_stm32_init(void)
Definition emmc_stm32.c:235
static void port_lock(void *context)
Definition emmc_stm32.c:200
static bool dcache_on(void)
Definition emmc_stm32.c:69
static osMutexId_t queue_mutex
Definition emmc_stm32.c:52
static void port_unlock(void *context)
Definition emmc_stm32.c:205
static bool dma_reachable(const void *p, uint32_t bytes)
Definition emmc_stm32.c:61
static const emmc_port_t port
Definition emmc_stm32.c:210
#define LEGACY_MAX_HZ
Definition emmc_stm32.c:37
static emmc_status_t port_start(void *context, uint32_t *block_count)
Definition emmc_stm32.c:115
emmc_log_t * emmc_stm32_log(void)
Definition emmc_stm32.c:246
static uint32_t divider_for(uint32_t kernel_hz, uint32_t max_hz)
Definition emmc_stm32.c:73
static void clamp_clock(void)
Definition emmc_stm32.c:80
static bool card_known
Definition emmc_stm32.c:57
static emmc_status_t wait_transfer_state(uint32_t deadline)
Definition emmc_stm32.c:90
std::vector< daq::SensorDataChunkCollection > chunks
Definition main.cpp:55
int32_t p
Definition flight_e2e.c:78
#define SCB_CCR_DC_Msk
SCB CCR: DC Mask.
#define SCB
SCB configuration struct.
__STATIC_FORCEINLINE void SCB_InvalidateDCache_by_Addr(volatile void *addr, int32_t dsize)
D-Cache Invalidate by address.
__STATIC_FORCEINLINE void SCB_CleanDCache_by_Addr(volatile void *addr, int32_t dsize)
D-Cache Clean by address.
#define SET_BIT(REG, BIT)
Definition stm32h7xx.h:207
#define MODIFY_REG(REG, CLEARMASK, SETMASK)
Definition stm32h7xx.h:219
#define READ_REG(REG)
Definition stm32h7xx.h:217
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
@ GPIO_PIN_SET
#define SDMMC_CLKCR_HWFC_EN
HW Flow Control enable.
#define SDMMC_CLKCR_CLKDIV
Clock divide factor.
#define SDMMC1
uint32_t HAL_RCCEx_GetPeriphCLKFreq(uint64_t PeriphClk)
#define RCC_PERIPHCLK_SDMMC
#define NULL
Definition usbd_def.h:44
static const idle_config_t config
Definition idle_task.c:17
*brief create JSON pointer *sa reference_tokens const string_t & a
Definition json.hpp:13877
size_type count(const key_type &key) const
Definition json.hpp:19264
MMC_HandleTypeDef hmmc1
Definition main.c:54
@ HAL_OK
static uint8_t scratch[BS]