记录一次血崩式的CPLD不稳定 bug经验

版权声明:版权归ciscomonkey所有,QQ:1183699227 https://blog.csdn.net/ciscomonkey/article/details/89608908

-------------分界
北京地区,4月24日,一场大雨,来得异常突然,不同寻常的天气预示着不同寻常的事情要发生。妖气弥漫了帝都,一阵狂风飘过,收到报告,你写的这个CPLD程序门控有不正常了,又没有了。
what?肯定是硬件问题,之前测试都是非常正常的,然后再装到设备里面去的,就一个门控电路,这么low的东西,毕竟学习了FPGA一年了,写个延时可调的门控,那真的是不到30分钟的事情。怎么会老是出错呢?我用前仿真,后仿真,时序约束,可达到85°的高温,slack都满足,怎么会错?
种种不惑,不解摆在我面前,我仔仔细细又看了程序,雨依旧很大,我去吃了饭,点餐前,我直接拿出自己的电脑,迟迟未点餐,我modelsim仿真,每一个信号,都没问题。虽然程序很简单,但是这种死去活来抓不住的问题,让我怀疑自己能力,让我领导怀疑我的能力,甚至可能导致我不能接下一个FPGA任务,因为我写的程序是不可靠的,在现实中,如果你的程序一个月出一次问题,都是不可靠的。这样的不稳定,可能会导致比如门控,在不该开的时候开,可能会打坏器件,而一个器件可能价值10W,所以,虽然是一个简单的东西,但十分重要。
可以说,我从来没遇到过这种问题,回到实验室,我测了又测,争辩了又争辩,晚上11点回到寝室,我觉得我的状态机,设置为格雷码,不会有任何跑飞的情况了。但是,烧了两台CPLD,看似正常了,但是第二天,接到报告,有一台又不工作了,重启一下才工作。我去,这简直不能忍受了,不就一个受到触发后,延时一段时间(可调)然后出一个高电平(可调),这样的r j 程序,怎么还会错,我也想不通呀,我怀疑了一次又一次的硬件,我感觉我智商到了0,我熟悉不能再熟悉的状态机,我简直不敢用了。
然而,CPLD这种器件是不支持Signal tap的,所以我也没办法调试。后来我决定用计数器的方式,计数到某个值出高电平,计数到最后一个值,恢复至低。最后烧到8台CPLD程序中,都正常运行了,截止目前,没收到报告说有错。虽然这个事情暂时过去了,但是,这个原因找不出来可能会导致我的程序是不可靠的我的代码是不可靠的,这个印象将会存在于领导中,这对我的打击将是非常巨大的。
于是,我用我的EP4CE10F17C8来调试,想观察一下状态机,于是我添加了一句防止优化的命令,在定义状态机的后面,FPGA一直都完美运行,状态都没问题。到了今天,事情过去了三天了,我又去实验室,拿了一个CPLD,我用这个程序,下载进去,纳闷儿的事情发生了,也是一个偶然发现了一个惊天秘密,我发现我在没有写/synthesis preserve/ ,此时有个管脚是不输出波形的,然而我一旦写了这个,管脚输出波形,恢复正常,我试过很多次,上电重启,上电重启,上电重启,上电重启。。。发现真的是这样。
难道CPLD这种器件把我的状态寄存器给我优化掉了?这个偶然的发现。既让我兴奋,又让我不解,虽然我还不敢保证是不是真正凶手导致的不稳定,但是事情确实是在我眼皮子底下发生了,而且我验证了很多次。我打算明天后天,将继续验证这个结论,如果确实是这样,只能说明/synthesis preserve/ 将影响实际布线,在CPLD这种器件中写状态机,要尤其小心。
我觉得这个事情,也算是一个血泪史吧,这几天淋了一身雨,倒头就睡了,一场大雨似乎让我觉得一定要武装自己的知识,不断让自己强大再强大。

以下是代码:如果有哪位有经验的大哥,可以说说你的看法,代码非常简单,仿真,slack均以满足85°高温等均已经测试。

顶层:

module ex_pulse_triger
# (

parameter low_leval_time_pulse_1=1, 	//ex_triger外部触发后的延时时间,实际测试+2
parameter high_leval_time_pulse_1=300,//高电平时间,实际测试:保持不变

parameter low_leval_time_pulse_2=23,	//pulse_out1触发后的延时时间,实际测试+2
parameter high_leval_time_pulse_2=250,//高电平时间,实际测试,保持不变

parameter low_leval_time_pulse_3=23,	//pulse_out2触发后的延时时间,实际测试+2
parameter high_leval_time_pulse_3=200,//高电平时间,实际测试,保持不变

parameter data_width_1=9,     //第一个脉冲高电平宽度
parameter data_width_2=9,	//第二个脉冲高电平宽度
parameter data_width_3=9//


)
(
input ex_triger,   //外部触发
input sys_clk,
//output ex_triger_test,


output pulse_out1,	//
output pulse_out1_n,

output pulse_out2,
output pulse_out2_n,

output pulse_out3,
output pulse_out3_n
);

reg ex_triger_reg;     //寄存外部信号
reg ex_triger_reg_reg;
reg ex_triger_reg_reg_reg;
reg ex_triger_reg_reg_reg_reg;

reg pulse_out1_reg;		//寄存外部信号2
reg pulse_out1_reg_reg;
reg pulse_out1_reg_reg_reg;
reg pulse_out1_reg_reg_reg_reg;

reg pulse_out2_reg;     //寄存外部信号3
reg pulse_out2_reg_reg;
reg pulse_out2_reg_reg_reg;
reg pulse_out2_reg_reg_reg_reg;

reg start_pulse1;  	//启动信号脉冲1

reg start_pulse2;  	//启动信号脉冲2

