用户工具

站点工具


icore4tx_fpga_21
银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
版本 日期 作者 修改内容
V1.0 2020-07-01 gingko 初次建立



实验二十一:双口RAM实验——基于双口RAM的ARM与FPGA通信

一、 实验目的与意义

  1. 掌握双口RAM IP核的调用及例化方法。
  2. 掌握RAM读写时序。
  3. 掌握ISE软件的使用方法。

二、 实验设备及平台

  1. iCore4TX 双核心板点击购买
  2. USB-CABLE(或相同功能)仿真器。
  3. JLINK(或相同功能)仿真器。
  4. Micro USB线缆。
  5. Keil MDK 开发平台。
  6. ISE开发平台。
  7. 电脑一台。

三、 实验原理

  • RAM全称是Random Access Memory(随机存取存储器),是与处理器进行数据交互的存储器。既可向指定地址单元写入信息也可从指定地址单元中读取信息,掉电后数据丢失,属于易失性存储器。
  • 双口RAM分为伪双口RAM和真双口RAM两种,伪双口RAM一个端口只读,另一个端口只写;而真双口RAM两个端口都能读写。在SPI通信实验中,使用的就是伪双口RAM,因此,在本实验中只讲真双口RAM。从整体上讲,真双口RAM和伪双口RAM都能同时进行读写操作;虽然有两个端口,两套数据线和地址线,但是存储数据的空间单元是共享的,因此存储后的数据也是共享的。其基本工作时序和普通RAM一致,都是以时钟上升沿作为触发事件,在上升沿到来前保证地址和数据稳定即可将数据准确的写入对应的地址单元中,读取时,也是如此。其读写时序如图21-1所示。
  • 21-1(a) 读时序图

21-1(a) 读时序图

  • 21-1(b) 写时序图

21-1(b) 写时序图

  • 本实验基于ARM+FPGA构架,通过SPI实现ARM与FPGA之间通信,以双口RAM作为FPGA内部的数据存储器,实现ARM对数据的读写功能。ARM通过SPI将数据发送到FPGA,FPGA通过A端口将数据写入存储器中,然后通过B端口读出返回给ARM,实现数据的交互,实验流程较为简单,重点是要掌握双口RAM IP核的例化以及其读写操作的控制。

四、 双口RAM IP核的调用

1、 新建一个工程名为twodual_ram的工程。 图21-2 2、 在工程目录栏选中工程,右键单击,点击New Source…。 图21-3 3、 弹出界面的左侧栏选中IP(CORE Generator & Architecture Wizard)选项,并在右侧File name栏输入新建IP核的文件名,然后点击Next。 图21-4 4、 弹出Select IP界面如下图所示,在Memories & Storage Elements 栏下找到RAM & ROM,点击前面的“+”展开,单击选中Block Memory Generator,然后点击Next 进入下一步。 图21-5 5、 直接点击Finished。 图21-6 6、 然后弹出RAM IP 核相关信息设置的界面,这里的Interface Type选项保持默认设置Native即可,然后点击Next,如下图所示: 图21-7 7、 在Memory Type 栏选择RAM类型,本实验是双口RAM实验,所以这里选择True Dual Port RAM,然后点击Next。 图21-8 8、 设置RAM的位宽为8,存储深度为1024,Port A Options和Port B Options参数勾选如下图红框中所示,然后点击Next。 图21-9 9、 此界面保持默认选项,直接点击Next。 图21-10 10、 此界面也是默认选项,点击Next。 图21-11 11、 保持默认,点击Generate。可以在工程目录栏中看到spi_ram IP核文件已经添加完成。 图21-12

五、 代码讲解

  • 前面已经完成双口RAM IP核文件的建立核添加,接下来要在工程文件中例化该IP核模块,才能在FPGA中构建双口RAM,具体例化代码如下:
