记录一次CPLD资源过少、时序伪例的解决办法

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

1、背景:

CPLD虽然是几乎淘汰产品,但是体积非常小,而且不需要额外的EPCS存储器,所以完成简单的时序来说,也有尚存的一席之地。

这次使用的是MAX V系列的CPLD,完成外部触发后,产生一个可控低电平,接着是可控高电平的pulse_out1,接着pulse_out2又受pulse_out1的上升沿触发,接着pulse_out3又受pulse_out2的触发,以此完成类似的可控操作。
在这里插入图片描述
上图,我已经在我的quartus 13.1中安装了MAX V系列的CPLD,安装过程类似于曾经写的一篇文章:https://blog.csdn.net/ciscomonkey/article/details/87896715

2、代码

代码如下:
顶层模块:
实现了三个pulse的触发,和上升沿的检测

module ex_pulse_triger
# (

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

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

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

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


)
(
input ex_triger,   //外部触发
input sys_clk,
input rst,				//低电平复位
output pulse_out1,	//
output pulse_out2,
output pulse_out3
);

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

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

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



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

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

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



always @ (posedge sys_clk)
begin
	ex_triger_reg<=ex_triger;
	ex_triger_reg_reg<=ex_triger_reg;
end

assign start_pulse1=!ex_triger_reg_reg&ex_triger;
//---------------------------------------------------



always @ (posedge sys_clk)
begin
	pulse_out1_reg<=pulse_out1;
	pulse_out1_reg_reg<=pulse_out1_reg;
end

assign start_pulse2=!pulse_out1_reg_reg&pulse_out1;
//---------------------------------------------------




always @ (posedge sys_clk)
begin
	pulse_out2_reg<=pulse_out2;
	pulse_out2_reg_reg<=pulse_out2_reg;
end

assign start_pulse3=!pulse_out2_reg_reg&pulse_out2;


//--------------------------脉冲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),
.rst(rst),		//低电平复位
.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),
.rst(rst),		//低电平复位
.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),    	//输入启动信号
.sys_clk(sys_clk),
.rst(rst),		//低电平复位
.pulse_out(pulse_out3)
);


endmodule

底层模块:

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

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

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

);

localparam IDLE_state=2'b0;
localparam low_level_state=2'b01;
localparam high_level_state=2'b10;

reg pulse_out_reg=0;

reg [1:0] now_state=0;
reg [1:0] next_state=0;

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

//1、实现状态转换
always @ (posedge sys_clk or negedge rst)
begin
	if(!rst)							//低电平复位
	now_state<=IDLE_state;
	else
	now_state<=next_state;
end

//2、根据条件产生下一个状态
always@(*)
begin
	case (now_state)
	IDLE_state:
				begin
					if(start)
						next_state=low_level_state;	
					else
						next_state=IDLE_state;
				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:
			next_state=IDLE_state;
	endcase

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

end

always @ (posedge sys_clk or negedge rst)
begin
	if(!rst)
	pulse_out_reg<=0;
	else if(now_state==high_level_state)
	pulse_out_reg<=1;
	else
	pulse_out_reg<=0;
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|low_leval_time==0)
start_idle_flag<=1;
else
start_idle_flag<=0;
end



assign pulse_out=pulse_out_reg;
		

endmodule

时序分析:
以下选择的是后来我们决定用5M10ZE64C4,时序也是完全符合的,不存在时序违规。
在这里插入图片描述
在这里插入图片描述

3、心得

1、CPLD体积小,如果仅仅是完成简单的触发等时序,完全可以采用CPLD这类器件。
2、CPLD资源相当少,一般逻辑资源如上图,才160,稍微不注意,就会超标,所以,寄存器输入一定要尽量减少,位宽需要多少就定义多少,不要定义太宽,可以用assign的,就不用寄存器。能用一个计数器的,绝不用两个计数器,状态机位宽、状态机状态数目能少尽量少。条件能写==的,就不写<=
3、很容易造成时序违规问题,所以,要解决时序违规,可以适当增加一个或两个寄存器。特别是状态机里面的条件,可以把条件化为一个寄存器flag标志,满足条件,输出flag,从而减小了数据路径,从而解决时序伪例。在本实际例子中就是这么解决的,从而能让fmax能跑到如此大。如果忘记了,看看我写的底层模块即可。

猜你喜欢

转载自blog.csdn.net/ciscomonkey/article/details/88408314