天天看點

記錄一次血崩式的CPLD不穩定 bug經驗

-------------分界

北京地區,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均正常

記錄一次血崩式的CPLD不穩定 bug經驗

繼續閱讀