STM32H743 FMC与FPGA通信

STM32H743 FMC与FPGA通信
STM32H743 与 FPGA 通过 FMCFlexible Static Memory Controller接口通信是一种高效、常见的异构系统互联方案尤其适用于需要高速、大容量数据交换的场景如工业控制、高速数据采集和信号处理。STM32H743 的 FMC 是一个高度灵活的静态存储器控制器原本设计用于连接 NOR Flash、SRAM、NAND Flash 等存储器。通过将其配置为 SRAM / NOR Flash 模式并连接 FPGA 端的双端口 RAM 或寄存器接口可以实现两者间的并行数据交互。这种方案能让 STM32 像访问内部存储器一样直接通过地址总线读写 FPGA 内的数据延迟极低。硬件连接与配置要点引脚分配通常使用 16 位数据总线、地址总线以及控制信号如片选 NE1、读使能 NOE、写使能 NWE、地址有效 NADV实现连接。电平匹配必须确保 STM32H743 的 FMC 接口电压通常是 3.3V与 FPGA 的 IO 电压兼容。不匹配时需添加电平转换芯片。时序配置FMC 的时序参数如DataSetupTime、AddressSetupTime需根据 FPGA 的响应速度进行配置。时序过紧会导致数据不稳过松则会降低传输速率。关键软件实现与优化地址映射访问STM32 端可将 FPGA 的寄存器映射到 FMC 的地址空间如0x60000000通过宏定义实现便捷的读写操作。#define FPGA_REG(addr) (*(volatile uint16_t *)(0x60000000 | ((addr) 1))) // 写入 FPGA 寄存器 FPGA_REG(0x10) 0xABCD; // 读取 FPGA 寄存器 uint16_t status FPGA_REG(0x11);DMA 传输对于批量数据配合 DMA 使用 FMC 可以大幅降低 CPU 负载实现接近内存到内存的传输效率。MPU 配置是必须的STM32H743 的 Cortex-M7 内核带有缓存直接访问 FMC 地址时可能会因缓存一致性导致数据错乱。必须通过MPU内存保护单元将 FMC 所映射的地址区域配置为Strongly-ordered或Device类型以禁止缓存保证每次读写都直接访问 FPGA 硬件避免数据丢失或错位。FPGA 端逻辑FPGA 端需要实现一个双端口 RAM 作为数据缓冲区并通过地址译码逻辑响应 STM32 的读写请求。同时需注意将 FMC 的异步控制信号同步到 FPGA 的时钟域并对双向数据总线进行三态控制。时序参数怎么算H743 的 HCLK3 最高 240MHzFMC 时钟 HCLK3 / (CLKDivision 1)。通常设 CLKDivision1即 FMC_CLK 120MHz但异步 SRAM 模式实际上不用 FMC_CLK时序完全由 ADDSET/DATAST/BUSTURN 以 HCLK3 周期计算参数含义典型值时间 (HCLK3240MHz)ADDSET地址建立到 NOE/NWE 拉低28.3 nsDATASTNOE/NWE 低电平持续时间4-616.6-25 nsBUSTURN总线恢复读转写或连续读间隔1-24.2-8.3 nsFPGA 端逻辑通常跑 50-100MHz所以 DATAST 至少给 4 个 HCLK3 周期~33ns确保 FPGA 有时间响应。如果 FPGA 逻辑慢用 NWAIT 信号动态插入等待周期。常见踩坑清单数据总线悬空— FPGA 的 data 引脚必须实现三态不读时释放为 z。上面的 Verilog 用assign fmc_data data_drive_en ? read_data_r : 16bz;处理了这个问题。字节对齐— 16 位总线模式下偶地址访问 D[7:0]奇地址访问 D[15:8]。如果你用uint16_t*指针地址必须偶数对齐否则触发 HardFault。Cache 问题— H743 有 D-CacheFMC 地址 0x60000000 默认是 Cacheable 的。如果 FPGA 寄存器值随时变化状态寄存器、FIFO必须把 MPU 把 0x60000000 区域设为 Device 或 Non-Cacheable否则读到的是缓存旧值。__DSB() 屏障— 每次写完 FPGA 寄存器后加__DSB()确保写操作真正到达总线再继续执行后续代码。NWAIT 时序— 如果用 NWAITFMC 需要开启AsynchronousWait FMC_ASYNCHRONOUS_WAIT_ENABLEFPGA 在需要延长时拉低 NWAITFMC 会等待。MDMA/DMA 带宽— 大块数据传输用 MDMA 做 memory-to-memory 搬运比 CPU 逐字写快很多。FMC 的突发写可以达到 ~30-40 MB/s16位120MHz。MPU 配置示例关闭 FMC 区域 Cachevoid MPU_ConfigForFMC(void) { MPU_Region_InitTypeDef MPU_InitStruct {0}; HAL_MPU_Disable(); MPU_InitStruct.Enable MPU_REGION_ENABLE; MPU_InitStruct.BaseAddress 0x60000000; MPU_InitStruct.Size MPU_REGION_SIZE_1MB; MPU_InitStruct.AccessPermission MPU_REGION_FULL_ACCESS; MPU_InitStruct.IsBufferable MPU_ACCESS_NOT_BUFFERABLE; MPU_InitStruct.IsCacheable MPU_ACCESS_NOT_CACHEABLE; // Critical! MPU_InitStruct.IsShareable MPU_ACCESS_SHAREABLE; MPU_InitStruct.Number MPU_REGION_NUMBER0; MPU_InitStruct.TypeExtField MPU_TEX_LEVEL1; MPU_InitStruct.SubRegionDisable 0x00; MPU_InitStruct.DisableExec MPU_INSTRUCTION_ACCESS_DISABLE; HAL_MPU_ConfigRegion(MPU_InitStruct); HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); }代码示例/** * STM32H743 FMC FPGA communication example * FMC configured as SRAM mode, 16-bit data bus, Bank1 NE1 * * Memory map: * Bank1 NE1 base address: 0x6000_0000 * FPGA register space: 0x6000_0000 ~ 0x600F_FFFF (1MB) * * HCLK3 240 MHz, FMC clock HCLK3 / (divider1) * With divider1 (FMC prescaler /2), FMC_CLK 120 MHz */ #include stm32h7xx_hal.h #include string.h /* * FPGA register address definitions * */ /* Bank1, NE1 base address (defined by STM32H743 FMC memory map) */ #define FPGA_BASE_ADDR 0x60000000U /* FPGA internal register offsets (word offset, each 2 bytes since 16-bit bus) */ #define FPGA_REG_ID 0x00 /* Read-only: FPGA firmware ID */ #define FPGA_REG_CTRL 0x02 /* Control register */ #define FPGA_REG_STATUS 0x04 /* Status register */ #define FPGA_REG_DATA_LEN 0x06 /* Data length */ #define FPGA_REG_DATA_PORT 0x08 /* Data FIFO port (read/write) */ #define FPGA_REG_IRQ_EN 0x0A /* Interrupt enable */ #define FPGA_REG_VERSION 0x0C /* Firmware version */ /* Register pointer macros */ #define FPGA_REG(off) (*(volatile uint16_t *)(FPGA_BASE_ADDR (off))) /* * FMC GPIO initialization * D0-D15, A0-A18, NE1, NOE, NWE, NBL0, NBL1, NWAIT * */ static void FMC_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); /* Configure FMC pins: all alternate function, push-pull, speed very high */ GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_PULLUP; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate GPIO_AF12_FMC; /* --- Data bus D0-D15 --- */ /* PD0D0, PD1D1, PD8D8, PD9D9, PD10D10, PD14D14, PD15D15 */ GPIO_InitStruct.Pin GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_14 | GPIO_PIN_15; HAL_GPIO_Init(GPIOD, GPIO_InitStruct); /* PE7D4, PE8D5, PE9D6, PE10D7, PE11D12, PE12D13, PE13D14, PE14D15 */ GPIO_InitStruct.Pin GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14; HAL_GPIO_Init(GPIOE, GPIO_InitStruct); /* PE0NBL0, PE1NBL1 */ GPIO_InitStruct.Pin GPIO_PIN_0 | GPIO_PIN_1; HAL_GPIO_Init(GPIOE, GPIO_InitStruct); /* --- Address bus A0-A18 --- */ /* PF0A0, PF1A1, PF2A2, PF3A3, PF4A4, PF5A5, PF12A6, PF13A7, PF14A8, PF15A9 */ GPIO_InitStruct.Pin GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15; HAL_GPIO_Init(GPIOF, GPIO_InitStruct); /* PG0A10, PG1A11, PG2A12, PG3A13, PG4A14, PG5A15 */ GPIO_InitStruct.Pin GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5; HAL_GPIO_Init(GPIOG, GPIO_InitStruct); /* PG9A16, PG10A17, PG12A18 */ GPIO_InitStruct.Pin GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_12; HAL_GPIO_Init(GPIOG, GPIO_InitStruct); /* --- Control signals --- */ /* PD4NOE, PD5NWE, PD7NE1 */ GPIO_InitStruct.Pin GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7; HAL_GPIO_Init(GPIOD, GPIO_InitStruct); /* NWAIT input on PD6 (optional) */ GPIO_InitStruct.Pin GPIO_PIN_6; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; HAL_GPIO_Init(GPIOD, GPIO_InitStruct); } /* * FMC SRAM mode initialization * Configured for FPGA communication * */ static void FMC_SRAM_Init(void) { FMC_NORSRAM_TimingTypeDef Timing {0}; __HAL_RCC_FMC_CLK_ENABLE(); /* --- Timing configuration --- * HCLK3 240 MHz, FMC prescaler FMC_NORSRAM_FLASH_ACCESS_NO_DIVIDER * FMC_CLK HCLK3 / 2 120 MHz (8.33 ns per cycle) * * For FPGA at ~50 MHz internal logic, typical safe timing: * ADDSET 2 (16.6 ns address setup) * DATAST 4 (33.3 ns data valid time for FPGA to respond) * BUSTURN 2 (16.6 ns bus turnaround) * * Total read cycle ADDSET DATAST 1 7 cycles ≈ 58 ns * Total write cycle ADDSET DATAST 1 7 cycles ≈ 58 ns */ Timing.AddressSetupTime 2; Timing.AddressHoldTime 1; Timing.DataSetupTime 4; Timing.BusTurnAroundDuration 2; Timing.CLKDivision 1; /* FMC_CLK HCLK3/2 */ Timing.DataLatency 2; Timing.AccessMode FMC_ACCESS_MODE_A; /* SRAM mode A */ hsram.Instance FMC_NORSRAM_DEVICE; hsram.Extended FMC_NORSRAM_EXTENDED_DEVICE; hsram.Init.NSBank FMC_NORSRAM_BANK1; /* Use NE1 */ hsram.Init.DataAddressMux FMC_DATA_ADDRESS_MUX_DISABLE; hsram.Init.MemoryType FMC_MEMORY_TYPE_SRAM; hsram.Init.MemoryDataWidth FMC_NORSRAM_MEM_BUS_WIDTH_16; /* 16-bit */ hsram.Init.BurstAccessMode FMC_BURST_ACCESS_MODE_DISABLE; hsram.Init.WaitSignalPolarity FMC_WAIT_SIGNAL_POLARITY_LOW; hsram.Init.WaitSignalActive FMC_WAIT_TIMING_BEFORE_WS; hsram.Init.WriteOperation FMC_WRITE_OPERATION_ENABLE; hsram.Init.WaitSignal FMC_WAIT_SIGNAL_DISABLE; /* Set ENABLE if using NWAIT */ hsram.Init.ExtendedMode FMC_EXTENDED_MODE_DISABLE; hsram.Init.AsynchronousWait FMC_ASYNCHRONOUS_WAIT_DISABLE; hsram.Init.WriteBurst FMC_WRITE_BURST_DISABLE; hsram.Init.ContinuousClock FMC_CONTINUOUS_CLOCK_SYNC_ASYNC; HAL_SRAM_Init(hsram, Timing, Timing); } /* * High-level FPGA access functions * */ /** * Write a 16-bit value to FPGA register at given offset * param offset Register offset (must be even for 16-bit bus) * param value 16-bit data to write */ void FPGA_WriteReg(uint16_t offset, uint16_t value) { FPGA_REG(offset) value; __DSB(); /* Data Synchronization Barrier - ensure write completes */ } /** * Read a 16-bit value from FPGA register at given offset * param offset Register offset (must be even for 16-bit bus) * return 16-bit data read from FPGA */ uint16_t FPGA_ReadReg(uint16_t offset) { uint16_t val FPGA_REG(offset); __DSB(); return val; } /** * Burst write data block to FPGA * Uses pointer increment for fast transfer * param offset Starting register offset (FIFO port recommended) * param buf Source data buffer * param len Number of 16-bit words to write */ void FPGA_BurstWrite(uint16_t offset, const uint16_t *buf, uint32_t len) { volatile uint16_t *p (volatile uint16_t *)(FPGA_BASE_ADDR offset); for (uint32_t i 0; i len; i) { *p buf[i]; } __DSB(); } /** * Burst read data block from FPGA * param offset Starting register offset (FIFO port recommended) * param buf Destination buffer * param len Number of 16-bit words to read */ void FPGA_BurstRead(uint16_t offset, uint16_t *buf, uint32_t len) { volatile uint16_t *p (volatile uint16_t *)(FPGA_BASE_ADDR offset); for (uint32_t i 0; i len; i) { buf[i] *p; } __DSB(); } /** * Use MDMA for high-speed DMA transfer to FPGA * FMC can be MDMA destination/source in memory-to-memory mode */ void FPGA_MDMA_Write(uint16_t offset, const uint16_t *src, uint32_t len) { /* Configure MDMA channel for memory-to-memory transfer */ /* Source: internal SRAM buffer */ /* Destination: FMC bank1 address (FPGA) */ /* This achieves near-maximum FMC bandwidth */ extern MDMA_HandleTypeDef hmdma_fpga; HAL_MDMA_Start(hmdma_fpga, (uint32_t)src, (uint32_t)(FPGA_BASE_ADDR offset), len * 2, /* byte count */ 128); /* 128-byte block transfer */ /* Wait for transfer complete */ HAL_MDMA_PollForTransfer(hmdma_fpga, HAL_MDMA_FULL_TRANSFER, 1000); } /* * Example: Initialize FPGA and transfer data * */ void FPGA_Example(void) { uint16_t fpga_id; uint16_t tx_buf[256]; uint16_t rx_buf[256]; /* Initialize FMC GPIO and controller */ FMC_GPIO_Init(); FMC_SRAM_Init(); /* Wait for FPGA to be ready */ HAL_Delay(100); /* Read FPGA ID to verify connection */ fpga_id FPGA_ReadReg(FPGA_REG_ID); if (fpga_id ! 0xFPGA) { /* Replace with your expected ID */ /* Handle error: FPGA not responding */ return; } /* Prepare test data */ for (int i 0; i 256; i) { tx_buf[i] (uint16_t)(i 0xFFFF); } /* Write 256 words to FPGA data FIFO */ FPGA_WriteReg(FPGA_REG_DATA_LEN, 256); FPGA_BurstWrite(FPGA_REG_DATA_PORT, tx_buf, 256); /* Trigger processing */ FPGA_WriteReg(FPGA_REG_CTRL, 0x0001); /* Start bit */ __DSB(); /* Wait for done (poll status register) */ while ((FPGA_ReadReg(FPGA_REG_STATUS) 0x01) 0) { /* Timeout handling recommended */ } /* Read back processed data */ FPGA_BurstRead(FPGA_REG_DATA_PORT, rx_buf, 256); /* Verify data integrity */ for (int i 0; i 256; i) { if (rx_buf[i] ! tx_buf[i]) { /* Data mismatch */ } } }

最新新闻

日新闻

周新闻

月新闻