icap对flash的在线升级


要求:设计区域 1 的程序,上电后自动加载此程序,此时开始计时如果 20 秒内没有检测到串口发送的擦除指令,那么我们启动 icap 跳转,跳转到区域 2 程序中。如果希望再次升级的话必须重新给板卡上电使得程序回到区域 1 中,并在20秒计时内通过fpga_update软件将新的应用程序更新到flash中,实现flash的在线升级。
在这里插入图片描述

设计思想:我们制作两个程序,第一个区域执行程序 1(包含flash_ctrl和icap)能实现flash控制和程序跳转功能,这个区域的程序是固定的不会被修改。第二个区域的程序 2 是我们用户设计的功能程序或者说产品程序(有更新需求的程序)。

一、icap原语介绍(针对 S6 系列的 ICap),之后可以拓展到A7、K7当中去

icap原语查找方式(ise软件):

  1. language template
    在这里插入图片描述
  2. 在弹出的对话框中找到Internal Configuration Access Port。
    在这里插入图片描述
  3. icap 实例化原语
    在这里插入图片描述
    信号解释:

1、DEVICE_ID:不同芯片的DEVICE_ID不相同,在使用该原语时,要查找对应芯片的ID ;
2、SIM_CFG_FILE_NAME:仿真使用,默认即可。
3、BUSY:原语对应的忙信号
4、O:配置数据的输出
5、CE:原语的使能信号,低电平有效
6、CLK:原语的时钟信号
7、I:原语配置数据的输入信号,位宽为16bit需要按照步骤传输以下数据,
(其中 opcode 指的是器件 read 的命令(基于 spi 的 flash read 命令为 03h)
在这里插入图片描述
8、WRITE:读写原语的使能信号,低电平有效

需要注意的是:在我们传输这些配置数据时,需要将这些配置数据按照 byte 为单位,进行高低位互换,如下:
在这里插入图片描述

二、程序1设计

2.1信号结构框图

在这里插入图片描述

其中flash_ctrl模块之前已经实现:
这里介绍一下icap_delay 模块和 icap_ctrl模块(重点)

2.2 icap_delay设计

module icap_delay(
	input	wire 		sclk,
	input	wire 		rst,
	output	reg 		icap_flag,
	input	wire 		rx_flag,
	input	wire [7:0]	rx_data,
	output	wire 		led
	);

parameter TIMER_END = 1000000000-1;
reg 	[31:0]	time_cnt;
reg				stop_flag;

assign led = 1;

// 接收到擦除指令的标志
always @(posedge sclk or posedge rst) begin
	if (rst == 1'b1) begin
		stop_flag <= 1'b0;
	end
	else if (rx_flag== 1'b1 && rx_data == 8'hee) begin
		stop_flag <= 1'b1;
	end
end

// 接收到擦除指令之前保持计数,接收到指令后停止计数(不是清0)
always @(posedge sclk or posedge rst) begin
	if (rst == 1'b1) begin
		time_cnt <= 'd0;
	end
	else if (stop_flag == 1'b1) begin
		time_cnt <= time_cnt;
	end
	else begin
		time_cnt <= time_cnt + 1'b1;
	end
end

// icap执行跳转的标志,当计数超过20s后,执行icap完成跳转。
always @(posedge sclk or posedge rst) begin
	if (rst == 1'b1) begin
		icap_flag <= 1'b0;
	end
	else if (time_cnt >= TIMER_END) begin
		icap_flag <= 1'b1;
	end
	else begin
		icap_flag <= 1'b0;
	end
end
endmodule 

2.3 icap_ctrl设计(可以当模板使用,之后修改关键参数即可)

上面介绍了icap执行的关键就是向icap中顺序输入数据,且数据需要按照字节进行高低位互换。
顺序执行采用状态机跳转的方式即可。
在这里插入图片描述

module	icap_ctrl( 
input	wire	sclk	,
input	wire	rst_n	,
input	wire	pi_flag	
);

wire		clk	;
reg		c_en	;
reg		wr_en	;
reg[15:0]	i_data	;	
wire[15:0]	i_crop	;

reg[15:0]	state	;


//使用时每个byte高低位需要互换
parameter	DUM_WORD	=	16'hFFFF;//空闲字
parameter	SYNC_WORD1	=	16'hAA99;//同步字1
parameter	SYNC_WORD2	=	16'h5566;//同步字2
parameter	GEN_WORD1	=	16'h3261;//向General1写1个type1的字数据
parameter	LOW_ADDR	=	16'h0000;//16位起始地址											-----------------根据flash实际划分的地址进行修改
parameter	GEN_WORD2	=	16'h3281;//向General2写1个type1的字数据
parameter	HIG_ADDR	=	16'h0310;//读操作码及高8位地址,操作码为0x03 read 0x0b fast Ready	-----------------根据flash实际划分的地址进行修改
parameter	GEN_WORD3	=	16'h32A1;//向General3写1个type1的字数据	
parameter	LOW_ADDR_BACK	=	16'h0000;//fallback起始低16位地址							    -----------------根据flash实际划分的地址进行修改
parameter	GEN_WORD4	=	16'h32C1;//向General4写1个type1的字数据	
parameter	HIG_ADDR_BACK	=	16'h0300;//读操作码及fallback高8位地址,操作码为0x03		    -----------------根据flash实际划分的地址进行修改
parameter	GEN_CMD_WORD	=	16'h30A1;//向CMD写1个type1的字数据	
parameter	IPROG_CMD	=	16'h000E;//IPROG命令
parameter	NOP_CMD		=	16'h2000;//空命令

//状态
parameter	S_DUM_WORD	=	16'h0001;//空闲字
parameter	S_SYNC_WORD1	=	16'h0002;//同步字1
parameter	S_SYNC_WORD2	=	16'h0004;//同步字2
parameter	S_GEN_WORD1	=	16'h0008;//向General1写1个type1的字数据
parameter	S_LOW_ADDR	=	16'h0010;//16位起始地址
parameter	S_GEN_WORD2	=	16'h0020;//向General2写1个type1的字数据
parameter	S_HIG_ADDR	=	16'h0040;//操作码及高8位地址,操作码为0x03 普通读 0x0b FAST read
parameter	S_GEN_WORD3	=	16'h0080;//向General3写1个type1的字数据	
parameter	S_LOW_ADDR_BACK	=	16'h0100;//fallback起始低16位地址
parameter	S_GEN_WORD4	=	16'h0200;//向General4写1个type1的字数据	
parameter	S_HIG_ADDR_BACK	=	16'h0400;//操作码及fallback高8位地址
parameter	S_GEN_CMD_WORD	=	16'h0800;//向CMD写1个type1的字数据	
parameter	S_IPROG_CMD	=	16'h1000;//IPROG命令
parameter	S_NOP_CMD1	=	16'h2000;//空命令
parameter	S_NOP_CMD2	=	16'h4000;//空命令
parameter	S_NOP_CMD3	=	16'h8000;//空命令

always@(posedge	sclk	or	negedge	rst_n)
	if(rst_n==1'b0)	
		state	<=	S_DUM_WORD;
	else case(state)
	S_DUM_WORD:
	if(pi_flag==1'b1)
		state	<=	S_SYNC_WORD1;
	S_SYNC_WORD1:
		state	<=	S_SYNC_WORD2;
	S_SYNC_WORD2:
		state	<=	S_GEN_WORD1;
	S_GEN_WORD1:
		state	<=	S_LOW_ADDR;
	S_LOW_ADDR:
		state	<=	S_GEN_WORD2;
	S_GEN_WORD2:
		state	<=	S_HIG_ADDR;
	S_HIG_ADDR:
		state	<=	S_GEN_WORD3;
	S_GEN_WORD3:
		state	<=	S_LOW_ADDR_BACK;
	S_LOW_ADDR_BACK:
		state	<=	S_GEN_WORD4;
	S_GEN_WORD4:
		state	<=	S_HIG_ADDR_BACK;
	S_HIG_ADDR_BACK:
		state	<=	S_GEN_CMD_WORD;
	S_GEN_CMD_WORD:
		state	<=	S_IPROG_CMD;
	S_IPROG_CMD:
		state	<=	S_NOP_CMD1;
	S_NOP_CMD1:
		state	<=	S_NOP_CMD2;
	S_NOP_CMD2:
		state	<=	S_NOP_CMD3;
	S_NOP_CMD3:
		state	<=	S_DUM_WORD;
	default:state	<=	S_DUM_WORD;
	endcase

//先拉低写使能,再拉低时钟使能
always@(posedge	sclk	or	negedge	rst_n)
	if(rst_n==1'b0)	
		c_en	<=	1'b1;
	else if(state==S_SYNC_WORD2)
		c_en	<=	1'b0;
	else if(state==S_DUM_WORD)
		c_en	<=	1'b1;
		
//先拉低写使能,再拉低时钟使能
always@(posedge	sclk	or	negedge	rst_n)
	if(rst_n==1'b0)	
		wr_en	<=	1'b1;
	else if(state==S_SYNC_WORD1)
		wr_en	<=	1'b0;
	else if(state==S_DUM_WORD)
		wr_en	<=	1'b1;

//发送控制字
always@(posedge	sclk	or	negedge	rst_n)
	if(rst_n==1'b0)	
		i_data	<=	DUM_WORD;
	else case(state)
	S_DUM_WORD:
		i_data	<=	DUM_WORD;
	S_SYNC_WORD1:
		i_data	<=	SYNC_WORD1;
	S_SYNC_WORD2:
		i_data	<=	SYNC_WORD2;    
	S_GEN_WORD1:                           
		i_data	<=	GEN_WORD1;     
	S_LOW_ADDR:                            
		i_data	<=	LOW_ADDR;      
	S_GEN_WORD2:                           
		i_data	<=	GEN_WORD2;     
	S_HIG_ADDR:                            
		i_data	<=	HIG_ADDR;      
	S_GEN_WORD3:                           
		i_data	<=	GEN_WORD3;     
	S_LOW_ADDR_BACK:                       
		i_data	<=	LOW_ADDR_BACK; 
	S_GEN_WORD4:                           
		i_data	<=	GEN_WORD4;     
	S_HIG_ADDR_BACK:                       
		i_data	<=	HIG_ADDR_BACK; 
	S_GEN_CMD_WORD:                        
		i_data	<=	GEN_CMD_WORD;  
	S_IPROG_CMD:                           
		i_data	<=	IPROG_CMD;     
	S_NOP_CMD1:                            
		i_data	<=	NOP_CMD;       
	S_NOP_CMD2:                            
		i_data	<=	NOP_CMD;       
	S_NOP_CMD3:                            
		i_data	<=	NOP_CMD;      
	default:i_data	<=	NOP_CMD;
	endcase

//对输入的数据按字节为单位进行高低位互换	
assign	i_crop	=	{i_data[8],i_data[9],i_data[10],i_data[11],i_data[12],i_data[13],i_data[14],i_data[15],i_data[0],i_data[1],i_data[2],i_data[3],i_data[4],i_data[5],i_data[6],i_data[7]};

//实例化icap原语		
ICAP_SPARTAN6 #(                                                                                          
.DEVICE_ID('h4001093),     //不同型号的芯片,ID号不同 x9 ID='h4001093
.SIM_CFG_FILE_NAME("NONE")  // Specifies the Raw Bitstream (RBT) file to be parsed by the simulationendmodule
                          // model                                                                
)                                                                                                      
ICAP_SPARTAN6_inst (                                                                                   
.BUSY(BUSY),   // 1-bit output: Busy/Ready output                                                   
.O(O),         // 16-bit output: Configuartion data output bus                                      
.CE(c_en),       // 1-bit input: Active-Low ICAP Enable input                                         
.CLK(sclk),     // 1-bit input: Clock input                                                          
.I(i_crop),         // 16-bit input: Configuration data input bus                                        
.WRITE(wr_en)  // 1-bit input: Read/Write control input                                             
);  

endmodule    

三、程序2设计

这里直接使用之前设计的一个呼吸灯

四、下板操作

关于操作方面,首先要有一个意识:fpga首先要有flash控制的功能(这是前提!!),然后才能执行对flash进行读写等操作)。

这里介绍一下老师提供的一个小软件,fpga_update,界面如下,可以实现flash的写操作。
在这里插入图片描述
在这里插入图片描述

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在实际工程应用中,我们时常会遇到为解决某个老产品的BUG,需要在工程现场更新设备的FPGA代码,或者参加电信测试时需要现场升级设备FPGA程序以便于调试。公司现阶段所用的Altera FPGA程序代码一般存放于芯片配套的FLASH存储器中,而常见的对印制板上FLASH编程有几种方法,原始的方法是使用编程器,这种方法需要要将芯片取下,十分不便,或者通过JTAG接口连接到PC机上,但需要专用下载软件(一般由芯片生产厂商提供)。在测试现场或调测机房现场,要找到FPGA的专用下载线是比较困难的,且Altera FPGA的专用下载软件并不是每个PC设备上都有的。有时仅为了更新一个FPGA的程序就需要研发或客服人员亲自到现场去烧写程序,这既不便捷,也使得设备维护成本大大增加。  经过可行性与成本的考虑,我们找到一种既方便实用又低成本的方法来实现FPGA程序的在线升级。即在MCU中(单片机或ARM均可)用软件来模拟XModem协议,将程序文件传输到FPGAFLASH中。这种方法使用WINDOWS自带的超级终端软件来传送文件,无需安装专用软件,硬件支持仅需要一根通用串口线,只要在目标板MCU上增加一段实现XModem协议传输的代码,就可以方便的实现FPGA程序下载了。这种特点不仅方便了客服人员,也给研发和生产人员在现场调试和软件升级、修改中带来极大方便。
FPGA在线升级是指通过一些手段更新FPGA中的程序,而不需要进行常规意义下的下载程序。常用的工具有ICAP原语和flash在线升级面临两个问题:如何将新的FPGA程序下载到flash中,以及如何让FPGA执行新的程序。在线升级的意义在于,当产品上线时,由于JTAG接口过于笨重,一般不会保留该接口。因此,在没有JTAG接口的情况下,要想下载新的程序,就必须使用在线升级的方法。在线升级的策略一般是通过产品自带的通讯接口(如串口、以太网接口等)传输待更新的固件包,然后将固件包写入FPGA的配置flash中,覆盖原来通过JTAG烧写的代码。当芯片重新上电或进行一次重配置时,FPGA会执行新的固件包。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *3* [基于FPGA在线升级](https://blog.csdn.net/zhangningning1996/article/details/104122587)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [FPGA远程固件在线升级](https://blog.csdn.net/weixin_45104510/article/details/129154915)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值