赞
踩
在国产的GD32和复旦微FPGA之间实现较高带宽的数据通信,可以使用EXMC接口。EXMC接口通过部分逻辑与FPGA中例化的BRAM接口相连。使用双口RAM的形式就能实现GD32和FPGA内部逻辑的交互。
GD32是主,EXMC是用来接外设的。把FPGA的BRAM当做外部存储去进行访问即可
EXMC是类似于STM32中的FSMC接口,FSMC(Flexible Static Memory Controller),可变静态存储控制器)是STM32系列采用的一种新型的存储器扩展技术。在外部存储器扩展方面具有独特的优势,可根据系统的应用需要,方便地进行不同类型大容量静态存储器的扩展。
使用EXMC实现GD32与FPGA多块BRAM的数据通信。
能实现多块就能实现一块,差不多,加了一个地址解码的过程
你问我为何不直接使用ZYNQ呢,内嵌的ARM硬核与FPGA之间直接就有多组高速的数据通路,没办法,用户的奇怪需求。
EXMC的介绍可以参考FSMC:
最好先了解清楚以上背景知识再看原文。
由于需求的原因实际上还加了很多其他的模块:比如CAN,UART,DDR,RGMII,MicroBlaze。这里就不赘述了。与本博客相关的就是红色框框内的内容:
顶层接口主要是:EXMC的输入输出接口,双口RAM的输入输出接口。
信号名 | 宽度 | 作用 |
---|---|---|
EXMC_DATABUS | 32 | 数据总线,支持8bit 16bit 32bit |
EXMC_ADDERBUS | 26 | 地址总线 |
EXMC_NE | 4 | 片选 |
EXMC_NWAIT | 1 | 等待信号 |
EXMC_NWE | 1 | 写使能 |
EXMC_NOE | 1 | 读使能 |
EXMC_NBL | 4 | 字节有效信号 |
EXMC_NADV | 1 | 地址有效信号 |
//EXMC是GD32过来的接口
inout [31:0] EXMC_DATABUS,
input [25:0] EXMC_ADDERBUS,
input [3:0] EXMC_NE,
output EXMC_NWAIT,
input EXMC_NWE,
input EXMC_NOE,
input [3:0] EXMC_NBL,
input EXMC_NADV,
BRAM接口都是从Block Design 中引出来的。
内容也很简单,可以稍微学习一下。
名称 | 作用 |
---|---|
BRAM_PORTA_0_0_addr | 地址 |
BRAM_PORTA_0_0_clk | 时钟 |
BRAM_PORTA_0_0_din | 数据输入 |
BRAM_PORTA_0_0_dout | 数据输出 |
BRAM_PORTA_0_0_en | 数据输入输出使能 |
BRAM_PORTA_0_0_rst | 复位 |
BRAM_PORTA_0_0_we | 字节选择 |
代码:定义在了top层
//BRAM定义在了Block Design中,位宽为32位,深度为4k
wire [31:0] BRAM_PORTA_0_0_addr;
wire BRAM_PORTA_0_0_clk;
wire [31:0] BRAM_PORTA_0_0_din;
wire [31:0] BRAM_PORTA_0_0_dout;
wire BRAM_PORTA_0_0_en;
wire BRAM_PORTA_0_0_rst;
wire [3:0] BRAM_PORTA_0_0_we;
//双口RAM才能实现GD32和FPGA的交互
wire [31:0] BRAM_PORTB_0_0_addr;
wire BRAM_PORTB_0_0_clk;
wire [31:0] BRAM_PORTB_0_0_din;
wire [31:0] BRAM_PORTB_0_0_dout;
wire BRAM_PORTB_0_0_en;
wire BRAM_PORTB_0_0_rst;
wire [3:0] BRAM_PORTB_0_0_we;
BRAM接口在Block Design中。当然也能不在Block Design中使用。
BRAM在BD中的样子,由于BRAM用的多,做了封装,方便查看整理修改。
我们对比两个接口的类型,发现:
特别注意EXMC_DATABUS是inout类型!!!
整体关系如下:
(GD32) <--> (EXMC控制器) ===EXMC接口=== (FPGA逻辑) ===BRAM接口 === (BRAM) === BRAM接口 === (FPGA逻辑)
核心代码:
实测能够稳定运行。
有几个注意的点:
module exmc2bram(
input sys_rst,
input sys_clk,
// inout [15:0] EXMC_DATABUS ,
output[31:0] EXMC_DATABUS_rd ,
input [31:0] EXMC_DATABUS_wr ,
input EXMC2BRAM_EN ,
input [25:0] EXMC_ADDERBUS ,
input EXMC_NADV,
input [3:0] EXMC_NE,
output EXMC_NWAIT,
input EXMC_NWE, //
input EXMC_NOE, //
input [3:0] EXMC_NBL, //
output [31:0] BRAM_R_0_addr,
output BRAM_R_0_clk,
output [31:0] BRAM_R_0_din,
input [31:0] BRAM_R_0_dout,
output BRAM_R_0_en,
output BRAM_R_0_rst,
output [3:0] BRAM_R_0_we,
output [31:0] BRAM_W_0_addr,
output BRAM_W_0_clk,
output [31:0] BRAM_W_0_din,
input [31:0] BRAM_W_0_dout,
output BRAM_W_0_en,
output BRAM_W_0_rst,
output [3:0] BRAM_W_0_we,
output done
);
wire read_en;
wire write_en;
reg write_en_delay;
reg write_en_delay2;
reg write_en_delay3;
wire write_en_in;
reg read_en_delay;
wire addr_en;
reg addr_en_delay;
wire data_en;
assign EXMC_NWAIT=1'b0;
//提取输入输出有效的值
//当写使能有效,片选有效,读无效,地址有效,且使能BRAM(自创的,因为有多块RAM)时,写使能
assign write_en= (~EXMC_NWE)& (~EXMC_NE[0]) & EXMC_NOE & EXMC_NADV & EXMC2BRAM_EN;
//当读有效,片选有效,地址有效,且使能BRAM,读使能
assign read_en = (~EXMC_NOE)& (~EXMC_NE[0]) & EXMC_NADV & EXMC2BRAM_EN;
assign write_en_in = (!write_en_delay3) && write_en_delay;
always @ (posedge sys_clk ) begin
write_en_delay <=write_en;
write_en_delay2 <=write_en_delay;
write_en_delay3 <=write_en_delay2;
end
assign addr_en = read_en_delay & read_en;
assign data_en = addr_en_delay & addr_en;
always @(posedge sys_clk) begin
read_en_delay <=read_en;
addr_en_delay <=addr_en;
end
assign BRAM_W_0_addr = write_en_in ? {16'd0,EXMC_ADDERBUS[15:0] } : 32'hZZZZ_ZZZZ;
assign BRAM_W_0_din = write_en_in ? (EXMC_ADDERBUS[1:0]==2'b00 ? {24'd0, EXMC_DATABUS_wr[7:0] } :
(EXMC_ADDERBUS[1:0]==2'b01 ? {16'd0, EXMC_DATABUS_wr[7:0], 8'd0 } :
(EXMC_ADDERBUS[1:0]==2'b10 ? {8'd0, EXMC_DATABUS_wr[7:0], 16'd0 } :
(EXMC_ADDERBUS[1:0]==2'b11 ? { EXMC_DATABUS_wr[7:0], 24'd0 } : 32'hZZZZ_ZZZZ))))
: 32'hZZZZ_ZZZZ;
assign BRAM_W_0_clk = sys_clk;
assign BRAM_W_0_en = write_en_in ;
assign BRAM_W_0_we = write_en_in ? (EXMC_ADDERBUS[1:0]==2'b00 ? 4'b0001 :
(EXMC_ADDERBUS[1:0]==2'b01 ? 4'b0010 :
(EXMC_ADDERBUS[1:0]==2'b10 ? 4'b0100 :
(EXMC_ADDERBUS[1:0]==2'b11 ? 4'b1000 : 4'bZZZZ))))
: 4'bZZZZ;
assign BRAM_W_0_rst = 1'b0;
assign BRAM_R_0_addr = addr_en ? {16'd0,EXMC_ADDERBUS[15:0] } : 32'hZZZZ_ZZZZ;
assign EXMC_DATABUS_rd = data_en ? (EXMC_ADDERBUS[1:0]==2'b00 ? {24'd0, BRAM_R_0_dout[7 :0 ] } :
(EXMC_ADDERBUS[1:0]==2'b01 ? {24'd0, BRAM_R_0_dout[15:8 ] } :
(EXMC_ADDERBUS[1:0]==2'b10 ? {24'd0, BRAM_R_0_dout[23:16] } :
(EXMC_ADDERBUS[1:0]==2'b11 ? {24'd0, BRAM_R_0_dout[31:24] } : 32'hZZZZ_ZZZZ))))
: 32'hZZZZ_ZZZZ;
assign BRAM_R_0_clk = sys_clk;
assign BRAM_R_0_en = addr_en;
assign BRAM_R_0_we = 4'b0000;
assign BRAM_R_0_rst = 1'b0;
endmodule
对于多个BRAM的时候我是怎么做的呢?
首先对不同的BRAM进行地址的分配,由于26位的地址,高位很多时候用不到,与嵌入式工程师商量,将高位地址进行分块,不同的BRAM高位不一样,就能通过提取高位判断的方式明确要写到那个BRAM中,就是一个简单的译码器的原理。
下面的代码还实现了inout信号输入输出的问题。
代码如下:
module decode_module(
input clk ,
inout [31:0]EXMC_DATABUS ,
input [25:0]EXMC_ADDERBUS ,
input EXMC_NADV,
input [3:0] EXMC_NE,
input [31:0]EXMC_DATABUS_rd_0 ,
input [31:0]EXMC_DATABUS_rd_1 ,
input [31:0]EXMC_DATABUS_rd_2 ,
input [31:0]EXMC_DATABUS_rd_3 ,
input [31:0]EXMC_DATABUS_rd_4 ,
input [31:0]EXMC_DATABUS_rd_5 ,
input [31:0]EXMC_DATABUS_rd_6 ,
input [31:0]EXMC_DATABUS_rd_7 ,
input [31:0]EXMC_DATABUS_rd_8 ,
input [31:0]EXMC_DATABUS_rd_9 ,
input [31:0]EXMC_DATABUS_rd_10 ,
input [31:0]EXMC_DATABUS_rd_11 ,
input [31:0]EXMC_DATABUS_rd_12 ,
input [31:0]EXMC_DATABUS_rd_13 ,
output [31:0]EXMC_DATABUS_wr_0 ,
output [31:0]EXMC_DATABUS_wr_1 ,
output [31:0]EXMC_DATABUS_wr_2 ,
output [31:0]EXMC_DATABUS_wr_3 ,
output [31:0]EXMC_DATABUS_wr_4 ,
output [31:0]EXMC_DATABUS_wr_5 ,
output [31:0]EXMC_DATABUS_wr_6 ,
output [31:0]EXMC_DATABUS_wr_7 ,
output [31:0]EXMC_DATABUS_wr_8 ,
output [31:0]EXMC_DATABUS_wr_9 ,
output [31:0]EXMC_DATABUS_wr_10 ,
output [31:0]EXMC_DATABUS_wr_11 ,
output [31:0]EXMC_DATABUS_wr_12 ,
output [31:0]EXMC_DATABUS_wr_13 ,
output [13:0]EXMC2BRAM_EN ,
output done
);
wire is_rs232_0 = EXMC_ADDERBUS[25:20]== 6'h00 ? 1'b1 : 1'b0 ;
wire is_rs232_1 = EXMC_ADDERBUS[25:20]== 6'h02 ? 1'b1 : 1'b0 ;
wire is_rs232_2 = EXMC_ADDERBUS[25:20]== 6'h04 ? 1'b1 : 1'b0 ;
wire is_rs422_0 = EXMC_ADDERBUS[25:20]== 6'h06 ? 1'b1 : 1'b0 ;
wire is_rs485_0 = EXMC_ADDERBUS[25:20]== 6'h08 ? 1'b1 : 1'b0 ;
wire is_rs485_1 = EXMC_ADDERBUS[25:20]== 6'h0A ? 1'b1 : 1'b0 ;
wire is_can_0 = EXMC_ADDERBUS[25:20]== 6'h0C ? 1'b1 : 1'b0 ;
wire is_can_1 = EXMC_ADDERBUS[25:20]== 6'h0E ? 1'b1 : 1'b0 ;
wire is_rgmii_0 = EXMC_ADDERBUS[25:20]== 6'h10 ? 1'b1 : 1'b0 ;
wire is_rgmii_1 = EXMC_ADDERBUS[25:20]== 6'h12 ? 1'b1 : 1'b0 ;
wire is_rgmii_2 = EXMC_ADDERBUS[25:20]== 6'h14 ? 1'b1 : 1'b0 ;
wire is_rgmii_3 = EXMC_ADDERBUS[25:20]== 6'h16 ? 1'b1 : 1'b0 ;
wire is_rgmii_4 = EXMC_ADDERBUS[25:20]== 6'h18 ? 1'b1 : 1'b0 ;
wire is_rgmii_5 = EXMC_ADDERBUS[25:20]== 6'h1A ? 1'b1 : 1'b0 ;
wire is_read = EXMC_ADDERBUS[19:16]== 4'h0 ? 1'b1 : 1'b0 ;
wire is_write = EXMC_ADDERBUS[19:16]== 4'h1 ? 1'b1 : 1'b0 ;
wire [13:0] read_case_w;
assign read_case_w={ is_rgmii_5, is_rgmii_4, is_rgmii_3, is_rgmii_2, is_rgmii_1, is_rgmii_0,
is_can_1, is_can_0, is_rs485_1, is_rs485_0, is_rs422_0, is_rs232_2, is_rs232_1, is_rs232_0};
assign EXMC2BRAM_EN=read_case_w; //enable exmc to bram module
assign EXMC_DATABUS_wr_0 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_1 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_2 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_3 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_4 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_5 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_6 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_7 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_8 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_9 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_10 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_11 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_12 = EXMC_DATABUS ;
assign EXMC_DATABUS_wr_13 = EXMC_DATABUS ;
reg [31:0]EXMC_DATABUS_r =32'h0000_0000;
always @(posedge clk ) begin
case (read_case_w)
14'b0000_0000_0000_01: EXMC_DATABUS_r = EXMC_DATABUS_rd_0;
14'b0000_0000_0000_10: EXMC_DATABUS_r = EXMC_DATABUS_rd_1;
14'b0000_0000_0001_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_2;
14'b0000_0000_0010_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_3;
14'b0000_0000_0100_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_4;
14'b0000_0000_1000_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_5;
14'b0000_0001_0000_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_6;
14'b0000_0010_0000_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_7;
14'b0000_0100_0000_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_8;
14'b0000_1000_0000_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_9;
14'b0001_0000_0000_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_10;
14'b0010_0000_0000_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_11;
14'b0100_0000_0000_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_12;
14'b1000_0000_0000_00: EXMC_DATABUS_r = EXMC_DATABUS_rd_13;
default: EXMC_DATABUS_r = EXMC_DATABUS_rd_0;
endcase
end
assign EXMC_DATABUS =is_read?EXMC_DATABUS_r:32'hZZZZ_ZZZZ;
代码是早期工作时期写的,欢迎大家批评指教,交流学习。
代码有偿提供,可以邮箱联系我1391783671@qq.com也可以私信联系,不定时上线。
欢迎点赞+收藏,谢谢大家,祝工作愉快。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。