记录一次CPLD资源过少、时序伪例的解决办法
1、背景:
CPLD虽然是几乎淘汰产品,但是体积非常小,而且不需要额外的EPCS存储器,所以完成简单的时序来说,也有尚存的一席之地。
这次使用的是MAX V系列的CPLD,完成外部触发后,产生一个可控低电平,接着是可控高电平的pulse_out1,接着pulse_out2又受pulse_out1的上升沿触发,接着pulse_out3又受pulse_out2的触发,以此完成类似的可控操作。
上图,我已经在我的quartus 13.1中安装了MAX V系列的CPLD,安装过程类似于曾经写的一篇文章:https://blog.****.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、根据条件产生下一个状态
[email protected](*)
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能跑到如此大。如果忘记了,看看我写的底层模块即可。