| **银杏科技有限公司旗下技术文档发布平台** ||||
|技术支持电话|**0379-69926675-801**|||
|技术支持邮件|Gingko@vip.163.com|||
^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^
| V1.0 | 2020-02-27 | gingko | 初次建立 |
===== 实验十九:基于双口 RAM 的 ARM+FPGA 数据存取实验 =====
==== 一、 实验目的与意义 ====
- 了解双口 RAM 读写的工作原理。
- 掌握双口 RAM 使用方法。
- 掌握 QuartusII 集成开发环境使用方法。
二、 实验设备及平台
- iCore3 双核心板。[[https://item.taobao.com/item.htm?id=524229438677|点击购买]]
- Blaster(或相同功能)仿真器和 USB 线缆。[[https://item.taobao.com/item.htm?id=554869837940|点击购买]]
- Micro USB 线缆。
- QuartusII 开发平台。
- Keil MDK 开发平台。
- 装有 WIN XP(及更高版本)系统的计算机。
==== 三、 实验原理 ====
* 由于双口RAM有两条相互独立的地址线、数据线和控制线,能实现数据共享,本实验针对双口 RAM 的工作特点进行实验验证。实验中通过 FPGA 对 RAM 的 A 端口进行读写操作,RAM的A端口先向RAM中写入0到255的数据,然后再读出来,如此循环操作。同时,通过 Commix 向 STM32 发送读命令,STM32收到命令后,通过STM32F407的 FSMC总线实现与FPGA通信,对RAM的 B 端口进行读控制,发送一次读命令RAM的 B 端口把相应地址里面的数据发送至 ARM,再发送至 Commix 显示出来以验证实验的成功性。 在本实验中实现了双口 RAM 的两种工作模式:1、双口 RAM 一个端口写操作,另一个端口 读操作;2、双口 RAM 两个端口同时读操作。实验的原理如下图 1 所示。
{{ :icore3:icore3_fpga_19_1.png?direct |实验原理图}}
==== 四、 Quartus 内部双口 RAM 块调用 ====
1、 打开 Tools——MegaWizard Plug-In Manager.
{{ :icore3:icore3_fpga_19_2.png?direct |图1}}
2、创建IP核,点击next。
{{ :icore3:icore3_fpga_19_3.png?direct |图2}}
3、在弹出界面1存储器列表里选择2双口RAM。3处选择FPGA类型。4处选择输出文件语言类型。5处设置文件存储位置和命名。点击6处next进行下一项设置。
{{ :icore3:icore3_fpga_19_4.png?direct |图3}}
4、设置如下
{{ :icore3:icore3_fpga_19_5.png?direct |图4}}
5、设置存储器深度为256,输出总线宽度16bit,使用FPGA逻辑资源类型选择自动分配。点击next。
{{ :icore3:icore3_fpga_19_6.png?direct |图5}}
6、配置端口时钟和读使能信号。
{{ :icore3:icore3_fpga_19_7.png?direct |图6}}
7、注意红框中不打勾,点击Next,
{{ :icore3:icore3_fpga_19_8.png?direct |图7}}
8、后面几项保持默认设置,直接点击Next,最后点击Finish,双口RAM就构建好了。注意:在存储内容初始化选项设置里选择保持空白,因为本实验是要向双口RAM中写入数据再读出数据并进行对比的操作,不需要在存储器内预装数据。
{{ :icore3:icore3_fpga_19_9.png?direct |图8}}
{{ :icore3:icore3_fpga_19_10.png?direct |图9}}
{{ :icore3:icore3_fpga_19_11.png?direct |图10}}
{{ :icore3:icore3_fpga_19_12.png?direct |图11}}
==== 五、代码讲解 ====
* 构建工程并添加好双口RAM核后,就可以进行逻辑功能的编写了。本实验要实现双口RAM的两种工作模式,这里分别讲一下两种工作模式的实现流程和方法。
* 模式1:由FPGA控制,先通过A端口向RAM中写入256个数据,然后读出来,循环操作。这是对A端口的独立读写操作。
* 为了进行数据的对比操作,存储的数据值跟其地址一样,通过计数生成存储数据,并将数据存入与其地址一样的存储单元。计数器以记满512个数为一个周期,前256个时钟周期内向RAM中顺序写入数据,后256个时钟周期内从RAM中顺序读取数据。代码如下:
always @ (posedge a_clk or negedge rst_n)
begin
if(!rst_n)
begin
a_wren <= 1'd0;
a_rden <= 1'd0;
a_datain <= 10'd0;
a_addr <= 10'd0;
end
else if(cnt >= 10'd0 && cnt <=10'd255)
begin
a_wren <= 1'd1; //写使能信号拉高
a_addr <= cnt; //写地址
a_datain <= cnt; //写入数据
a_rden <= 1'd0; //读使能信号拉低
end
else if(cnt >= 10'd256 && cnt <= 10'd511)
begin
a_rden <= 1'd1; //读使能信号拉高
a_addr <= cnt - 10'd256; //读地址
a_wren <= 1'd0; //写使能信号拉低
end
end
* FPGA读取数据之后,对读取器数据和其存储地址进行比对,并根据对比结果产生错误提示信号,再以此信号控制FPGA_LED的显示颜色。
* 模式2:在FPGA对RAM进行读写操作的同时,STM32通过FSMC总线对RAM的B端口进行读操作。实现对RAM的双端口读操作。
* 当STM32从RAM中读取数据时,相当于STM32通过FSMC总线对外部存储器进行操作,因此,检测FSMC传递至FPGA的读信号,并将RAM的值从B端口输出至FSMC总线,实现STM32对RAM的读功能。代码如下:
//--------------------------ram_b_rd---------------------------//
/*控制ram_b端口的读取功能,当读信号来临时,读取相应的数据发送至fsmc总线*/
wire rd = (CS0 | RD); //提取读信号
wire wr = (CS0 | WR); //提取写信号
reg wr_clk1,wr_clk2;
always @(posedge PLL_100M or negedge rst_n)
begin
if(!rst_n)
begin
wr_clk1 <= 1'd1;
wr_clk2 <= 1'd1;
end
else
{wr_clk2,wr_clk1} <= {wr_clk1,wr};
end
wire b_clk = (!wr_clk2 | !rd); //提取ram_b端口的时钟
assign DB = !rd ? b_dataout : 16'hzzzz; //读信号来临,读取ram_b端口数据
* 在代码中,对时钟做了延迟处理,以保证时钟的触发沿位于数据的稳定期,具体实现可以参考例程代码。
==== 六、 实验步骤 ====
- 把仿真器与 iCore3 的 SWD 调试口连接(直接相连或者通过转换器相连);
- 将 USB-Blaster 与 iCore3 的 JTAG 调试口相连;
- 将跳线帽插在 USB UART;
- 把 iCore3(USB_UART)通过 Micro USB 线与计算机连接,为 iCore3 供电;
- 打开 Commix 串口精灵,找到对应的 COM 端口打开;
- 打开 Quartus II 开发环境,并打开实验工程;
- 烧写 FPGA 程序到 iCore3 上;
- 打开 Keil MDK 开发环境,并打开实验工程;
- 烧写 ARM 程序到 iCore3 上;
- 输入串口命令,观察实验现象。
==== 七、 实验现象 ====
* 一方面,观察 FPGA_LED 的颜色,FPGA向RAM 中先写如数据,然后再读出来,读出数据与其地址相等,绿灯长亮。否则,红灯亮。
* 另一方面,通过 Commix 发送读命令读取 RAM 地址中的数据,对比写入数据与读取的数据,可以知道写入与读取的数据相同。
* **命令格式**: 读命令:ram+空格+read+空格+address+\cr\lf
* 例如:ram read 123\cr\lf
* 解析:读取 ram 的第 123 地址的数据。