在含有CPLD芯片的电子产品中,由于代码中的BUG需要升级固件,如果以前的固件内没有离线烧写系统,那么必须要通过专门的烧写工具把固件下载到CPLD中去(如USB Blaster),但这样非常繁琐,而且不方便售后服务,所以代码内集成离线烧写系统的重要性可见一斑。
废话不多说,直接进入正题!!!
方案一:ALTERA 官方Jam STAPL Byte-Code Player Version 2.2
点击官方源码跳转到官方下载页面
该方案可移植到DOS、嵌入式(包括STM32等MCU)、windows等平台,采用.jbc二进制文件(可以由quartus软件生成)作为程序文件进行编程。
移植到MCU(STM32为例子)额外注意事项如下:
1、JBC文件中的数据到提出出来存放到一个数组中(由jbc2data.c得到)
2、移植参考STM32的CPLD离线烧写系统设计.pdf、源码中的readme.txt、AN 122 Using Jam STAPL for ISP via an Embedded Processor.pdf文档
3、根据平台屏蔽或者删除掉不相关的函数、头文件
4、Jam STAPL Byte-Code Playe源码中的标准请参考jesd71_stapl.pdf
5、源码本身占用大概20K的空间,另外还要占用大概16K以上的RAM空间(因为代码中申请了非常多的堆空间)
6、.jbc文件可由quartus II ->代码下载页面 -> file -> create Jam、Svf、Jbc… -> Jam STAPL Byte-Code 2.0(.jbc)生成 (方案二的.svf文件也在这里生成,一般选择1MHz的TCK频率)
移植后成功升级CPLD固件的输出信息:
方案二:由SVF文件获得编程细节,模拟JTAG进行编程
重要参考文章:
1、MCU模拟JTAG接口对LATTICE CPLD FPGA 进行在线编程加载
NOTE:
1)、该文章内JTAGState结构体中的Pattern为TMS对应不同状态的值,如IDLE->SHIFTIR,TMS从1->1->0->0变化(计为0xC0),共4个TCK。
2)、Lattice与Altera的SVF文件有很大区别,所以只需要看懂Excute_SIR、SDR、Set_JTAG_State_Machine()等几个函数即可
2、ARM JTAG 调试原理.pdf(仔细读懂里面的状态机转换)
SVF文件部分细节如下(生成方法同生成.jbc文件类似,注意TCK频率不宜过高,1MHz即可):
!
!
!
TRST ABSENT;
ENDDR IDLE;
ENDIR IRPAUSE;
STATE IDLE;
SIR 10 TDI (2CC);
RUNTEST IDLE 1003 TCK ENDSTATE IDLE;
!
!
!
!CHECKING SILICON ID
!
!
!
SIR 10 TDI (203);
RUNTEST 8 TCK;
SDR 13 TDI (0089);
SIR 10 TDI (205);
RUNTEST 8 TCK;
SDR 16 TDI (FFFF) TDO (8232) MASK (FFFF);
SDR 16 TDI (FFFF) TDO (2AA2);
SDR 16 TDI (FFFF) TDO (4A82);
SDR 16 TDI (FFFF) TDO (8C0C);
SDR 16 TDI (FFFF) TDO (0080);
方案思路:因为.svf文件描述的是JTAG的编程细节,只要我们从.svf文件中提取出相应数据,包括指令数据、输入输出数据等,然后按照对应指令执行即可。
例子:
SIR 10 TDI (203);
该语句表示发送10位指令数据0x203;通过算法处理该语句,提取出该语句的有效信息为"IR"指令、“10"位数据、数据为“0x203”,MCU模拟JTAG编程时,先匹配命令"IR",再发送相应位数的指令数据即可,其他命令同理可得(提取算法需要自己写,可用Qt的正则表达式提取)。
//命令可以用如下数据对应
typedef enum{
RESET_CMD = 0x00,
IDLE_CMD = 0x01,
RUN_TCK = 0x02,
RUN_IDLE_TCK = 0x03,
SIR_CMD = 0x04,
SDR_DIR = 0x05,
SDR_TDO = 0x06,
SDR_TDO_MASK = 0x07,
INVALID_CMD = 0xFF
}JTAG_CMD;
重要提示:.jbc的编程波形几乎与svf文件中描述的一致,所以可以通过.jbc文件的编程波形(由逻辑分析仪捕捉得到)分析、修改自己的代码
NOTE:文章末尾有部分.jbc运行编程时测得的波形文件(由方案一得到,逻辑分析仪软件为KingstVIS,方案一的波形与quratus II直接下载.jbc文件的波形主要区别于IDLE状态下,方案一中TCK为低,quartus一直运行TCK)
由KingstVIS逻辑分析仪得到的.jbc编程波形
步骤一:
提取svf文件中的数据存到对应数组,分别存到命令数组、数据数组、TCK数组、TDI数组、TDO数组、MASK数组中
#define Get_CMD(x) cmd_arry[x]
#define Get_DATA(x) data_arry[x]
#define Get_TCK(x) tck_arry[x]
#define Get_TDI(x) tdi_arry[x]
#define Get_TDO(x) tdo_arry[x]
#define Get_MASK(x) mask_arry[x]
步骤二:
匹配对应命令,然后指令对应指令
//成功返回0
//错误代码:
//1:Excute_SIR
//2:Excute_SDR
//3和4:输出与预期值不符合
BYTE Jtag_Excute(void){
DWORD tck=0,cmd_index=0,data_index=0,tck_index=0,tdi_index=0,tdo_index=0,mask_index=0;
DWORD cmd_size= CMD_FILE_SIZE;
JtagGpioInit();
while(cmd_size--){
//提取命令
switch(Get_CMD(cmd_index++)){
case RESET_CMD:
CurState = JBIRESET;
Set_JTAG_State_Machine(JBIRESET);
delay_us(1000);
break;
case IDLE_CMD:
Set_JTAG_State_Machine(IDLE);
break;
case RUN_TCK:
if(Get_TCK(tck_index) == 50000) tck = 500003;
else tck = Get_TCK(tck_index);
delay_us(tck);
tck_index++;
break;
case RUN_IDLE_TCK:
Set_JTAG_State_Machine(IDLE);
if(Get_TCK(tck_index) == 50000) tck = 500003;
else tck = Get_TCK(tck_index);
delay_us(tck);
tck_index++;
break;
case SIR_CMD:
if(JBISUCCESS != Excute_SIR(Get_TDI(tdi_index++),Get_DATA(data_index++))) return 1;
break;
case SDR_DIR:
if(JBISUCCESS != Excute_SDR(Get_DATA(data_index++),Get_TDI(tdi_index++),0,0)) return 2;
break;
case SDR_TDO:
TDO_DATA = Get_TDO(tdo_index++);
if(JBISUCCESS != Excute_SDR(Get_DATA(data_index++),Get_TDI(tdi_index++),1,0)) return 3;
break;
case SDR_TDO_MASK:
TDO_DATA = Get_TDO(tdo_index++);
TDO_MASK = Get_MASK(mask_index++);
if(JBISUCCESS != Excute_SDR(Get_DATA(data_index++),Get_TDI(tdi_index++),1,1)) return 4;
break;
default:
break;
}
}
Set_JTAG_State_Machine(IDLE);
Set_JTAG_State_Machine(JBIRESET);
printf("CPLD 代码升级成功\r\n");
return 0;
}
NOTE:方案一源码占用资源非常多,而且代码量非常大,方案二几乎不占用RAM资源,而且真正的代码就只有一百多行,非常便于理解,同样非常适用于RAM资源紧张的MCU中。
资料总结:
MCU模拟JTAG烧写CPLD固件的文档资料:https://download.csdn.net/download/weixin_42518229/12912882
Altera CPLD升级官方Demo 移植到STM32F103ZGT6工程
https://download.csdn.net/download/weixin_42518229/18944029