【前言】 本次设计要求完成dsp实现cpld在线升级的功能。使用芯片cpld是紫光pango系列芯片PGC2KGLPG144,对应开发环境是紫光的PDS(pango design suite);dsp是tiC200系列芯片TMS320F28374S,开发环境CCS。
1. 需求
完成cpld在线升级功能开发。
2. 分析
在线升级指通过串口或者网口进行数据(本次设计指cpld的程序文件)传输,即:以除原有的通过仿真器进行的程序烧录方式之外的可实现的任意一种方法进行cpld的程序烧录或者称“程序下载”。
3. 设计
为实现串口在线升级,首先将cpld的位流文件通过串口存储至dsp的部分片上区域,随后将数据取出并下载至cpld,以实现基本功能。
3.1 spi通信设计
本次设计只考虑从板spi设计,后续简称为sspi。设计包括两个部分,第一个是sspi的初始化配置,主要是io口的配置;其他是主从板通信设计,主要是具体的引脚控制,例如字节发送函数,片选使能、禁止函数。
- 初始化配置
a. io模式:所有引脚皆配置为普通io。
b. io输出方向:片选引脚(STE)、时钟引脚(CLK)、主线(SIMO)被配置为输出方向;从线(SOMI)被配置为输入方向。
c. 同步异步:所有引脚皆配置为异步模式。 - 引脚控制
本次设计sspi引脚控制严格参照某光《Compact系列CPLD器件从SPI接口配置和编程应用指南》要求的控制时序,在每次片选使能之前手动给8个时钟。
3.2 串口设计
串口功能是本次一大核心设计。用户通过串口发送指定的串口指令以实现相应的:dsp数据存储、cpld程序下载、工程的初始化配置、其他调试功能。
- 初始化配置
a. 选择GPIO64、GPIO65分别作为dsp的专用串口接收、发送口。
b. 串口波特率配置为115200、1停止位、8数据位、无校验位。
c. 使能FIFO功能,并将接收FIFO中断电平位设置为16以尽量减少rx中断生成。 - 串口中断设计
本次串口设计仅使能rx_fifo中断,当接收字节大于或者等于16时触发一次中断。进入中断后清除中断标志以在下次满足条件时继续触发中断。同时恢复接收FIFO电平位为初始化设置的16字节,避免通信超时造成意外后果。 - 串口缓冲区设计
用户通过串口每发送16字节数据进入一次串口接收中断,dsp通过串口接收的数据会被暂时存在串口的缓冲区gx_sci_buf.ui_buf[ ]里面。这个数组被定义为16进制数组,能存放8个16位数据。每进入一次串口中断都会把接收到的16个字节数据依次赋值到这个缓冲区里面,第奇数个数据左移8位,偶数不用移位。
这个缓冲区存在的作用是:
i. 用来在存放数据至dsp的片上空间时直接从此缓冲区取8个16进制数据写到对应地址即可。定义缓冲区为8个16位数据而非16个8位数据也正是这个原因。
ii. 用来区分串口指令和要存储至dsp片上空间的数据。因为这两种数据都需要通过串口进行传输,串口指令被定义为几个特殊的数字,这几个数字是位流文件里面没有的,避免混淆,存放了不必要的数据。
3.3 数据存储
本设计使用了ti的flash_api库函数。这些函数可用于擦除、编程TMS320F2837xS设备上的Flash。
暂定dsp片上flash地址0xA0000起长度0xA600作为cpld的程序存储空间。将cpld的二进制文件转化为16进制通过串口发送时,每次发送16字节都会写相应的8个16位数据到当前的dsp片上flash地址里。当前的dsp片上flash地址会在每次进串口接收中断时更新,初始值设置为0xA0000。
3.4 数据下载
在进行工程初始化以及dsp数据存储之后,开始进行cpld的程序下载。
程序下载步骤如下:首先需要验证cpld的32位id返回码是否匹配,满足条件后对cpld进行激活处理,随后开始进行程序下载并验证,最后重置cpld以使用新的位流文件。以上几个控制动作无论是读编码还是进行唤醒、下载、验证,都是直接对cpld发送指令完成。指令相关函数参考某光的STM32工程。
3.5 其他设计
为方便后续进行功能性验证,暂时加入了emif功能文件,以便于在cpld芯片中程序正常时能够对dsp发送相关信息,通过dsp的memory browser窗口可以看到emif模块的相关信息。关于emif模块设计的更多信息请参考前一篇博客《emif实现dsp~cpld之间的数据传输》。
3.6 流程框图
4. 编码
4.1 bsp
void v_bsp_sspi_gpio_init(void)
{
GPIO_setPinConfig(GPIO_18_GPIO18);
GPIO_setQualificationMode(SSPI_CLK_PIN, GPIO_QUAL_ASYNC);
GPIO_setDirectionMode(SSPI_CLK_PIN, GPIO_DIR_MODE_OUT);
GPIO_setPinConfig(GPIO_16_GPIO16);
GPIO_setQualificationMode(SSPI_SIMO_PIN, GPIO_QUAL_ASYNC);
GPIO_setDirectionMode(SSPI_SIMO_PIN, GPIO_DIR_MODE_OUT);
GPIO_setPinConfig(GPIO_19_GPIO19);
GPIO_setQualificationMode(SSPI_STE_PIN, GPIO_QUAL_ASYNC);
GPIO_setDirectionMode(SSPI_STE_PIN, GPIO_DIR_MODE_OUT);
GPIO_setPinConfig(GPIO_17_GPIO17);
GPIO_setQualificationMode(SSPI_SOMI_PIN, GPIO_QUAL_ASYNC);
GPIO_setDirectionMode(SSPI_SOMI_PIN, GPIO_DIR_MODE_IN);
return;
}
void v_bsp_sci_init(uint32_t base)
{
v_bsp_sci_gpio_init();
SCI_disableModule(base);
SCI_setConfig(base, DEVICE_LSPCLK_FREQ, 115200, (SCI_CONFIG_WLEN_8 | SCI_CONFIG_STOP_ONE | SCI_CONFIG_PAR_NONE));
SCI_disableLoopback(base);
SCI_resetChannels(base);
SCI_enableFIFO(base);
// RX and TX FIFO Interrupts Enabled
SCI_enableInterrupt(base, SCI_INT_RXFF);
SCI_disableInterrupt(base, SCI_INT_RXERR | SCI_INT_RXRDY_BRKDT);
SCI_setFIFOInterruptLevel(base, SCI_FIFO_TX1, SCI_FIFO_RX16); // tx:<= 1byte rx:>= 16byte
SCI_enableModule(base);
Interrupt_enable(INT_SCIA_RX);
// Interrupt_enable(INT_SCIA_TX);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
SCI_performSoftwareReset(base);
SCI_resetTxFIFO(base);
SCI_resetRxFIFO(base);
return;
}
4.2 user
void v_flash_api_data_load(uint32_t start_addr, uint16 *buf, uint16_t data_num)
{
Fapi_StatusType e_status;
EALLOW;
e_status = Fapi_issueProgrammingCommand((uint32_t *)start_addr, buf, data_num, 0 , 0, Fapi_AutoEccGeneration);
while (Fapi_checkFsmForReady() != Fapi_Status_FsmReady)
{
// wait until the Flash program operation is over
}
if (e_status != Fapi_Status_Success)
{
// program error
}
EDIS;
return;
}
void v_flash_buffer_read(unsigned char *pbuffer, unsigned long current_bitstream_bytes, unsigned int num_of_byte)
{
unsigned int ui_cnt = 0;
unsigned int ui_num_of_16bits;
unsigned long ul_current_addr = FLASH_ADDRESS_INIT;
unsigned int ui_flash_data;
ul_current_addr += (uint16_t)(current_bitstream_bytes / 2);
ui_num_of_16bits = (uint16_t)(num_of_byte / 2);
for (ui_cnt = 0; ui_cnt < ui_num_of_16bits; ui_cnt++)
{
ui_flash_data = *((uint16_t *)ul_current_addr);
*pbuffer = (uint8_t)((ui_flash_data & 0xff00) >> 8);
pbuffer++;
*pbuffer = (uint8_t)((ui_flash_data & 0x00ff) );
pbuffer++;
ul_current_addr++;
}
return;
}
void v_pgc_proc(SCI_BUFFER *buf, PGC_DEVICE *dev)
{
unsigned long ui_feature_ctrl;
unsigned char uc_comd;
{
uc_comd = buf->ui_buf[0];
switch (uc_comd)
{
case UART_COMD_SHOW_DETAILS:
{
v_pango_show_status(dev);
}
break;
case UART_COMD_PROGRAM_UID:
{
v_pango_change_uid(dev, PGC_SSPI_WRDIS);
}
break;
case UART_COMD_PROGRAM_CRAM:
{
ui_feature_ctrl = gcul_feature_control_val_b;
v_pango_sspi_program_cram(dev, ui_feature_ctrl);
}
break;
case UART_COMD_PROGRAM_FLASH:
{
ui_feature_ctrl = gcul_feature_control_val_a;
v_pango_sspi_program_flash(dev, ui_feature_ctrl, &op);
}
break;
case UART_COMD_STRUCT_OP_INIT:
{
v_pango_get_operate_properties(&op);
}
break;
}
}
return;
}
4.3 main
void main(void)
{
Device_init();
DINT;
IER = 0x0000;
IFR = 0x0000;
Device_initGPIO();
Interrupt_initModule();
Interrupt_initVectorTable();
v_bsp_sspi_init();
v_bsp_sci_init(SCIA_BASE);
v_flash_api_init(&gx_flash_api);
v_pango_init();
Interrupt_register(INT_SCIA_RX, &v_sci_rx_fifo_isr);
EINT;
ERTM;
for (;;)
{
__asm ( " NOP");
}
}
unsigned int gui_sci_rx_cnt = 0;
__interrupt void v_sci_rx_fifo_isr(void)
{
unsigned long *pul_data_buffer = (uint32_t *)gx_sci_buffer.ui_buf;
unsigned long ul_addr_current;
unsigned int ui_rev_num_8bits;
unsigned int ui_rev_num_16bits;
unsigned int ui_temp;
unsigned int ui_cnt;
bool b_data_tranfer_en = false;
unsigned int ui_i;
gui_sci_rx_cnt++;
ui_rev_num_8bits = SCI_getRxFIFOStatus(SCIA_BASE); // 既然都进中断了说明fifo里已经存了16个字节了
ui_rev_num_16bits = (uint8_t)(ui_rev_num_8bits / 2);
// 地址和缓存数据刷新不及时,因为在进入中断之后才会刷新,等于是每隔16个字节才会帅新一次
ul_addr_current = ul_get_flash_api_current_addr(&gx_flash_api);
if (ui_rev_num_8bits > 0 && ui_rev_num_8bits <= e_get_rx_fifo_level(SCIA_BASE))
{
for (ui_cnt = 0; ui_cnt < ui_rev_num_16bits; ui_cnt++)
{
ui_temp = (uint16_t)uc_sci_read_rx_buf() << 8;
gx_sci_buffer.ui_buf[ui_cnt] = ui_temp | uc_sci_read_rx_buf();
}
if ((gx_sci_buffer.ui_buf[0] != 0x0000) && (gx_sci_buffer.ui_buf[0] != 0xffff))
{
if ((gx_sci_buffer.ui_buf[0] == gx_sci_buffer.ui_buf[1]) && (gx_sci_buffer.ui_buf[0] == gx_sci_buffer.ui_buf[2]) && (gx_sci_buffer.ui_buf[0] == gx_sci_buffer.ui_buf[3]) && (gx_sci_buffer.ui_buf[0] == gx_sci_buffer.ui_buf[4]) && (gx_sci_buffer.ui_buf[0] == gx_sci_buffer.ui_buf[5]) && (gx_sci_buffer.ui_buf[0] == gx_sci_buffer.ui_buf[6]) && (gx_sci_buffer.ui_buf[0] == gx_sci_buffer.ui_buf[7]))
{
if ((gx_sci_buffer.ui_buf[0] == UART_COMD_SHOW_DETAILS) || (gx_sci_buffer.ui_buf[0] == UART_COMD_PROGRAM_UID) || (gx_sci_buffer.ui_buf[0] == UART_COMD_PROGRAM_CRAM) || (gx_sci_buffer.ui_buf[0] == UART_COMD_PROGRAM_FLASH) || (gx_sci_buffer.ui_buf[0] == UART_COMD_STRUCT_OP_INIT))
{
v_pgc_proc(&gx_sci_buffer, &gx_pgc_dev);
}
else
{
b_data_tranfer_en = true;;
}
}
else
{
b_data_tranfer_en = true;
}
}
else
{
b_data_tranfer_en = true;
}
if (b_data_tranfer_en)
{
if (b_get_flash_api_active(&gx_flash_api))
{
v_flash_api_data_load(ul_addr_current, gx_sci_buffer.ui_buf, ui_rev_num_16bits);
v_flash_api_verify(ul_addr_current, pul_data_buffer);
v_flash_api_struct_update(&gx_flash_api, ui_rev_num_8bits);
}
else
{
v_flash_api_erase(FLASH_ADDRESS_INIT);
v_flash_api_erase(FLASH_SECT8);
v_flash_addr_init(&gx_flash_api);
for (ui_i = 0; ui_i < 8; ui_i++)
{
gx_sci_buffer.ui_buf[ui_i] = 0;
}
}
}
}
else
{
// 异常处理
}
SCI_setFIFOInterruptLevel(SCIA_BASE, SCI_FIFO_TX1, SCI_FIFO_RX16);
SCI_clearOverflowStatus(SCIA_BASE);
// clear interrupt flag
SCI_clearInterruptStatus(SCIA_BASE, SCI_INT_RXFF);
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
}
5. 测试
-
在进行下载之前先擦除cpld的程序,因为当前无程序,可以看到cpld对dsp无任何回复。
-
随后打开串口助手并依次在发送区输入:
a. 设备初始化:0x55555555555555555555555555555555;
b. cpld位流数据,占84956字节,为了凑足一个中断的字节数,在末尾加上4个0
c. cpld位流文件下载指令:0x44444444444444444444444444444444; -
等待cpld程序下载完毕,dsp的相应片上区域存在数据。说明cpld已下载程序并正常向dsp回复数据。
6. 说明
6.1 数据存储设备选择
综上所述,cpld的位流信息在下载之前需要提前存储到dsp的部分片上flash区域。而在当前pcs的dsp板板上存在一芯片FM25W256-G,此芯片同样具有存储数据的作用。未选择此芯片作用cpld位流信息存储设备的理由如下:cpld位流文件转化为16进制之后,占空间84956字节。而铁线存储器(也就是上述提到的芯片FM25W256-G)最大存储字节为32k字节,小于需要写入的字节个数。
6.2 sspi的io口功能选择
dsp的多数引脚都具有复用功能,因此,sspi的四个引脚除去目前配置为普通io口之外,也可以作为spi的专用io口使用,并且ti具有单独的字节发送接收方式。选择将四个引脚配置为普通io的理由如下:cpld的部分指令生效对四个sspi引脚的时序具有苛刻的要求,配置为普通io的话具有更强的灵活性。可以举个例子来理解这一点:
sspi片选使能需要在将STE引脚拉低之前给至少8个时钟周期,这个逻辑可以通过用户手动实现。但若将引脚配置为专用io口后,sspi的时钟产生总会后于片选信号使能生效,sspi的时钟信号结束也总会先于片选使能禁止,不符合cpld芯片的指令时序。
6.3 后续优化方向
- flash_api激活失败处理:当前在flash_api初始化时未对激活失败分支作处理。后续可以加入急停逻辑(如果后续认为存数据入dsp的优先级较高)或者等待逻辑。
- 串口指令、写入dsp数据验证方法改进:当前将串口指令设定为几个位流文件中不存在的比较特殊的数值。但不排除后续在cpld版本更新后恰好出现这几个数值,那时将会混淆串口指令与普通写入的数据。因为需要更新相应的数据验证方法。
- 信息打印:目前dsp仅在cpld数据下载以及下载完毕时对串口回复相应的数据。为了更加详细的了解当前系统的烧录进程,后续可以添加串口消息打印函数以更方便地将消息发送出来。
- 设备初始化:当前设备各初始化数据被设定为固定数值,如特征控制编码固定为0x30983。若后续版本升级有修改的需求,可以添加相应的功能以手动通过串口修改各参数的具体数值。