一、注意问题出现阶段
1.下属异常代码在C综合阶段功能是正确的;
2.下属异常代码在RTL综合阶段是能够正常综合的;
3.下属异常代码在RTL综合后进行C/RTL协同仿真是失败的。
一、if(j == 0)引发的c/rtl协同仿真代码案例一
void fuction
(
)
{
static ap_uint<24> win_buff[10];
#pragma HLS ARRAY_PARTITION variable=win_buff complete dim=0
for(ap_uint<16> i=0; i<height; i++) {
for(ap_uint<16> j=0; j<(cols+1); j++) {
#pragma HLS pipeline II=1
srctemp = src.read();
// ================= 错误代码 =================
for(ap_uint<4> k = 0; k < 10; k++) {
#pragma HLS UNROLL
if(j==0){
win_buff[k] = srctemp;
win_buff[k + 1] = srctemp;
win_buff[k + 2] = srctemp;
win_buff[k + 3] = srctemp;
win_buff[k + 4] = srctemp;
}
else{
win_buff[k] = win_buff[k + 1];
win_buff[k + 1] = win_buff[k + 2];
win_buff[k + 2 = win_buff[k + 3];
win_buff[k + 3] = win_buff[k + 4];
win_buff[k + 4] = srctemp;
}
}
// ==========================================================
}
}
}
二、if(j == 0)引发的c/rtl协同仿真代码案例二
void fuction
(
)
{
static ap_uint<24> win_buff[10];
#pragma HLS ARRAY_PARTITION variable=win_buff complete dim=0
for(ap_uint<16> i=0; i<height; i++) {
for(ap_uint<16> j=0; j<(cols+1); j++) {
#pragma HLS pipeline II=1
srctemp = src.read();
// ================= 错误代码 =================
if(j==0){
for(ap_uint<4> k = 0; k < 10; k++) {
#pragma HLS UNROLL
win_buff[k] = srctemp;
win_buff[k + 1] = srctemp;
win_buff[k + 2] = srctemp;
win_buff[k + 3] = srctemp;
win_buff[k + 4] = srctemp;
}
}
else{
for(ap_uint<4> k = 0; k < 10; k++) {
#pragma HLS UNROLL
win_buff[k] = win_buff[k + 1];
win_buff[k + 1] = win_buff[k + 2];
win_buff[k + 2] = win_buff[k + 3];
win_buff[k + 3] = win_buff[k + 4];
win_buff[k + 4] = srctemp;
}
}
// ==========================================================
}
}
}
三、正确的案例
void fuction
(
)
{
static ap_uint<24> win_buff[10];
#pragma HLS ARRAY_PARTITION variable=win_buff complete dim=0
for(ap_uint<16> i=0; i<height; i++) {
for(ap_uint<16> j=0; j<(cols+1); j++) {
#pragma HLS pipeline II=1
srctemp = src.read();
// ================= 错误代码 =================
for(ap_uint<4> k = 0; k < 10; k++) {
#pragma HLS UNROLL
win_buff[k] = win_buff[k + 1];
win_buff[k + 1] = win_buff[k + 2];
win_buff[k + 2] = win_buff[k + 3];
win_buff[k + 3] = win_buff[k + 4];
win_buff[k + 4] = srctemp;
}
// ==========================================================
}
}
}
四、失败的原因分析
1.失败的日志
Vivado Simulator 2018.3
ERROR: [Simtcl 6-50] Simulation engine failed to start: Failed to launch child process (child exe not found).
Please see the Tcl Console or the Messages for details.
ERROR: [COSIM 212-303] Aborting co-simulation: RTL simulation failed.
ERROR: [COSIM 212-344] Rtl simulation failed.
INFO: [COSIM 212-211] II is measurable only when transaction number is greater than 1 in RTL simulation. Otherwise, they will be marked as all NA. If user wants to calculate them, please make sure there are at least 2 transactions in RTL simulation.
could not read "xxx/solution1/sim/tv/rtldatafile/sim/report/cosim.log": no such file or directory
while executing
"source xxx/solution1/cosim.tcl"
invoked from within
"hls::main xxx/solution1/cosim.tcl"
("uplevel" body line 1)
invoked from within
"uplevel 1 hls::main {*}$args"
(procedure "hls_proc" line 5)
invoked from within
"hls_proc $argv"
Finished C/RTL cosimulation.
这段异常从因为日志看是说综合工具和综合工具仿真器有问题,或者是环境设置有问题,推荐电脑重启或者关机,或者重新安装软件。
我试了试这些方法都不行,实际上我发现就是那个if j==0那段代码造成的异常。后面仔细分析了一下,是这段代码片剖坏了移位寄存器功能,造成综合工具在仿真的时候出现了二义性,从而报错。
2.失败的原因分析
上述失败代码中if(j == 0) 不是简单加了一个边界判断,而是破坏了 HLS 原代码的规则移位寄存器结构和流水线时序,导致 C 综合/RTL 生成失败,最终 C/RTL 仿真启动时找不到 RTL 仿真可执行文件。
五、去掉static修饰wind_buffer也可也解决问题
void fuction
(
)
{
// static ap_uint<24> win_buff[10];//c/rtl协同仿真异常
static ap_uint<24> win_buff[10];//c/rtl协同仿真正常
#pragma HLS ARRAY_PARTITION variable=win_buff complete dim=0
for(ap_uint<16> i=0; i<height; i++) {
for(ap_uint<16> j=0; j<(cols+1); j++) {
#pragma HLS pipeline II=1
srctemp = src.read();
// ================= 错误代码 =================
if(j==0){
for(ap_uint<4> k = 0; k < 10; k++) {
#pragma HLS UNROLL
win_buff[k] = srctemp;
win_buff[k + 1] = srctemp;
win_buff[k + 2] = srctemp;
win_buff[k + 3] = srctemp;
win_buff[k + 4] = srctemp;
}
}
else{
for(ap_uint<4> k = 0; k < 10; k++) {
#pragma HLS UNROLL
win_buff[k] = win_buff[k + 1];
win_buff[k + 1] = win_buff[k + 2];
win_buff[k + 2] = win_buff[k + 3];
win_buff[k + 3] = win_buff[k + 4];
win_buff[k + 4] = srctemp;
}
}
// ==========================================================
}
}
}
static修饰win buffer造成win buffer是移位寄存器,并且初始化在bitstream加载过程初始化,然后if的两种判断,破坏了结构;
去掉static后,变成普通的变量,设计就变得正确了。
这个试验是个很好的案例,我差不多花费了一个小时,才把这段逻辑推导成功和分析明白。
六、总结
1.当使用 static 修饰时:
HLS 认为 win_buff 是一个持久状态变量,需要在 FPGA 上电或复位时恢复到特定状态(通常是 0)。
当您在循环中加入 if(j==0) 的复杂逻辑时,工具不仅要处理上述的 MUX 逻辑,还要处理静态变量的复位逻辑。这导致生成的 RTL 代码极其复杂。Vivado 2018.3 版本在处理这种“带条件复位的复杂状态机”时,可能生成了某种边界条件的 RTL 代码,导致仿真器编译失败。
2.当移除 static 后:
win_buff 变成了一个局部变量。
在 RTL 硬件实现中,局部变量通常没有全局复位需求。它只是一个随算法逻辑状态变化的寄存器组。
移除 static 后,HLS 不再生成复杂的初始化/复位电路,逻辑负载减轻。虽然移位逻辑依然因为 if-else 变得复杂(不如纯移位高效),但由于去掉了复位逻辑的“负赘”,仿真器得以勉强完成编译并运行。
3.失败原因:if(j==0) 的分支逻辑破坏了移位寄存器推断,生成了巨大的 MUX 逻辑,导致 RTL 综合或仿真引擎崩溃。
4.移除 static 成功的原因:移除静态属性去掉了复位逻辑的负担,使工具勉强完成了编译,但这只是“治标不治本”,硬件效率依然很低。
5.最佳实践:保持正确代码的写法。不要在流水线循环内部对移位寄存器做复杂的条件初始化,利用输出延迟(j > delay)来自然过滤无效数据。