reg start_pulse3;  	//启动信号脉冲3



always @ (posedge sys_clk )
begin
		ex_triger_reg<=ex_triger;
		ex_triger_reg_reg<=ex_triger_reg;
		ex_triger_reg_reg_reg<=ex_triger_reg_reg;
		ex_triger_reg_reg_reg_reg<=ex_triger_reg_reg_reg;
		start_pulse1<=ex_triger_reg_reg_reg_reg&!ex_triger_reg;	
end

//---------------------------------------------------


always @ (posedge sys_clk)
begin
		pulse_out1_reg<=pulse_out1;
		pulse_out1_reg_reg<=pulse_out1_reg;
		pulse_out1_reg_reg_reg<=pulse_out1_reg_reg;
		pulse_out1_reg_reg_reg_reg<=pulse_out1_reg_reg_reg;
		start_pulse2<=!pulse_out1_reg_reg_reg_reg&pulse_out1_reg;	
end

//---------------------------------------------------




always @ (posedge sys_clk)
begin	
		pulse_out2_reg<=pulse_out2;
		pulse_out2_reg_reg<=pulse_out2_reg;
		pulse_out2_reg_reg_reg<=pulse_out2_reg_reg;
		pulse_out2_reg_reg_reg_reg<=pulse_out2_reg_reg_reg;
		start_pulse3=!pulse_out2_reg_reg_reg_reg&pulse_out2_reg;	
end


//--------------------------脉冲1的控制
pulse_out_module
# (
.low_leval_time(low_leval_time_pulse_1),//触发后延迟时间

.high_leval_time(high_leval_time_pulse_1),	//高电平时间

.data_width(data_width_1)

)pulse_out_module_inst1
(
.start(start_pulse1),    	//输入启动信号
.sys_clk(sys_clk),
.ex_triger(ex_triger),
.pulse_out(pulse_out1)
);

//--------------------------脉冲2的控制
pulse_out_module
# (
.low_leval_time(low_leval_time_pulse_2),//触发后延迟时间

.high_leval_time(high_leval_time_pulse_2),	//高电平时间

.data_width(data_width_2)

)pulse_out_module_inst2
(
.start(start_pulse2),    	//输入启动信号
.sys_clk(sys_clk),
.ex_triger(ex_triger),
.pulse_out(pulse_out2)
);

//--------------------------脉冲3的控制
pulse_out_module
# (
.low_leval_time(low_leval_time_pulse_3),//触发后延迟时间

.high_leval_time(high_leval_time_pulse_3),	//高电平时间

.data_width(data_width_3)

)pulse_out_module_inst3
(
.start(start_pulse3),    	//输入启动信号
.ex_triger(ex_triger),
.sys_clk(sys_clk),
.pulse_out(pulse_out3)
);

assign pulse_out1_n=~pulse_out1;
assign pulse_out2_n=~pulse_out2;
assign pulse_out3_n=~pulse_out3;



endmodule

module pulse_out_module
# (
parameter low_leval_time=0,//触发后延迟时间

parameter high_leval_time=0,	//高电平时间

parameter data_width=0     //高电平计数器宽度
)
(
input start,    	//输入启动信号
input ex_triger,
input sys_clk,
output pulse_out

);

localparam IDLE_state=2'b00;
localparam IDLE_state_rst=2'b01;
localparam low_level_state=2'b11;
localparam high_level_state=2'b10;

reg pulse_out_reg=0;

reg [1:0] now_state    /*synthesis preserve*/ = IDLE_state;
reg [1:0] next_state  /*synthesis preserve*/= IDLE_state;

reg [data_width-1:0] leval_cnt;
reg start_high_flag=1'b0;
reg start_idle_flag=1'b0;

//1、实现状态转换
always @ (posedge sys_clk)
begin
	now_state<=next_state;
end

//2、根据条件产生下一个状态
always @ (*)
begin
	case (now_state)
	IDLE_state:
	
				begin
					if(!ex_triger)
						next_state=IDLE_state_rst;	
					else
						next_state=IDLE_state;
				end
				
	IDLE_state_rst:
	
				begin
					if(start)
					next_state=low_level_state;
					else
					next_state=IDLE_state_rst;
				end
				
	low_level_state:
				begin
					if(start_high_flag)
						next_state=high_level_state;	
					else
						next_state=low_level_state;
				end
				
	
	high_level_state:
				begin
					if(start_idle_flag)
						next_state=IDLE_state;	
					else
						next_state=high_level_state;
				end
	default:
	begin
			next_state=IDLE_state;
	end
	endcase

end
//3、状态条件输出
always @ (posedge sys_clk)
begin	
	case (next_state)
	IDLE_state:
				begin
					leval_cnt<=0;
					pulse_out_reg<=0;
					//high_leval_cnt<=0;
				end
	IDLE_state_rst:
				begin
					leval_cnt<=0;
					pulse_out_reg<=0;
				end
	low_level_state:
				begin
					leval_cnt<=leval_cnt+1;
					pulse_out_reg<=0;
				end
				
	
	high_level_state:
				begin
					leval_cnt<=leval_cnt+1;
					pulse_out_reg<=1;
				end
	default:
	begin
					leval_cnt<=0;
					pulse_out_reg<=0;
	end
	endcase

end



always @ (posedge sys_clk)
begin
if(leval_cnt==low_leval_time-1|low_leval_time==0)
start_high_flag<=1;
else
start_high_flag<=0;
end


always @ (posedge sys_clk)
begin
if(leval_cnt==low_leval_time+high_leval_time-1)
start_idle_flag<=1;
else
start_idle_flag<=0;
end



assign pulse_out=pulse_out_reg;
		

endmodule

用FPGA的signal tap均正常

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ciscomonkey/article/details/89608908
今日推荐