//------------------------------------------------//
//spi_ram
//负责存储SPI通信的数据(具体的存储位置可通过spi命令寄存器设置)
//双口RAM的缺点是与单口RAM相比,相同容量的RAM占用逻辑资源比较多。
//优点在于具有2个输入2个输出端口,两个端口都可用于输入输出,可实现同时读写
//数据从A端口写入,从B端口读出
spi_ram u0(
	.clka(wr_clk),
	.wea(wr_en),
	.ena(1'd1),
	.addra(address_a),
	.dina(receive_byte),
	.douta(a_dataout),
 
	.clkb(rd_clk),
	.web(1'd0),
	.enb(rd_en),
	.addrb(address_b),
	.dinb(8'd0),
	.doutb(data_out)
);
  • 例化之后,FPGA要完成对ARM传输指令的解析,这部分代码是通过case语句实现的。根据接收的指令,case语句会跳转到不同的处理状态,完成对应指令状态需要的操作,具体代码参考如下:
case(rx_state)
	rx_idle_state:begin//空闲状态,匹配伪命令时钟
		rx_state <= rx_judge_state;
		end
	rx_judge_state:begin//接收指令字节,判断指令含义分配跳转状态机状态
		case(receive_byte)	
			8'h01:begin//存储指令编码(获取设备状态)
				order <= receive_byte;
				end
			8'h02:begin//存储指令编码,跳转状态(写数据的长度信息)
				rx_state <= rx_wr_length_state;
				order <= receive_byte;
				end
			8'h04:begin//存储指令编码,跳转状态(写数据的地址信息)
				rx_state <= rx_wr_add_state;
				order <= receive_byte;
				end
			8'h05:begin//存储指令编码,跳转状态(读数据的长度信息)
				rx_state <= rx_rd_ledgth_state;
				order <= receive_byte;
				end
			8'h07:begin//存储指令编码,跳转状态(读数据的地址信息)
				rx_state <= rx_rd_add_state;
				order <= receive_byte;
				end
			default:;
		endcase
	end
	rx_wr_length_state:begin//获取写数据长度信息,高字节在前,低字节在后
		case(rx_cnt)
			8'd0:begin
				wr_length_r[15:8] <= receive_byte;
				rx_cnt <= rx_cnt + 1'd1;
				end
			8'd1:begin
				wr_length_r <= {wr_length_r[15:8],receive_byte};
				end	
			default:begin
				end
		endcase		
	end
	rx_wr_add_state:begin//获取写数据地址,并产生RAM的写地址,高字节在前
		case(rx_cnt)
			8'd0:begin
				wr_address_r[15:8] <= receive_byte; 
				rx_cnt <= rx_cnt + 1'd1;
				end
			8'd1:begin
				wr_address_r <= {wr_address_r[15:8],receive_byte};
				rx_cnt <= rx_cnt + 1'd1;
				wr_en_r <= 1'd1;
				end	
			8'd2:begin
				wr_address_r <= wr_address_r + 1'd1;
				if(wr_cnt==wr_length_r-1'd1)
					begin
						wr_cnt <= 12'h00;
						rx_cnt <= rx_cnt + 1'd1;
					end
				else
					begin	
                                             wr_cnt <= wr_cnt + 1'd1;	
                                         end	
			end
				default:begin
					end
		endcase	
	end
	rx_rd_ledgth_state:begin//获取读数据长度信息,高字节在前,低字节在后
		case(rx_cnt)
			8'd0:begin
				rd_length_r[15:8] <= receive_byte;
				rx_cnt <= rx_cnt + 1'd1;
				end
			8'd1:begin
				rd_length_r <= {rd_length_r[15:8],receive_byte};
				end	
			default:begin
				end
		endcase		
	end
	rx_rd_add_state:begin//获取读数据地址信息,并产生读地址信息,高字节在前
		case(rx_cnt)
			8'd0:begin
				rd_address_r[15:8] <= receive_byte;
				rx_cnt <= rx_cnt + 1'd1;
				end
			8'd1:begin
				rd_address_r <= {rd_address_r[15:8],receive_byte};
				rx_cnt <= rx_cnt + 1'd1;
				rd_en_r <= 1'd1;
				end
			8'd2:begin
				rd_address_r <= rd_address_r + 1'd1;
				if(rd_cnt==rd_length_r-1'd1)
					begin
						rd_cnt <= 12'h00;
						rx_cnt <= rx_cnt + 1'd1;
				else
					begin
						rd_cnt <= rd_cnt + 1'd1;
					end
				end		
			default:;			
		endcase
	end
endcase

六、 实验步骤及实验结果

图21-13

  1. 将硬件正确连接,如图21-13所示。
  2. 打开putty串口调试工具,打开设备管理器查看对应的端口信息,在putty中打开对应的端口,并设置波特率为115200,用于打印串口信息及控制RAM读写;
  3. 将编写好的FPGA代码进行编译,并下载到开发板中;
  4. 将编写好的ARM代码编译,并下载到开发板中,putty工具中会打印相应的SPI通信相关信息(若想多次测试,查看结果在putty中输入“test”即可);
  5. 观察实验现象及putty终端打印信息——FPGA_LED闪烁,putty终端打印如图21-14所示.

图21-14

七、 拓展实验

1.通过逻辑分析仪观察RAM读写时序是否和参考时序一致。

icore4tx_fpga_21.txt · 最后更改: 2022/04/01 11:39 由 sean