|  **银杏科技有限公司旗下技术文档发布平台**  ||||
|技术支持电话|**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 地址的数据。