libero PolarFire soc SPI-DirectC 实战 生成 固件 包含 softconsole 生成 的 HEX 文件 - 所长 - 博客园
1、参考文档 SPI-DirectC User Guide,下图是 系统框图, 可以使用 一个芯片 通过SPI接口 烧录文件到 目标设备 ,目标设备支持 smartfusion2 polarfire 、以及 polarfire soc 系列
![image]()
2、升级文件 格式描述
![image]()
3、实际文件 截图
![image]()
4、字段1 , Designer Version Number 24字节
G5M-Designer
5、字段2 , Header Size , 0x 48 = 72 字节
6、字段3 , Image Size, 4个 字节 0x90C232 = 9486898 字节 ,正好 整个文件大小!
7、字段4 ,DAT File Version 1个字节 0x05
8、字段5, Tools Version Number 2个字节 = 0x0034
9、字段6 , Map Version Number 2个字节 = 0x0000
10、字段7 , Feature Flag = 0x0000
11、Device Family = 0x08 , 正好和 SPI-DirectC / dpalg.h/ 137行/ 代码中 #define G5SOC_DEVICE_FAMILY 0x8u
12、Device ID = 0x0F81A1CF
13、 Device ID Mask = 0x0F FF FF FF
14、Silicon Signature 0x00 1c 1c 1e 签名
15、Checksum = 0x3FC0
16 、Number of BSR Bits = 0x064F
17、Number of Components = 0x0008
18、Data Size = 0x0005
19、Erase Data Size = 0x0003
20、Verify Data Size = 0x0000
21、ENVM Data Size = 0x00 00
22、ENVM Verify Data Size = 0x00 00
23、UEK1_EXISTS 0x00
后面都是0 , 最后 多出 两个字节, 0x0006 无法解析,
header 大小这里是 72 字节 , 和 文档 对不上 ,文档里只有 70字节 相关解析!
数据块 编号 0x02 0x03 0x04 0x05 0x08 0x09
![image]()
数据块 编号 对应 含义 :DirectC | Microchip Technology SPI-DirectC / dpalg.h / 87行
#define GXM_Header_ID 0u
#define GXM_USER_INFO_ID 1u
#define GXM_ACT_UROW_DESIGN_NAME_ID 2u
#define GXM_BsrPattern_ID 3u
#define GXM_BsrPatternMask_ID 4u
#define GXM_NUMBER_OF_BLOCKS_ID 5u
#define GXM_UPK1_ID 6u
#define GXM_UPK2_ID 7u
#define GXM_datastream_ID 8u
#define GXM_erasedatastream_ID 9u
#define GXM_VerifyDataStream_ID 10u
#define GXM_EnvmDataStream_ID 11u
#define GXM_EnvmVerifyDataStream_ID 12u
#define GXM_DPK_ID 13u
02 80 00 00 00 09 00 00 00, 数据块 0x02 , 数据地址 0x00 00 00 80 , 数据长度 9个字节 , 说是9个字节,但是 不足16个字节 补的0 , 看到这里 前面 header 说是 70字节, 后面补充 2个0x00 , 满足 4字节对齐的 !
99 70 32 9C EC 97 63 85 2C 00 00 00 00 00 00 00
![image]()
03 90 00 00 00 CA 00 00 00 , 数据块 0x03, 数据地址 0x00 00 00 90, 数据长度 0xCA
![image]()
04 60 01 00 00 CA 00 00 00 数据块 0x04 , 地址 0x0160 长度 0xCA
![image]()
05 30 02 00 00 16 00 00 00 数据块 0x05 数据地址 0x230 , 数据长度 0x16 , 这个字段 22字节 分成 了 8个 22bit ,用于 记录 每次 16字节传输的 循环次数!
数据内容是
1F 00 C0 26 00 90 7A 90 8C 0A 00 1F 00 C0 07 00 B0 09 00 7C 00 00
![image]()
08 50 02 00 00 E0 BF 90 00 数据块 0x08 , 数据地址 0x250 , 数据长度 0x90BFE0
![image]()
09 A0 B4 90 00 00 00 00 00 数据块 0x09 , 数据地址 0x90B4A0 , 数据块 长度 0x00
void dp_G5M_do_program(void) 函数 解析
第一步 : 设置编程模式,发送指令 g5_pgmmode = 0x01 ;
第二步 : 读取 header 字段中 的 Data Size 字段 , 值 = 0x05, 函数如下: 这一步 把全局变量
global_uint1 = 5, global_uint2 = 1; 干啥用的 不知!
// 根据 根据偏移 55 读取 header 中的 Data Size 字段
dp_get_bytes(GXM_Header_ID,GXM_DATASIZE_OFFSET,GXM_DATASIZE_BYTE_LENGTH);
第三步:调取 dp_G5M_process_data(GXM_datastream_ID); 处理 数据块 编号 = 0x08 = GXM_datastream_ID
这里有 FOR 外循环 ,index 初始值 = global_uint2 =1 , 循环 次数 = global_uint1 = 5次; 循环多少次 ,是 生成 的文件 里 Data Size 字段 决定的。
第一次循环
下面这段操作 就是 从 22字节 里,取出 对应 22bit 的值, 22字节 共计分成 8个 22bit!
先是读取 数据块编号 = GXM_NUMBER_OF_BLOCKS_ID = 5, 数据偏移 = 0*22/8=0, 数据长度 4个字节 赋值 给 global_ulong1 ,
根据 数据块 05 内容 1F 00 C0 26 00 90 7A 90 8C 0A 00 1F 00 C0 07 00 B0 09 00 7C 00 00, 推断出 global_ulong1 = 0x26C0001F;
global_ulong1 >> 1-1)*22/%8 = 0; 这一步操作没有变化!
global_ulong1 = global_ulong1 & 0x3FFFFF = 0x1F
内循环 , 循环 次数 = global_ulong1 的值 = 31 次。
‘’‘重点’‘‘这里仔细看了下, 数据块 05 字段里 有 22字节, 22字节 分成8个 22bit , 每个 22bit 对应的值就是 内循环次数。2026年1月23日 16点19分’’‘’‘’
spi_command_buffer = G5M_SPI_FRAME_DATA;
dp_G5M_get_and_shift_in(BlockID, SPI_BUFFER_BIT_SIZE, DataIndex);
发送 指令 spi_command_buffer = G5M_SPI_FRAME_DATA = 0xEE , 读取 数据块 0x08 , BUFF 大小 128 bit = 16 字节, 数据 index = 0;
调用函数 dp_get_data 根据 数据块编号=8 , 和 数据起始位置 start_bit_index = DataIndex。
dp_G5M_check_hwstatus // 调用 接口 检测 BUSY 状态
// 将1个字节命令 0XEE 和 读取到 的 16字节数据 = 17个字节 传输给 spi 从机!
do_SPI_SCAN_in(spi_command_buffer, SPI_BUFFER_BIT_SIZE, page_buffer_ptr); (0xEE, 128, 16字节数据 );
数据 index = index + (128bit = 16 字节); 继续内循环!
#include <stdio.h>
#include <stdint.h>
// data_block_5 有 22 字节
uint8_t data_block_5[22] = {0x1F,0x00,0xC0,0x26,0x00,0x90,0x7A,0x90,0x8C,0x0A,0x00,
0x1F,0x00,0xC0,0x07,0x00,0xB0,0x09,0x00,0x7C,0x00,0x00};
uint32_t cycle_cnt = 0;
uint32_t dp_get_bytes( uint32_t byte_index )
{
// 最高位字节(左移24位),最低位字节(左移0位)
return (uint32_t)data_block_5[byte_index+3] << 24 |
(uint32_t)data_block_5[byte_index+2] << 16 |
(uint32_t)data_block_5[byte_index+1] << 8 |
(uint32_t)data_block_5[byte_index+0];
}
int main( void )
{
for( int i=1; i<=5; i++ )
{
// 内循环 次数变量 长度 是 22bit
// 因此 22字节里, 存放了 8个 22bit 的 内循环次数
// 先取出 4个字节数据, 然后 再进行移位, 在进行22bit与运算,最后得出 22bit
cycle_cnt = dp_get_bytes(((i - 1u) * 22u) / 8u);
cycle_cnt = cycle_cnt >>(((i - 1u) * 22u) % 8u);
cycle_cnt = cycle_cnt & 0x3FFFFF;
printf( "外循环=%d, 内循环=%d.\n", i, cycle_cnt );
}
printf( "end.\n" );
}
运行结果:
外循环=1, 内循环=31.
外循环=2, 内循环=155.
外循环=3, 内循环=591785.
外循环=4, 内循环=675.
外循环=5, 内循环=31.
end.
dp_G5M_erase_action 擦除 执行函数 解析 ;
擦除 也分 内循环和外循环 , 外循环 = header 里的 Number of Components 字段 = 8 -( Erase Data Size = 3 -1) , 即 结果 = 8-2=6;
外循环 从 6 开始 到 8 结束, 也就是 说 上面 那个 22字节, 也记录了 擦除 的 内循环次数 !,这也就是为什么 是 22 字节了, 前五个 是 编程 内循环次数, 后三个是 擦除内循环次数!!!!
外循环=1, 内循环=31.
外循环=2, 内循环=155.
外循环=3, 内循环=591785.
外循环=4, 内循环=675.
外循环=5, 内循环=31.
外循环=6, 内循环=31.
外循环=7, 内循环=155.
外循环=8, 内循环=31.
总结:
擦除内循环 总次数 = 31+155+31 = 217 , 每次 16字节, 217 *16 = 3472字节 = 0xD90; 数据块 9 中的 长度 是 0, 对不上 ,尴尬!
编程内循环 总次数 = 31+155+591785+675+31 = 592677,592677*16字节 = 9482832= 0x90B250 , 数据块8 中的 长度 = 0x90BFE0 对不上, 尴尬 ! 差值 = 3472 /16 = 217, 难道是 编程数据块的长度 包含了 擦除数据块的 长度,?
又去分析了一下 数据 ,上面分析是对的, 编程数据块长度 包含了 擦除数据块 长度:
// 编程 数据块 8 起始地址 0x250, 数据长度 0x90BFE0 两者之和 = 0x90C230 再加2个字节等于整个文件大小
// 擦除 数据块 9 起始地址 0x90B4A0, 数据长度0
// 分析结果: 编程数据块 中 包含了 擦除数据块
// 编程字节数 = 0x90B4A0 - 0x250 = 擦除总字节数 0X90B250= 9482832 / 16 = 内循环总次数= 592677
// 擦除字节数 = 0x90C230 - 0x90B4A0 = 擦除总字节数 0XD90= 3472 / 16 = 内循环总次数= 217
校验 流程 , 首先 发送指令g5_pgmmode = 0x02 , 再把 编程数据发过去, 如果,出错误 说明 编程失败!
文件 CRC 校验
void dp_compute_crc(void)
{
for (global_uchar2 = 0u; global_uchar2 < 8u; global_uchar2++)
{
global_uint2 = (global_uchar1 ^ global_uint1) & 0x01u;
global_uint1 >>= 1u;
if (global_uint2)
{
global_uint1 ^= 0x8408u;
}
global_uchar1 >>= 1u;
}
return;
}