Cyclone IV系列FPGA串口远程烧写详解
首先关于Cyclone IV系列的配置和远程系统更新需要阅读《Configuration and Remote System Upgrades in Cyclone IV Devices》这个手册,该手册在官网上可以找到。阅读这个手册可以了解到有关Cyclone IV的配置方式,如并行方式PS方式,串行方式AS这两种,以及主被动配置。还有就是配置的整个流程。这里笔者研究的是AS方式的主动配置,即通PC把配置文件通过串口发送到FPGA上然后FPGA通过一段小逻辑将接收到的文件写入到EPCS存储器里头,这里笔者用的是EPCS16的Flash存储器。烧写的文件格式是rpd(Raw Programming Data File)。
rpd文件的生成
首先在Quartus软件下,笔者用的是Quartus16.1版本,点击Assignments->Device->Device and Pin Optins->Configuration,勾选Use configuration device,然后选择使用的存储器件,这里笔者使用的是EPCS16所以选择EPCS16,如下图所示,然后点击ok,关闭对话框。
再次点击全编译,会自动生成.pof,这个.pof就是要生成.rpd文件的中间文件。当编译生成.pof文件后点击File->Convert Programming Files...,出现如下对话框,并按照如下对话框设置。
需要注意的是这里生成的.rpd文件是小端模式的,而FPGA里头需要的是大端的形式,解决的办法有两种,第1种:点击上述对话框中的Options/Boot info...按钮选择Big endian。第2种:在往Flash芯片里头写入的时候将高低位互换,笔者在这里用的就是第2中方法。
rpd文件的传输
这里笔者用的是matlab编写的一段串口发送小程序,仿照ymodem协议进行稳定可靠的传输。代码如下:
clc;
clear all;
close all;%%
file_name = 'E:\JIYI_Work_Space\Projects\FPGA_Remote_Update\JIYI_FPGA_RSU_20200921\output_files\JIYI_FPGA_RSU_Little.rpd';
file_id=fopen(file_name,'r'); %以读的方式打开串口原始数据
fseek(file_id,0,'eof'); %将位置索引号指向文件最后
read_total_bytes = double(ftell(file_id)); %获取将要读取的总字节数 127302624
fprintf('读取总字节数:%dBytes,%fKB,%fMB\n',read_total_bytes,read_total_bytes/1024,read_total_bytes/1024/1024);
fseek(file_id,0,'bof'); %将位置索引号指向文件开始
binary_data = uint8(fread(file_id,read_total_bytes,'uint8'));
figure;
plot(binary_data);%%
scom1 = serial('com3');%%%%%%%%%%%%%%%%open com5
scom1.Terminator = 'CR';
scom1.BaudRate = 115200;
scom1.InputBufferSize = 1024*1024;
scom1.OutputBufferSize = 1024*1024;
scom1.Timeout = 60; %等待时延 单位:秒
scom1.RequestToSend ='on';fopen(scom1); %打开串口
%%
start_str = [36 70 80 71 65 66]; % $FPGAB 24 46 50 47 41 42
while 1fwrite(scom1, start_str, 'uint8'); %发送开始传输字符fprintf('Flash整片擦除开始...\n');ack = fread(scom1,1,'uint8'); %0x55 85if(ack(1) == 85) %收到正确回复fprintf('收到正确开始回复,Flash整片擦除完毕:ack = %s\n',ack(1));break;elsefprintf('收到错误开始回复:ack = %s\n',ack(1)); end
end%%
head = uint8(250); %0xFA
transmit_over = uint8(170); %0xAA
frame_num_bytes = 2;
section_bytes = 128; % 1024*1024/128
frame_bytes = 1+1+frame_num_bytes+section_bytes+1;
frame_str = uint8(zeros(1,frame_bytes));
section_num = read_total_bytes/section_bytes;
i = 1;
error_flag = 0;
bar = waitbar(0,'读取数据中...'); % waitbar显示进度条
while i <= section_numtemp = binary_data(((i-1)*section_bytes+1):(i*section_bytes));frame_str(1) = head; %帧头 0xFAframe_str(2) = uint8(section_bytes); %要传输的字节数量frame_str(3) = uint8(mod(i,256)); %帧编号的低8位frame_str(4) = uint8(fix(i/256)); %帧编号的高8位frame_str(5:(section_bytes+4)) = temp; %填充数据sum8 = sum(frame_str(1:end-1)); %得到校验和frame_str(end) = uint8(mod(sum8,256));fwrite(scom1, frame_str, 'uint8'); %发送生成的数据ack = fread(scom1,1,'uint8'); %0x55 85if(ack(1) == 85) %收到正确回复str=['loading...',num2str(i/section_num*100),'%']; waitbar(i/section_num,bar,str) % 更新进度条bar,配合bar使用i = i + 1;elseif(ack(1) == 102) % 0x66 收到的回复不正确时重复发送该帧数据fprintf('错误帧:i = %d\n',i);else fprintf('过程出错,已退出\n');error_flag = 1; %标志出错break;end
end
if error_flag == 0 %前面传输没有错误时
%数据结束while 1fwrite(scom1, transmit_over, 'uint8'); %发送结束字符 0xAAack = fread(scom1,1,'uint8'); %0x55 85if(ack(1) == 85) %收到正确回复fprintf('收到正确结束回复,Flash烧写完成:ack = %s\n',ack(1));break;elsefprintf('收到错误结束回复:ack = %s\n',ack(1)); endend
endclose(bar);
fclose(scom1); %关闭串口
FPGA接收与烧写逻辑
这里笔者用的是官方自带的ASMI ip核,用它就可以很轻松的将接收到的数据写入到Flash芯片里面。关于它的使用可以参考官方的手册《ASMI Parallel Intel® FPGA IP Core User Guide》。整个更新过程如下所述,1:接收到开始烧写命令后,FPGA触发对Flash芯片的整片擦除操作(写之前一定要先擦除,否则结果会不正确)。2:擦除完成后向PC回复擦除完成,PC接着开始传输正式的数据,并接受来自FPGA的校验结果,如果校验结果正确了,才会开始传输下一帧数据,如果校验结果不对,则重新传输该帧数据,直到结果正确为止,一直到最后一帧数据传输完成。3:最后一帧数据传输完成后,PC发送传输完成指令告诉FPGA,FPFA接收到回复PC确认烧写完成,退出烧写程序。实现的主要FPGA逻辑代码如下:
module FPGA_Remote_Update
(input wire MAIN_CLK, //全局50MHz时钟 input wire nRST, //全局复位信号input wire UART_RX,output wire UART_TX
);parameter ACK_CODE = 8'h55;
parameter ERROR_CODE = 8'h66;
parameter FRAME_HEAD = 8'hFA; //帧头
parameter TX_OVER = 8'hAA; //传输结束标志reg [3:0] state/*synthesis preserve*/;
reg [1:0] flash_clk_sam;
reg [1:0] busy_sam;
reg [7:0] bytes_max;
reg [7:0] bytes_cnt;
reg [7:0] check_sum;
reg check_sum_right;
reg data_get_valid;
always @(posedge MAIN_CLK)
beginif(~nRST) begin state <= 0;flash_clk_sam <= 0;busy_sam <= 0;check_sum <= 0;bytes_max <= 0;bytes_cnt <= 0;data_get_valid <= 0;check_sum_right <= 0;end else begin flash_clk_sam <= {flash_clk_sam[0], CLK_12_5M}; //对Flash时钟采样busy_sam <= {busy_sam[0], busy}; //对Flash控制模块的busy信号采样case(state)0: begin if(boot_start) //等待串口的 boot_start 发起begin state <= 1; end //3wren <= 0; bulk_erase <= 0;check_sum <= 0;bytes_cnt <= 0;bytes_max <= 0;data_get_valid <= 0;check_sum_right <= 0;uart_tx_byte <= 0; uart_tx_valid <= 0;end 1: begin if(flash_clk_sam == 2'b01) //发起芯片整片擦除信号,状态转移 flash_clk_sam[0], CLK_12_5M} == 2'b11begin wren <= 1; bulk_erase <= 1; state <= 2;end end 2:beginif({flash_clk_sam[0], CLK_12_5M} == 2'b11) //停止发起芯片整片擦除信号,状态转移begin wren <= 0; bulk_erase <= 0; state <= 3;endend 3:beginif(busy_sam == 2'b10) //等待擦除完毕,并回复上位机已准备好,并状态转移begin uart_tx_byte <= ACK_CODE; uart_tx_valid <= 1; state <= 4; end end 4: //接收除帧头部分的数据beginuart_tx_byte <= 0; uart_tx_valid <= 0;if(uart_rx_valid) //检测串口数据输出是否有效beginif(bytes_cnt) //接收字节数据计数beginif(bytes_cnt >= 3) begin bytes_cnt <= 0; endelse begin bytes_cnt <= bytes_cnt + 1; end endelse begin if(uart_rx_bytes == FRAME_HEAD) //判读第一个字节是否为帧头begin bytes_cnt <= 1; endendif(bytes_cnt == 0) begin if(uart_rx_bytes == TX_OVER) //判断是否为传输结束标志begin state <= 10; end end else if(bytes_cnt == 3) begin state <= 5; end //状态转移,标志地址初始化完成 case(bytes_cnt)0:beginif(uart_rx_bytes == FRAME_HEAD) //判读第一个字节是否为帧头begin bytes_max <= 0; addr <= 0; end end1: begin bytes_max <= uart_rx_bytes; end2: begin addr <= uart_rx_bytes; end //输入地址低8位3: begin addr <= {addr[23:16], uart_rx_bytes, addr[7:0]} - 1; end //输入地址高8位default:; endcaseif(bytes_cnt) //校验求和逻辑begin check_sum <= check_sum + uart_rx_bytes; end //累加8位校验和else begin check_sum <= uart_rx_bytes; end end end5: //准备写入的地址beginaddr <= addr*{16'b0, bytes_max}; //输入基地址state <= 6;data_get_valid <= 0;write <= 0;wren <= 0; shift_bytes <= 0;end6: //接收有效的数据begin if(uart_rx_valid) //检测串口数据输出是否有效beginbytes_cnt <= bytes_cnt + 1;datain <= uart_rx_bytes;data_get_valid <= 1; //标志获取一个后效的字节check_sum <= check_sum + uart_rx_bytes; //累加8位校验和endelse beginif(data_get_valid)beginif({flash_clk_sam[0], CLK_12_5M} == 2'b11)begin wren <= 1; shift_bytes <= 1; data_get_valid <= 0; end endelse beginif({flash_clk_sam[0], CLK_12_5M} == 2'b11)begin wren <= 0; shift_bytes <= 0;if(bytes_cnt >= bytes_max) begin state <= 7; end //判断是否到达指定接收数量条件了 end end end end7: //等待接收最后的校验和数据begin bytes_cnt <= 0; //清零if(uart_rx_valid) //检测串口数据输出是否有效begindata_get_valid <= 1; //标志获取一个后效的字节check_sum <= 0; //校验和清零if(check_sum == uart_rx_bytes) //判读校验和是否正确begin check_sum_right <= 1; end else begin check_sum_right <= 0; end endelsebeginif(data_get_valid) //接收到有效的一个字节了beginif(check_sum_right) //校验和正确时begin if({flash_clk_sam[0], CLK_12_5M} == 2'b11) //打开写入使能begin wren <= 1; write <= 1; state <= 8; end end elsebegin uart_tx_valid <= 1; uart_tx_byte <= ERROR_CODE; state <= 4; end //校验和不正确时,串口回复出错了,并返回状态4重新接收该帧数据 end end end8:begin check_sum_right <= 0; //校验和正确状态清零 if(data_get_valid)beginif({flash_clk_sam[0], CLK_12_5M} == 2'b11) //关闭写入使能begin wren <= 0; write <= 0; data_get_valid <= 0; state <= 9; endend end 9: //等待Flash烧写控制忙完begin if(busy_sam == 2'b10) //等待烧写完毕,并回复上位机准备好接收下一帧数据,并状态转移begin uart_tx_byte <= ACK_CODE; uart_tx_valid <= 1; state <= 4; endend10: //回复已收到结束命令begin uart_tx_byte <= ACK_CODE; uart_tx_valid <= 1; state <= 0;end default: begin state <= 0; end endcase end
end //-------------------------------------------- Flash控制模块 ---------------------------------------------
reg read;
reg rden;
reg [23:0] addr;
wire [7:0] dataout/*synthesis keep*/;
wire busy/*synthesis keep*/;
wire data_valid/*synthesis keep*/;
reg wren;
reg write;
reg shift_bytes;
reg [7:0] datain;
wire illegal_write/*synthesis keep*/;
reg bulk_erase;
wire illegal_erase/*synthesis keep*/;
ASMI ASMI_inst
(.clkin ( CLK_12_5M ), // clkin.clk.read ( read ), // read.read.rden ( rden ), // rden.rden.addr ( addr ), // addr.addr.reset ( ~nRST ), // reset.reset.dataout ( dataout ), // dataout.dataout.busy ( busy ), // busy.busy.data_valid ( data_valid ), // data_valid.data_valid.shift_bytes ( shift_bytes ),.wren ( wren ), // wren.wren.write ( write ), // write.write.datain ( {datain[0],datain[1],datain[2],datain[3],datain[4],datain[5],datain[6],datain[7]} ), //小端文件需要按位反转一下.illegal_write ( illegal_write ), // illegal_write.illegal_write.bulk_erase ( bulk_erase ), // bulk_erase.bulk_erase.illegal_erase ( illegal_erase ) // illegal_erase.illegal_erase
);//------------------------------------ PLL IP核生成Flash控制模块的时钟 ---------------------------------------
wire CLK_12_5M/*synthesis keep*/;
PLL_12_5M PLL_12_5M_inst
(.inclk0 ( MAIN_CLK ),.c0 ( CLK_12_5M ) //实际12.5MHz
);
//------------------------------------------- 例化串口应答模块 -----------------------------------------------
reg [7:0] uart_tx_byte;
reg uart_tx_valid;
ACK_Uart_TX ACK_Uart_TX_inst
(.MAIN_CLK(MAIN_CLK),.RST_N(nRST),.UART_TX(UART_TX),.Tx_Bytes(uart_tx_byte), //接收的数据.Tx_valid(uart_tx_valid)
);
//------------------------------------------- 例化串口接收模块 -----------------------------------------------
wire boot_start/*synthesis keep*/;
wire [7:0] uart_rx_bytes/*synthesis keep*/;
wire uart_rx_valid/*synthesis keep*/;
FPGA_data_Uart_RX FPGA_data_Uart_RX_inst
(.MAIN_CLK(MAIN_CLK),.RST_N(nRST),.UART_RX(UART_RX),.boot_start(boot_start), //标志boot开始.Rx_Bytes(uart_rx_bytes), //接收到的有效数据.Rx_valid(uart_rx_valid) //接收到的有效标志
);endmodule
至此,整个流程已介绍完成,笔者已经在实际的硬件上测试通过了。
Cyclone IV系列FPGA串口远程烧写详解相关推荐
- FPGA串口接收与发送详解( part 3 )
之前的part1~2已经详解完了单个数据的串口接收与发送,链接如下: FPGA串口接收与发送 详解 (part 1 )_居安士的博客-CSDN博客 FPGA串口接收与发送详解( part 2 )_居安 ...
- ADSP-21489的开发详解:SPIflash的硬件设计及程序烧写详解(含Flash驱动源码)
硬件准备 ADSP-21489EVB:ADI 21489处理器的开发板 AD-HP530ICE:ADI DSP专用仿真器 USBi:ADI SigmaDSP和SHARC DSP的图形化编程调试器 软件 ...
- ADSP-21489的开发详解:Norflash的硬件设计及程序烧写详解(含源代码)
编者的话 Flash 编程与烧写,原本应该是开发的最后一步,当所有程序都做好了,在线编译运行正常,才会通过 Flash 编程,生成二进制的可执行文件 LDR,再通过 JTAG 仿真器将 LDR 文件烧 ...
- cyclone IV 系列轻量级FPGA 芯片ep4ce6e22c8 引脚分布图
目录 简介 EP4CE6E22C8 IO分布 BANK IO分布 补充 简介 EP4CE6E22C8作为可以手动焊接的一款轻量级cyclone IV系列芯片,百度了一大圈都只有一个用户手册,找不到引脚 ...
- Altera Cyclone IV系列命名规则
1.Cyclone IV E系列命名规则 1.首先是EP4C,这是Cyclone IV的代号简称,只要是Cyclone IV系列都以EP4C开头 2.然后是一个字母E,E代表是E系列,Enhanced ...
- (札记)Altera Stratix IV系列FPGA TRUE LVDS RX input termination 在Quartus工程中的设置方法...
Altera Stratix IV系列FPGA Row bank的TRUE LVDS_RX支持oct(on chip termination),所以设计的时候不需要外接一个100ohm电阻.备注:我使 ...
- 的远程烧写_农用气象环境远程监测管理系统
秋收十月,一个传统行业的收获的季节.在我国农业领域,是农民最忙碌的季节,蔬菜,粮食,水果.都在忙着收货.等收获完还有准备下一季的种植.气候环境是对农业种植起到至关重要的作用.下面给介绍一下农用气象站的 ...
- 【蓝牙串口无线烧写程序】适用于STM32F405RG的Bootloader
[下载链接] 链接:https://pan.baidu.com/s/1gCQ2ayH2OQz0bQBAciNJ4w 提取码:qljc [使用说明] (1)用ST-Link或JLink将Bootload ...
- Altera的FPGA用烧写器烧写POF文件,烧写成功,显示100%,但是逻辑做的点灯没亮,一般会是哪的问题呀?烧写sof,灯亮。
Altera的FPGA用烧写器烧写POF文件,烧写成功,显示100%,但是逻辑做的点灯没亮,一般会是哪的问题呀?烧写sof,灯亮. 欢迎使用Markdown编辑器 你好! 这是你第一次使用 Markd ...
- 学习日记——FPGA实验平台板级电路详解
一.板级电路整体架构 我接下来一段时间学习的就是"勇敢的芯"FPGA 实验平台,它是特权同学和至芯科技携手打造的一款基于Altera Cyclone IV FPGA 器件的入门级 ...
最新文章
- 计算机录入速度标准,怎么提高电脑录入速度?
- Linux学习之遇到的小问题---查看系统版本,虚拟机创建共享文件夹,用到的命令记录。
- 服务器损坏mysql修复_云服务器mysql数据库损坏修复mysql
- 了解Spring Web初始化
- mysql 动态索引_MySQL的索引
- w ndows7文档加密取消,win7文件夹怎么加密?windows7文件加密方法
- 【微软亚洲研究院院长洪小文专访---谈大学生实习就业】
- 不止命令行-自定义VS生成事件
- Struts2 - Action no cache
- Julia: 关于Array排序函数sortslices
- 推荐一个springboot和springcloud系列的博客专家--方志朋
- [中文/英文]VC6 sp6补丁下载|VS6 sp6补丁下载 [防VC6link死机]
- ESXI7.0与6.7官网下载地址
- linux卸载usb声卡,Linux alsa 声卡驱动 安装 卸载 设置默认声卡
- 采购人必须明白的八大发展趋势及原则
- brew mysql_brew mysql指定版本
- 【职场加油站】给职场新人的几条忠告
- 好用的记事本app推荐 记事待办极简便签
- linux内核的挂载,通过Linux内核使用RDT
- MySQL 5.7.17.0 下载安装笔记