前言
本文介绍STM32与FPGA通过fsmc通信的实现方法。
一、fsmc介绍
FSMC(Flexible Static Memory Controller,可变静态存储控制器)是STM32系列采用的一种新型的存储器扩展技术。在外部存储器扩展方面具有独特的优势,可根据系统的应用需要,方便地进行不同类型大容量静态存储器的扩展。
该使用方法本质是将FPGA当做SRAM来驱动,支持的存储器类型有SRAM、PSRAM、NOR/ONENAND、ROM、LCD接口(支持8080和6800模式)、NANDFlash和16位的PCCard。
二、平台说明
本次例程使用的处理芯片:STM32F407和Cyclone IV EP4CE22E144。
三、硬件部分
1.FPGA部分连接方式
2.STM32部分连接方式
STM32引脚连接使用的是复用fsmc功能引脚。
四、软件部分
1.STM32软件部分
STM32 FSMC初始化
/* Includes ------------------------------------------------------------------*/
#include "fsmc.h"
#include "gpio.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
SRAM_HandleTypeDef hsram1;
/* FSMC initialization function */
void MX_FSMC_Init(void)
{
FSMC_NORSRAM_TimingTypeDef Timing;
HAL_FSMC_MspInit();
/** Perform the SRAM1 memory initialization sequence
*/
hsram1.Instance = FSMC_NORSRAM_DEVICE;
hsram1.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
/* hsram1.Init */
hsram1.Init.NSBank = FSMC_NORSRAM_BANK1;
hsram1.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_ENABLE;
hsram1.Init.MemoryType = FSMC_MEMORY_TYPE_PSRAM;
hsram1.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
hsram1.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
hsram1.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
hsram1.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
hsram1.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
hsram1.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
hsram1.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
hsram1.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
hsram1.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_ENABLE;
hsram1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
hsram1.Init.PageSize = FSMC_PAGE_SIZE_NONE;
/* Timing */
Timing.AddressSetupTime = 1;
Timing.AddressHoldTime = 1;
Timing.DataSetupTime = 5;
Timing.BusTurnAroundDuration = 1;
Timing.CLKDivision = 16;
Timing.DataLatency = 17;
Timing.AccessMode = FSMC_ACCESS_MODE_A;
/* ExtTiming */
if (HAL_SRAM_Init(&hsram1, &Timing, NULL) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
static uint32_t FSMC_Initialized = 0;
static void HAL_FSMC_MspInit(void){
/* USER CODE BEGIN FSMC_MspInit 0 */
/* USER CODE END FSMC_MspInit 0 */
GPIO_InitTypeDef GPIO_InitStruct;
if (FSMC_Initialized) {
return;
}
FSMC_Initialized = 1;
/* Peripheral clock enable */
__HAL_RCC_FSMC_CLK_ENABLE();
/** FSMC GPIO Configuration
PE7 ------> FSMC_DA4
PE8 ------> FSMC_DA5
PE9 ------> FSMC_DA6
PE10 ------> FSMC_DA7
PE11 ------> FSMC_DA8
PE12 ------> FSMC_DA9
PE13 ------> FSMC_DA10
PE14 ------> FSMC_DA11
PE15 ------> FSMC_DA12
PD8 ------> FSMC_DA13
PD9 ------> FSMC_DA14
PD10 ------> FSMC_DA15
PD14 ------> FSMC_DA0
PD15 ------> FSMC_DA1
PD0 ------> FSMC_DA2
PD1 ------> FSMC_DA3
PD3 ------> FSMC_CLK
PD4 ------> FSMC_NOE
PD5 ------> FSMC_NWE
PD6 ------> FSMC_NWAIT
PD7 ------> FSMC_NE1
PB7 ------> FSMC_NL
PE0 ------> FSMC_NBL0
PE1 ------> FSMC_NBL1
*/
/* GPIO_InitStruct */
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
|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/* GPIO_InitStruct */
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14
|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_3
|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* GPIO_InitStruct */
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_FSMC;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USER CODE BEGIN FSMC_MspInit 1 */
/* USER CODE END FSMC_MspInit 1 */
}
void HAL_SRAM_MspInit(SRAM_HandleTypeDef* sramHandle){
/* USER CODE BEGIN SRAM_MspInit 0 */
/* USER CODE END SRAM_MspInit 0 */
HAL_FSMC_MspInit();
/* USER CODE BEGIN SRAM_MspInit 1 */
/* USER CODE END SRAM_MspInit 1 */
}
static uint32_t FSMC_DeInitialized = 0;
static void HAL_FSMC_MspDeInit(void){
/* USER CODE BEGIN FSMC_MspDeInit 0 */
/* USER CODE END FSMC_MspDeInit 0 */
if (FSMC_DeInitialized) {
return;
}
FSMC_DeInitialized = 1;
/* Peripheral clock enable */
__HAL_RCC_FSMC_CLK_DISABLE();
/** FSMC GPIO Configuration
PE7 ------> FSMC_DA4
PE8 ------> FSMC_DA5
PE9 ------> FSMC_DA6
PE10 ------> FSMC_DA7
PE11 ------> FSMC_DA8
PE12 ------> FSMC_DA9
PE13 ------> FSMC_DA10
PE14 ------> FSMC_DA11
PE15 ------> FSMC_DA12
PD8 ------> FSMC_DA13
PD9 ------> FSMC_DA14
PD10 ------> FSMC_DA15
PD14 ------> FSMC_DA0
PD15 ------> FSMC_DA1
PD0 ------> FSMC_DA2
PD1 ------> FSMC_DA3
PD4 ------> FSMC_NOE
PD5 ------> FSMC_NWE
PD6 ------> FSMC_NWAIT
PD7 ------> FSMC_NE1
PB7 ------> FSMC_NL
PE0 ------> FSMC_NBL0
PE1 ------> FSMC_NBL1
*/
HAL_GPIO_DeInit(GPIOE, GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
|GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1);
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_14
|GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_4
|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_7);
/* USER CODE BEGIN FSMC_MspDeInit 1 */
/* USER CODE END FSMC_MspDeInit 1 */
}
void HAL_SRAM_MspDeInit(SRAM_HandleTypeDef* sramHandle){
/* USER CODE BEGIN SRAM_MspDeInit 0 */
/* USER CODE END SRAM_MspDeInit 0 */
HAL_FSMC_MspDeInit();
/* USER CODE BEGIN SRAM_MspDeInit 1 */
/* USER CODE END SRAM_MspDeInit 1 */
}
STM32通信部分代码:
/* USER CODE END Prototypes */
#define fpga_write(addr_base,data) *((uint16_t *const volatile)(0x60000000 + addr_base)) = (uint16_t)data
#define fpga_read(offset) *((uint16_t *const volatile)(0x60000000 + offset))
//#define fpga_read(offset) *((volatile int32_t* )(0x60000000 + offset))
void fsmc_fpga_write(int addr_base,uint16_t* data,int lenth);
void fsmc_fpga_read(int offset,uint16_t* data,int lenth);
2.FPGA软件部分
FPGA内部使用avalon总线接口。因此,需要实现fsmc转avalon桥接模块的程序。程序如下:
fsmc_to_avmm
module fsmc_to_avmm(
input csi_clk,
input csi_clk_4x,
input csi_reset_n,
output reg [15 : 0] avm_address,
output reg avm_read_n,
input [15 : 0] avm_readdata,
output reg avm_write_n,
output [15 : 0] avm_writedata,
(* direct_enable = 1 *)
input avm_waitrequest,
output [1 : 0] avm_byteenable,
input coe_nadv,
(* direct_enable = 1 *)
input coe_ne,
input coe_noe,
input coe_nwe,
input [1 : 0] coe_nbl,
output reg coe_nwait,
inout [15 : 0] coe_addr_data_bus
);
reg [1 : 0] meta_nadv;
reg [1 : 0] meta_ne;
reg [1 : 0] meta_noe;
reg [1 : 0] meta_nwe;
reg stb_nadv;
reg stb_ne;
reg stb_noe;
reg stb_nwe;
reg [15 : 0] fsmc_data_out;
reg [15 : 0] addr_latch;
(* direct_enable = 1 *)
reg [15 : 0] write_addr_latch;
reg [15 : 0] data_latch;
reg [1 : 0] byteenable_wr_latch;
reg fifo_wrreq;
reg fifo_rdreq;
wire fifo_almostfull;
wire fifo_empty;
wire fifo_full;
wire [35 : 0] fifo_dout;
wire [7 : 0] fifo_usedw;
(* direct_enable = 1 *)
reg [15 : 0] avm_read_address;
wire [15 : 0] avm_write_address;
reg [15 : 0] avm_readdata_latch;
assign coe_addr_data_bus = (stb_noe) ? 16'bz : fsmc_data_out;
always @ (posedge csi_clk_4x or negedge csi_reset_n)
begin
if (!csi_reset_n) begin
meta_nadv <= 2'b11;
meta_ne <= 2'b11;
meta_noe <= 2'b11;
meta_nwe <= 2'b11;
stb_nadv <= 1;
stb_ne <= 1;
stb_noe <= 1;
stb_nwe <= 1;
end
else begin
meta_nadv <= {meta_nadv[0],coe_nadv};
meta_ne <= {meta_ne[0],coe_ne};
meta_noe <= {meta_noe[0],coe_noe};
meta_nwe <= {meta_nwe[0],coe_nwe};
stb_nadv <= coe_nadv;
stb_ne <= coe_ne;
stb_noe <= coe_noe;
stb_nwe <= coe_nwe;
end
end
always @ (posedge coe_nadv or negedge csi_reset_n)
begin
if (!csi_reset_n) begin
addr_latch <= 16'b0;
end
else begin
if (!coe_ne) begin
addr_latch <= coe_addr_data_bus;
// Should add constraints between addr_latch and csi_clk clock regions
// Recommended to use set_max_delay or set_multicycle_path
end
else begin
addr_latch <= addr_latch;
end
end
end
always @ (posedge coe_nwe or negedge csi_reset_n)
begin
if (!csi_reset_n) begin
data_latch <= 16'b0;
end
else begin
if (!coe_ne) begin
data_latch <= coe_addr_data_bus;
byteenable_wr_latch <= ~coe_nbl;
// Should add constraints between data_latch and csi_clk clock regions
// Recommended to use set_max_delay or set_multicycle_path
end
else begin
data_latch <= data_latch;
byteenable_wr_latch <= byteenable_wr_latch;
end
end
end
reg [2 : 0] fsmc_state;
reg [2 : 0] fsmc_next_state;
localparam FSMC_IDLE = 3'b1;
localparam FSMC_READ = 3'b10;
localparam FSMC_WRITE = 3'b100;
always @ (posedge csi_clk or negedge csi_reset_n)
begin
if (!csi_reset_n) begin
fsmc_state <= FSMC_IDLE;
end
else begin
fsmc_state <= fsmc_next_state;
end
end
always @ (*)
begin
case (fsmc_state)
FSMC_IDLE : begin
casez ({ stb_ne, stb_noe, stb_nwe }) // synthesis full_case
3'b001 : begin
fsmc_next_state = FSMC_READ;
end
3'b010 : begin
fsmc_next_state = FSMC_WRITE;
end
3'b011 : begin
fsmc_next_state = FSMC_IDLE;
end
3'b1?? : begin
fsmc_next_state = FSMC_IDLE;
end
endcase
end
FSMC_READ : begin
if (stb_noe) begin
fsmc_next_state = FSMC_IDLE;
end
else begin
fsmc_next_state = FSMC_READ;
end
end
FSMC_WRITE : begin
if (stb_nwe) begin
fsmc_next_state = FSMC_IDLE;
end
else begin
fsmc_next_state = FSMC_WRITE;
end
end
default : begin
fsmc_next_state = FSMC_IDLE;
end
endcase
end
always @ (posedge csi_clk or negedge csi_reset_n)
begin
if (!csi_reset_n) begin
avm_read_n <= 1;
avm_readdata_latch <= 0;
fifo_wrreq <= 0;
end
else begin
case (fsmc_state)
FSMC_IDLE : begin
fifo_wrreq <= 0;
casez ({ stb_ne, stb_noe, stb_nwe }) // synthesis full_case
3'b001 : begin // read
avm_read_n <= 0;
end
3'b010 : begin // write
end
3'b011 : begin
avm_read_n <= 1;
end
3'b1?? : begin // idle
avm_read_n <= 1;
end
endcase
end
FSMC_READ : begin
if (avm_waitrequest) begin
end
else if (avm_read_n == 0) begin
// if (avm_read_n) begin
// fsmc_data_out <= avm_readdata;
// end
// else begin
// fsmc_data_out <= fsmc_data_out;
// end
avm_read_n <= 1;
avm_readdata_latch <= avm_readdata;
end
else begin
end
end
FSMC_WRITE : begin
if (stb_nwe) begin
fifo_wrreq <= 1;
end
else begin
fifo_wrreq <= fifo_wrreq;
end
end
endcase
end
end
always @ (posedge csi_clk or negedge csi_reset_n)
begin
if (!csi_reset_n) begin
avm_read_address <= 0;
write_addr_latch <= 0;
end
else begin
if (!stb_noe) begin
avm_read_address <= addr_latch;
end
else begin
avm_read_address <= avm_read_address;
end
if (!stb_nwe) begin
write_addr_latch <= addr_latch;
// Set a false path or multicycle or max delay from stb_nwe to write_addr_latch
end
else begin
write_addr_latch <= write_addr_latch;
end
end
end
always @ (*)
begin
if (avm_read_n) begin
fsmc_data_out = avm_readdata;//avm_readdata_latch;
end
else begin
fsmc_data_out = avm_readdata_latch;//avm_readdata;
end
end
always @ (*)
begin
if (avm_write_n) begin
avm_address = avm_read_address;
end
else begin
avm_address = avm_write_address;
end
end
always @ (*)
begin
if ((fifo_empty == 0) && (avm_read_n == 1)) begin
avm_write_n = 0;
if (avm_waitrequest == 0) begin
fifo_rdreq = 1;
end
else begin
fifo_rdreq = 0;
end
end
else begin
fifo_rdreq = 0;
avm_write_n = 1;
end
end
/* always @ (*)
begin
(* full_case *)
case(fsmc_state)
FSMC_IDLE : begin
coe_nwait = 1;
end
FSMC_READ : begin
coe_nwait = ~avm_waitrequest;
end
FSMC_WRITE : begin
coe_nwait = ~fifo_almostfull;
end
endcase
end */
always @ (*)
begin
case({ avm_read_n, coe_nwe }) // synthesis full_case
2'b01 : begin
coe_nwait = ~avm_waitrequest;
end
2'b10 : begin
coe_nwait = ~fifo_almostfull;
end
2'b11 : begin
coe_nwait = 0;
end
endcase
end
assign {avm_byteenable, avm_write_address, avm_writedata} = avm_write_n ? {2'b11, 16'b0, 16'b0} : fifo_dout;
scfifo scfifo_component (
.clock (csi_clk),
.data (
{2'b00, // Reserved for checksum
byteenable_wr_latch,
write_addr_latch,
data_latch}),
.rdreq (fifo_rdreq),
.sclr (!csi_reset_n),
.wrreq (fifo_wrreq),
.almost_full (fifo_almostfull),
.empty (fifo_empty),
.full (fifo_full),
.q (fifo_dout),
.usedw (fifo_usedw),
.aclr (),
.almost_empty (),
.eccstatus ());
defparam
scfifo_component.add_ram_output_register = "ON",
scfifo_component.almost_full_value = 250,
scfifo_component.intended_device_family = "Cyclone IV E",
scfifo_component.lpm_hint = "RAM_BLOCK_TYPE=M9K",
scfifo_component.lpm_numwords = 256,
scfifo_component.lpm_showahead = "ON",
scfifo_component.lpm_type = "scfifo",
scfifo_component.lpm_width = 36,
scfifo_component.lpm_widthu = 8,
scfifo_component.overflow_checking = "ON",
scfifo_component.underflow_checking = "ON",
scfifo_component.use_eab = "ON";
endmodule
fsmc_ctrl
module fmsc_ctrl(
input clk,
input fmsc_WR, //FSMC写信号
input fmsc_RD, //FSMC读信号
input fmsc_CS0, //FSMC片选
input[24:16] fmsc_adress, //FSMC地址总线
input[15:0] fmsc_data, //FSMC数据总线
input fmsc_NADV //FSMC的NADV
);
endmodule
qsys内部配置如下:
总结
STM32部分使用标准fsmc通信功能,FPGA内部使用avalon总线进行通信,因此编写fsmc转avalon总线通信模块。