赞
踩
在线升级是指通过网络或其他远程方式对软件、固件或系统进行更新和升级的过程。FPGA的在线升级是指在运行时对FPGA芯片中的逻辑配置进行更新或修改,而无需物理更换芯片。一般开发阶段,开发人员常用JTAG对FPGA进行配置,用于工程的功能修改\调试\更新。但当投入为产品时,想要进行FPGA的固件更新,再通过JTAG来配置FPGA显然是比较麻烦的,所以需要在线升级功能。
不同型号和系列的FPGA可能会支持不同的配置模式,这里以7系列FPGA为例,其支持以下几种配置方式:
对常用的方式作简介:
Bitstream(bit):Bitstream是一种二进制文件格式,用于存储FPGA的配置信息。它包含了FPGA逻辑元件的连接和功能等详细信息,以及配置所需的时序和逻辑设置。Bitstream文件通常由FPGA开发工具生成,并通过不同的配置方式(如JTAG、SPI等)加载到FPGA中。
Binary(bin):Binary也是一种二进制文件格式,但与Bitstream不同,它通常是指纯粹的二进制数据文件,没有特定的FPGA配置结构。在某些情况下,可以将FPGA的配置数据导出为二进制文件,这样可以方便地进行备份、传输或其他处理。
MCS(Motorola S-record):MCS是Motorola S-record文件的缩写,是一种常见的文本文件格式(ASCII文件),用于存储数据和程序代码。在FPGA领域,MCS文件通常用于存储FPGA的配置数据。MCS文件包含了地址、数据和校验等信息,可以用于直接编程或烧录FPGA。
总结:bit和bin都是二进制文件,但bit是带有头信息的配置文件,bin文件是不带头信息的的配置文件,如图所示,就前面一部分配置信息不一样,其他的都一样。mcs是ASCII文件,其中两个ASCII字符用于表示数据的每个字节HEX文件,mcs文本结构可参考这篇博客(https://blog.csdn.net/hanhailong0525/article/details/122382501)。
上位机能够将新的配置文件.mcs更新到FPGA的外部存储器件flash中,从而实现FPGA的在线升级。
上位机通过pcie总线(并行)与FPGA相连,FPGA通过spi总线(串行)与flash相连。首先,上位机对.mcs文件进行预处理,把预处理后的文件数据传到FPGA中,然后FPGA将数据以并转串的方式写入flash中,更换flash里旧的配置文件,从而完成在线升级功能。其中spi_B为FPGA自我配置的专用spi引脚;spi_A为FPGA的普通IO口,用于FPGA向flash写.mcs文件数据的spi通道。
FPGA的任务是将上位机下传的预处理后的.mcs文件数据进行并转串处理,然后写入到flash中。设计中上位机将配置文件一个Byte一个Byte的下传,FPGA每接收到一个Byte,便将其并转串,通过spi写入到flash中。因此逻辑上需要设计spi逻辑接口模块,由上位机控制。本质上实现上位机对flash进行读写操作,而FPGA只起着数据转发的作用。
spi接口逻辑设计模块如下:
`timescale 1ns / 1ps module spi_interface( input ADSP_CLK, //系统时钟 input clk_1mhz, //生成spi cclk时钟, input wr_start, //上位机控制spi写 input rd_start, //上位机控制spi读 input spi_miso, //spi miso input [7:0] data_to_flash, //上位机要写入flash的Byte数据 output [7:0] data_to_dsp, //从flash读取要传入上位机的Byte数据 output spi_mosi, //spi mosi output reg spi_sck //spi sck ); wire spi_miso; wire spi_mosi; wire wr_clk; wire rd_clk; always @ (posedge ADSP_CLK) begin if(wr_start == 1'b1) begin spi_sck <= wr_clk; end else if(rd_start == 1'b1) begin spi_sck <= rd_clk; end end //spi写子模块 spi_write_data spi_write_data_inst( .adsp_clk (ADSP_CLK) , .ref_freq (clk_1mhz) , .wr_start (wr_start) , .data_to_flash (data_to_flash) , .wr_clk (wr_clk) , .spi_mosi (spi_mosi) ); //spi读子模块 spi_read_data spi_read_data_inst( .adsp_clk (ADSP_CLK) , .ref_freq (clk_1mhz) , .rd_start (rd_start) , .spi_miso (spi_miso) , .rd_clk (rd_clk) , .data_to_dsp (data_to_dsp) ); endmodule
`timescale 1ns / 1ps module spi_read_data( adsp_clk, ref_freq, rd_start, spi_miso, rd_clk, data_to_dsp ); parameter data_width = 8; parameter cnt_bit = 3; input adsp_clk; input ref_freq; input rd_start; input spi_miso; output rd_clk; output [data_width-1:0] data_to_dsp; reg ref_freq_reg; reg [cnt_bit:0] cnt_clk; reg ref_freq_en; reg ref_freq_en1; reg [cnt_bit:0] cnt_rd; reg [cnt_bit-1:0] data_bitsel; reg spi_miso_reg; reg [data_width-1:0] data_to_dsp_reg; always @ (posedge adsp_clk) begin ref_freq_reg <= ref_freq; end always @ (posedge adsp_clk) begin if(rd_start == 1'b1) begin if((ref_freq_reg == 1'b1) && (ref_freq == 1'b0)) begin if(cnt_clk < data_width) begin ref_freq_en <= 1'b1; cnt_clk <= cnt_clk + 1'b1; end else begin ref_freq_en <= 1'b0; end end end else begin ref_freq_en <= 1'b0; cnt_clk <= 4'b0000; end end always @ (posedge adsp_clk) begin if((ref_freq_reg == 1'b1) && (ref_freq == 1'b0)) begin ref_freq_en1 <= ref_freq_en; end end always @ (posedge adsp_clk) begin spi_miso_reg <= spi_miso; end always @ (posedge adsp_clk) begin if(ref_freq_en == 1'b0) begin cnt_rd <= 4'b0000; data_bitsel <= data_width - 1'b1; end else begin if((ref_freq_reg == 1'b0) && (ref_freq == 1'b1)) ///上升沿 begin if(cnt_rd <= data_width) begin data_to_dsp_reg[data_bitsel] <= spi_miso_reg; ///相当于串行转8位并行 data_bitsel <= data_bitsel - 1'b1; cnt_rd <= cnt_rd +1'b1; end end end end assign rd_clk = (ref_freq_en & ref_freq); assign data_to_dsp = data_to_dsp_reg; endmodule
`timescale 1ns / 1ps module spi_write_data( adsp_clk, ref_freq, wr_start, data_to_flash, wr_clk, spi_mosi ); parameter data_width = 8; parameter cnt_bit = 3; input adsp_clk; input ref_freq; input wr_start; input [data_width-1:0] data_to_flash; output wr_clk; output spi_mosi; reg ref_freq_reg; reg ref_freq_en; reg ref_freq_en1; reg [cnt_bit:0] cnt; reg [cnt_bit-1:0] data_bitsel; reg [data_width-1:0] data_to_flash_reg; reg spi_mosi_reg; reg spi_mosi_reg1; always @(posedge adsp_clk) begin ref_freq_reg <= ref_freq; ///一拍延时为的是构成后期的下降沿 end always @(posedge adsp_clk) begin if(wr_start == 1'b0) //不能往SPI flash里面写数据 begin ref_freq_en <= 1'b0; cnt <= 4'b0000; data_bitsel <= data_width - 1'b1; data_to_flash_reg <= data_to_flash; end else ///此时开始往flash里面写fpga里面发过来的数据 begin if((ref_freq_reg == 1'b1) && (ref_freq == 1'b0)) ///ref_freq_reg、ref_freq=1MHz //此处是FPGA下降沿的常用写法// begin if(cnt < data_width) begin ref_freq_en <= 1'b1; spi_mosi_reg <= data_to_flash_reg[data_bitsel]; ///移位过程。FPGA发给SPI Flash的是字节(8位并行数据),而SPI只能接受串行数据,故每字节都按高位到低位读取 data_bitsel <= data_bitsel - 1'b1; ///相当于并转串 cnt <= cnt + 1'b1; end else begin ref_freq_en <= 1'b0; //ref_freq_en 为控制data_to_flash读写的 end end end end always @(posedge adsp_clk) begin if((ref_freq_reg == 1'b1) && (ref_freq == 1'b0)) begin ref_freq_en1 <= ref_freq_en; spi_mosi_reg1 <= spi_mosi_reg; end end assign spi_mosi = spi_mosi_reg1; assign wr_clk = (ref_freq_en1 & ref_freq); ///ref_freq_en==1有效时,可对spiflash进行写入;ref_freq是为1MHz的信号。也即当ref_freq_en==1写入有效时,写时钟为1MHz endmodule
所选型的存储芯片型号为GD25B128E,简介如图所示,该芯片读写方式与M25P16相类似,结合FPGA的spi接口模块逻辑设计,笔者给出FLASH的软件驱动代码作为参考,该驱动代码主要控制FPGA的spi_interface接口逻辑模块,间接性地操控FPGA对FLASH的读写操作,本质上是上位机对FLASH的读写操作。
FLASH驱动代码
flash.c
//============================================================================== // // Title: flash.c // Purpose: A short description of the implementation. // // Created on: 2023/9/13 at 17:52:11 by Windows User. // Copyright: P R C. All Rights Reserved. // //============================================================================== //============================================================================== // Include files #include <Ivi.h> #include "flash.h" #include "stdio.h" //地址定义 #define regW_spi_wr 0x12C #define regW_spi_rd 0x12d #define regW_wrflash_data 0x12e #define regW_spi_flash_cs 0x12f #define regW_fpga_prog_b_ctrl 0x130 #define regW_sw_sel 0x131 #define regW_sw_en 0x132 #define regW_fpga_prog_b_en 0x133 #define regR_rdflash_data 0x12C //高低电平定义 #define sHigh 0x01 #define sLow 0x00 //命令定义 转为32位 #define Fcmd_05H 0x05 //Read Status Register-1 #define Fcmd_35H 0x35 //Read Status Register-2 #define Fcmd_15H 0x15 //Read Status Register-3 #define Fcmd_06H 0x06 //Write Enable #define Fcmd_60H 0x60 //Chip Erase #define Fcmd_C7H 0xC7 //Chip Erase #define Fcmd_20H 0x20 //Sector Erase #define Fcmd_02H 0x02 //Page Program #define Fcmd_03H 0x03 //Read Data extern ViSession vi; //定义一个 全局fifo 用来上位机下发数据 char *mcs_data_p = NULL; void delayunit() { int i,j; for(i=0; i<1; i++) { for(j=0; j<100; j++); } } void delayus(unsigned int us ) { int i; for(i=0; i<us; i++) { delayunit(); } } /****************************************************************** 函数名:Spi_sendByte 备注: 上位机通过pcie写入一个字节,然后fpga的spi并转串发送 参数: @pByte 要写入的字节 备注: *****************************************************************/ void Spi_sendByte(unsigned char pByte) { ViSession io = Ivi_IOSession(vi); WriteReg(io, regW_wrflash_data, (unsigned int)pByte); // 上位机下发一个字节 delayus(1); // 延时 WriteReg(io, regW_spi_wr, sHigh); // 拉高spi_wr spi指令发送 delayus(10); // 延时 WriteReg(io, regW_spi_wr, sLow); // 拉低spi_wr delayus(1); // 延时 } /****************************************************************** 函数名:Spi_recvByte 备注: fpga的spi串转并接收一个字节,上位机通过pcie总线读接收到 参数: 返回一个字节 备注: *****************************************************************/ unsigned char Spi_recvByte(void) { ViSession io = Ivi_IOSession(vi); unsigned int rd_data; delayus(1); WriteReg(io, regW_spi_rd, sHigh); // 拉高spi_rd spi指令发送 delayus(10); // 延时 WriteReg(io, regW_spi_rd, sLow); // 拉低spi_rd rd_data = ReadReg(io, regR_rdflash_data); //读取数据 delayus(1); // 延时 return ((unsigned char)rd_data); } /****************************************************************** 函数名:Flash_wait_ready 功能: 不断访问寄存器1的WIP值,等待flash内部准备完成 参数: 备注: *****************************************************************/ void Flash_wait_ready(void) { ViSession io = Ivi_IOSession(vi); unsigned char rState; unsigned char fBusy = 1; while(fBusy) { WriteReg(io, regW_spi_flash_cs, sLow); // cs拉低 delayus(5); Spi_sendByte(Fcmd_05H); // 发送读取状态寄存器1的命令05H rState = Spi_recvByte(); // 接收返回的状态寄存器1值 delayus(5); WriteReg(io, regW_spi_flash_cs, sHigh); // cs拉高 if((rState & 0x01) == 0) // 判断WIP的值,若为高,代表flash繁忙,等待flash准备好; { //若为低,代表flash空闲,可进行下一步操作,跳出循环 //fBusy = 0; break; } delayus(200); } } /****************************************************************** 函数名:Flash_write_command 功能: 仅往flash里写入一个字节的命令 参数: @Fcmd 要写入flash的命令 备注: *****************************************************************/ void Flash_write_command(unsigned int Fcmd) { ViSession io = Ivi_IOSession(vi); WriteReg(io, regW_spi_flash_cs, sLow); // cs拉低 delayus(5); Spi_sendByte((unsigned char)Fcmd); // 发送命令 delayus(5); WriteReg(io, regW_spi_flash_cs, sHigh); // cs拉高 } /****************************************************************** 函数名:Flash_Chip_Erase 功能: flash 擦除全部,擦除后值都是1 命令 60H或C7H 参数: @Fcmd 要写入flash的命令 备注: *****************************************************************/ void Flash_Chip_Erase() { ViSession io = Ivi_IOSession(vi); Flash_write_command(Fcmd_06H); // 让flash处于可写状态 delayus(100); Flash_write_command(Fcmd_C7H); // 写入擦除命令 delayus(100); Flash_wait_ready(); // 等待flash内部完成擦除工作 delayus(100); } /****************************************************************** 函数名:Flash_Sector_Erase 功能: flash 扇区擦除 命令20H 参数: @flashAddr 擦除扇区的地址 备注: *****************************************************************/ void Flash_Sector_Erase(unsigned int flashAddr) { ViSession io = Ivi_IOSession(vi); Flash_write_command(Fcmd_06H); // 让flash处于可写状态 delayus(100); // printf("flash Sector Erase ing...\n"); WriteReg(io, regW_spi_flash_cs, sLow); // cs拉低 delayus(5); Spi_sendByte(Fcmd_20H); // 上位机下发命令-Sector_Erase- Spi_sendByte(((flashAddr & 0x00FF0000)>>16)); // 下发起始地址 16bit~24bit Spi_sendByte(((flashAddr & 0x0000FF00)>>8)); // 下发起始地址 8bit~16bit Spi_sendByte(((flashAddr & 0x000000FF)>>0)); // 下发起始地址 0bit~8bit delayus(5); WriteReg(io, regW_spi_flash_cs, sHigh); // cs拉高 delayus(100); Flash_wait_ready(); // 等待flash工作完成扇区擦除 delayus(100); // printf("finsh Sector Erase!\n"); } /****************************************************************** 函数名:Flash_Read_State 功能: 读取状态寄存器的值,有三个状态寄存器,分别是State1->05H,State2->35H,state3->15H 参数: @Fcmd 要读取的寄存器命令 备注: *****************************************************************/ unsigned char Flash_Read_State(unsigned char Fcmd) { ViSession io = Ivi_IOSession(vi); unsigned char rState; WriteReg(io, regW_spi_flash_cs, sLow); // cs拉低 delayus(5); Spi_sendByte(Fcmd); // 发送读取状态寄存器1的命令05H rState = Spi_recvByte(); // 接收返回的状态寄存器1值 delayus(5); WriteReg(io, regW_spi_flash_cs, sHigh); // cs拉高 return rState; // 返回状态值 } /****************************************************************** 函数名:Flash_Page_Program 功能: flash的页写入操作 参数: @flashAddr 写入页的起始地址24bit @lenth 写入的数据长度 @dataArry[] 需要写入的数据数组 备注:长度lenth要小于等于256个字节 *****************************************************************/ void Flash_Page_Program(unsigned int flashAddr, unsigned char dataArry[], int lenth) { ViSession io = Ivi_IOSession(vi); int num; unsigned char wState=0; unsigned char ReadDataArry[lenth]; Flash_write_command(Fcmd_06H); // 先让flash处于可写状态 delayus(50); WriteReg(io, regW_spi_flash_cs, sLow); // cs拉低 delayus(5); Spi_sendByte(Fcmd_02H); // 上位机下发命令-page program- Spi_sendByte(((flashAddr & 0x00FF0000)>>16)); // 下发起始地址 16bit~24bit Spi_sendByte(((flashAddr & 0x0000FF00)>>8)); // 下发起始地址 8bit~16bit Spi_sendByte(((flashAddr & 0x000000FF)>>0)); // 下发起始地址 0bit~8bit for(num = 0; num < lenth; num++) // 连续写入lenth个数据 { Spi_sendByte(dataArry[num]); } delayus(5); WriteReg(io, regW_spi_flash_cs, sHigh); // cs拉高 delayus(50); Flash_wait_ready(); } /****************************************************************** 函数名:Flash_Read_Data_Byte 备注: flash进行数据读取 参数: @flashAddr 要读取的起始地址24bit @lenth 要读取的数据长度 @dataArry[] 读取到数据要存放的数组 备注:lenth长度不限 *****************************************************************/ void Flash_Read_Data_Byte(unsigned int flashAddr, unsigned char dataArry[], int lenth) { ViSession io = Ivi_IOSession(vi); int num; unsigned char readData; WriteReg(io, regW_spi_flash_cs, sLow); // cs拉低 delayus(5); Spi_sendByte(Fcmd_03H); // 上位机下发命令-Read Data Bytes- Spi_sendByte(((flashAddr & 0x00FF0000)>>16)); // 下发起始地址 16bit~24bit Spi_sendByte(((flashAddr & 0x0000FF00)>>8)); // 下发起始地址 8bit~16bit Spi_sendByte(((flashAddr & 0x000000FF)>>0)); // 下发起始地址 0bit~8bit for(int NUM = 0; NUM < lenth; NUM++) // 读取十个数据,并存放到数组flash_arry[] { readData = Spi_recvByte(); dataArry[NUM] = (unsigned char)readData; } delayus(5); WriteReg(io, regW_spi_flash_cs, sHigh); // cs拉高 } /****************************************************************** 函数名:Test_PageWrite() 功能: 往flash里页写入个数,然后在读回来比较,测试 参数: @PageNum 测试页的个数 备注: *****************************************************************/ void Test_PageWrite(long int PageNum) { unsigned char testArry[257] = {0}; unsigned char flash_arry[257] = {0}; unsigned int WAddr = 0; unsigned int RAddr = 0; unsigned int cnt_reg = 0; int ff_flag=0; //全部擦除 Flash_Chip_Erase(); //页写入的值 for(int i=0; i<256; i++) { testArry[i]=0xaa; } printf("page program Start...\n"); for(int iii=0; iii<PageNum; iii++) { Flash_Page_Program(WAddr, testArry, 256); if(((iii*100)/PageNum)>cnt_reg) { cnt_reg = (iii*100)/PageNum; printf("%d%%\n", cnt_reg); } WAddr += 0x100; Delay(0.001); } printf("100%%\n"); printf("page program finish!\n"); cnt_reg = 0; printf("check page program...\n"); for(int ii=0; ii<PageNum; ii++) { Flash_Read_Data_Byte(RAddr, flash_arry, 256); for(int NUM = 0; NUM < 256; NUM++) { if(flash_arry[NUM] != 0xff) { ff_flag++; } } //检查0xff if(ff_flag == 0) // 此时读到的全为0xff,记录地址 { printf("err RAddr:%x\n", RAddr); } //打印进度 if(((ii*100)/PageNum)>cnt_reg) { cnt_reg = (ii*100)/PageNum; printf("%d%%\n", cnt_reg); } RAddr += 0x100; ff_flag = 0; //复位错误0xff标志位 } printf("100%%\n"); printf("Check PP finish!\n"); } /****************************************************************** 函数名:TestPressure_PageWrite() 功能: 往flash里页写入个数,然后在读回来比较,测试 参数: @PageNum 测试页的个数 备注: *****************************************************************/ void TestPressure_PageWrite(long int PageNum) { unsigned char testArry[257] = {0}; unsigned char flash_arry[257] = {0}; unsigned int WAddr = 0; unsigned int RAddr = 0; unsigned int cnt_reg = 0; int ff_flag=0; //全部擦除 Flash_Chip_Erase(); //页写入的值 for(int i=0; i<256; i++) { testArry[i] = i; } // 写入flash printf("page program Start...\n"); for(int iii=0; iii<PageNum; iii++) { Flash_Page_Program(WAddr, testArry, 256); if(((iii*100)/PageNum)>cnt_reg) { cnt_reg = (iii*100)/PageNum; printf("%d%%\n", cnt_reg); } WAddr += 0x100; Delay(0.001); } printf("100%%\n"); printf("page program finish!\n"); // 检查flash的值 cnt_reg = 0; printf("check page program...\n"); for(int ii=0; ii<PageNum; ii++) { Flash_Read_Data_Byte(RAddr, flash_arry, 256); for(int NUM = 0; NUM < 256; NUM++) { if(flash_arry[NUM] != testArry[NUM]) { ff_flag++; } } // 判断并打印出错的页地址 if(ff_flag != 0) { printf("err RAddr:%x\n", RAddr); } // 打印进度 if(((ii*100)/PageNum)>cnt_reg) { cnt_reg = (ii*100)/PageNum; printf("%d%%\n", cnt_reg); } RAddr += 0x100; ff_flag = 0; } printf("100%%\n"); printf("Check PP finish!\n"); }
这软件部分主要对.mcs文件进行预处理,将mcs文件转换成 在flash里存储FPGA的配置文件,即.bin文件。如上1.3部分所述mcs、bit和bin三者之间的关系,因为mcs文件包含了地址、数据和校验等信息,且是ASCII形式的文件,预处理操作即是.mcs文件将不必要的配置信息(地址、数据和校验等)去掉,且将ASCII格式转换成16进制格式文件的操作。实际上也可以直接将.bin文件直接写入到flash中。这部分是师兄写的,就不附上全码了,下面附上部分参考代码,仅供参考代码思路,结合.mcs文件结构来看比较容易懂。最重要的部分,我们实际只用把类型为type 00的数据段读取出来即可,再次推荐这篇博客(https://blog.csdn.net/hanhailong0525/article/details/122382501):
/****************************************************************************** * @fn static uint8_t xilUpdate_TransASCII2HEX(_In_ char ascii) * @brief 将ascii转为Hex * @param[in] ascii : 字符码 * @param[out] 无 * @return Hex : 转换后的hex * @note ******************************************************************************/ static uint8_t xilUpdate_TransASCII2HEX(_In_ char ascii) { if ((ascii >= 'A') && (ascii <= 'F')) return ascii - 'A' + 10; else if ((ascii >= 'a') && (ascii <= 'z')) return ascii - 'a' + 10; else return ascii - '0'; } /****************************************************************************** * @fn uint32_t xilUpdate_FileTotalSize(_In_ const char * path) * @brief 获取文件大小 (以字节为单位) * @param[in] path : 文件路径 * @param[out] 无 * @return size : 文件大小 * @note ******************************************************************************/ uint32_t xilUpdate_FileTotalSize(_In_ const char * path) { uint32_t size = 0; FILE * fp = NULL; if ((fp = fopen(path, "r")) == 0) return 0; fseek(fp, 0, SEEK_END); size = ftell(fp); fclose(fp); return size; } /****************************************************************************** * @fn sint32_t xilUpdate_TranslateMCS2BIN(_In_ const char * mcsPath, * _In_ const char * binPath) * @brief 将MCS文件转换为BIN文件 * @param[in] mcsPath : MCS文件路径 * @param[in] binPath : BIN文件路径 * @param[out] 无 * @return retVal < 0 : 错误码 * retVal > 0 : BIN文件大小 * @note ******************************************************************************/ sint32_t xilUpdate_TranslateMCS2BIN(_In_ const char * mcsPath, _In_ const char * binPath) { uint8_t checkSum = 0; sint32_t MCSEof = FALSE; sint32_t error = UPDATE_ERROR_NONE; char * mcsDat = NULL; FILE * mcs_fp = NULL; FILE * bin_fp = NULL; M2B_Data M2Bdat = { 0 }; MCS_DataFormat * datFmt = NULL; mcs_fp = fopen(mcsPath, "r"); bin_fp = fopen(binPath, "wb"); if ((mcs_fp == NULL) || (bin_fp == NULL)) return 0; mcsDat = (char *)malloc(sizeof(char) * 64); memset(mcsDat, 0, 64); datFmt = (MCS_DataFormat *)mcsDat; while (MCSEof == FALSE) { fgets(mcsDat, 64, mcs_fp); ///< 按行读取 if (datFmt->header != ':') { error = UPDATE_ERROR_FORMAT_ERR; goto Exit; } M2Bdat.byteCnt = (uint8_t)((ASCII2HEX(datFmt->byteCnt[0]) << 4) + ASCII2HEX(datFmt->byteCnt[1]));///< 数据长度 M2Bdat.recType = (uint8_t)((ASCII2HEX(datFmt->recType[0]) << 4) + ASCII2HEX(datFmt->recType[1]));///< 数据类型 M2Bdat.hexAddr = (uint16_t)((ASCII2HEX(datFmt->hexAddr[0]) << 12) + (ASCII2HEX(datFmt->hexAddr[1]) << 8) + (ASCII2HEX(datFmt->hexAddr[2]) << 4) + (ASCII2HEX(datFmt->hexAddr[3])));///< 地址 checkSum = M2Bdat.byteCnt + M2Bdat.recType + ((M2Bdat.hexAddr >> 8) & 0xff) + (M2Bdat.hexAddr & 0xff); switch (M2Bdat.recType) { case MCS_TYPE_DATA_RECORD : { ///< Type = 0: Data Record(数据记录) for (int cnt = 0; cnt < (M2Bdat.byteCnt << 1); cnt += 2) ///< 读取数据 M2Bdat.dataRecord[cnt >> 1] = (uint8_t)((ASCII2HEX(datFmt->data.dataRecord[cnt]) << 4) + ASCII2HEX(datFmt->data.dataRecord[cnt + 1])); for (int cnt = 0; cnt < M2Bdat.byteCnt; cnt ++) ///< 计算校验和 checkSum += M2Bdat.dataRecord[cnt]; M2Bdat.checkSum = (uint8_t)(((ASCII2HEX(*(mcsDat + 9 + (M2Bdat.byteCnt << 1)))) << 4) + ASCII2HEX(*(mcsDat + 9 + (M2Bdat.byteCnt << 1) + 1)));///< 校验和 M2Bdat.dataCounter += M2Bdat.byteCnt; fwrite(&M2Bdat.dataRecord[0], 1, M2Bdat.byteCnt, bin_fp); ///< 写入数据 }break; case MCS_TYPE_END_OF_FILE : { ///< Type = 1: End of File Record(文件结尾记录) MCSEof = TRUE; }break; case MCS_TYPE_EXT_SEG_ADDR : { ///< Type = 2: Extended Segment Address Record(段地址记录) M2Bdat.segAddress = (ASCII2HEX(datFmt->data.segAddress[0]) << 12)///< 读取段地址 + (ASCII2HEX(datFmt->data.segAddress[1]) << 8) + (ASCII2HEX(datFmt->data.segAddress[2]) << 4) + (ASCII2HEX(datFmt->data.segAddress[3])); M2Bdat.checkSum = (uint8_t)((ASCII2HEX(*(mcsDat + 13)) << 4) + ASCII2HEX(*(mcsDat + 14)));///< 读取校验和 checkSum += (M2Bdat.segAddress & 0xff) + ((M2Bdat.segAddress >> 8) & 0xff);///< 计算校验和 fseek(bin_fp, M2Bdat.segAddress << 16, SEEK_SET); ///< 设定写入位置 }break; case MCS_TYPE_EXT_LINEAR_ADDR : { ///< Type = 4: Extended Linear Address Record(线性地址记录) M2Bdat.offsetAddr = (ASCII2HEX(datFmt->data.offsetAddr[0]) << 12)///< 读取线性地址 + (ASCII2HEX(datFmt->data.offsetAddr[1]) << 8) + (ASCII2HEX(datFmt->data.offsetAddr[2]) << 4) + (ASCII2HEX(datFmt->data.offsetAddr[3])); M2Bdat.checkSum = (uint8_t)((ASCII2HEX(*(mcsDat + 13)) << 4) + ASCII2HEX(*(mcsDat + 14)));///< 读取校验和 checkSum += (M2Bdat.offsetAddr & 0xff) + ((M2Bdat.offsetAddr >> 8) & 0xff);///< 计算校验和 fseek(bin_fp, M2Bdat.offsetAddr << 16, SEEK_SET); ///< 设定写入位置 }break; default : break; } checkSum = (uint8_t)(0x100 - checkSum); if ((MCSEof == FALSE) && (checkSum != M2Bdat.checkSum)) ///< 检测校验和 { error = UPDATE_ERROR_CHECK_FAIL; goto Exit; } } error = (sint32_t)M2Bdat.dataCounter; Exit: free(mcsDat); fclose(mcs_fp); fclose(bin_fp); return error; } /****************************************************************************** * @fn sint32_t xilUpdate_ProgramBIN2FLASH(_In_ const char * binPath) * @brief 在线升级,往FLASH写入bin文件 * @param[in] binPath : bin文件路径 * @param[out] 无 * @return retVal < 0 : 错误码 * retVal > 0 : BIN文件大小 * @note ******************************************************************************/ sint32_t xilUpdate_ProgramBIN2FLASH(_In_ const char * binPath) { uint32_t fsize; uint32_t wrNum = XILUPDATE_SET_FLASH_OPS_SIZE; // 页的大小,256个字节 uint32_t blknum; uint32_t counter = 0; sint32_t error = UPDATE_ERROR_NONE; uint32_t cnt_reg = 0; unsigned char flash_arry[260]; uint8_t * datBuf = NULL; FILE * fp = NULL; printf("Update Online Begin:\n"); fsize = xilUpdate_FileTotalSize(binPath); ///< 获取文件大小 if ((fp = fopen(binPath, "rb")) == NULL) return 0; datBuf = (uint8_t *)malloc(sizeof(uint8_t) * XILUPDATE_SET_FLASH_OPS_SIZE); blknum = fsize / XILUPDATE_SET_FLASH_OPS_SIZE; ///<blknum表示页的字节大小,为256个字节 blknum += (fsize % XILUPDATE_SET_FLASH_OPS_SIZE > 0) ? 1 : 0; ///< 计算写入的数据块数量,即要写入flash中要有多少页 /* 擦除flash */ if ( flash_ops.flash_EraseChip != NULL) printf("Flash Chip Erase ing...\n"); flash_ops.flash_EraseChip(); printf("Chip Erase Done!\n"); /* 开始往flash写入数据 */ printf("Program Bin To Flash ing...\n"); printf("0%%"); for (uint32_t cnt = 0; cnt < blknum; cnt ++) { fread(datBuf, 1, wrNum, fp); ///< 读取BIN文件 if (flash_ops.flash_WriteBuffer != NULL) ///< 每次按页的大小写入flash flash_ops.flash_WriteBuffer(XILUPDATE_SET_FLASH_PRAMADDR + cnt * XILUPDATE_SET_FLASH_OPS_SIZE, datBuf, wrNum);//该函数第一个参数为flash地址;第二个参数为要写入flash的数据缓存;第三个参数为要写入flash的数据量 counter += wrNum; wrNum = ((fsize - counter) > XILUPDATE_SET_FLASH_OPS_SIZE) ? XILUPDATE_SET_FLASH_OPS_SIZE : fsize - counter; if(((cnt*100)/blknum)>cnt_reg) { cnt_reg = (cnt*100)/blknum; printf("\r%d%%", cnt_reg); } } printf("\r100%%\n"); printf("Program Bin To Flash Done!\n"); error = (sint32_t)counter; Exit: free(datBuf); fclose(fp); return error; }
实验结果:基于flash的在线升级,flash的读写时钟频率为1MHz,升级的.mcs文件大小为30MB,实际写入flash的文件.bin大小为11MB,整个升级过程耗时约5min,升级完成后掉电重启,fpga能够自配置,并能够实现所升级的功能,表明升级成功。
总结:要理解FPGA的升级就是FPGA配置文件的更换,基于flash的FPGA升级就是要把新的配置文件替代掉原来存放flash的旧配置文件,使得每次FPGA上电重启都是更具新的配置文件进行配置。本次实验关键的地方:
文章写的有点草率,若能够帮到路过的你,是我的荣幸。你们的点赞,是我写文章的动力(比心),欢迎大家学习交流~
最后建议希望对大家有用:做技术自己单干对自己提升肯定很大,但一定不要闷头自己死磕,遇到瓶颈一定要多问,千万别憋大招,技术有交流才能更快的成长,不是每个坑都踩一遍才算成长,能知道坑在哪里不去踩也算成长!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。