对称型计数器Verilog代码学习(三段式状态机写法)
symme_timer.v
代码如下:
// Create Date: 2024/04/14
// Module Name: symme_timer.v
module symme_timer(
clk ,
rst_n ,
en ,
cfg_max ,
cnt //这样写端口方便例化操作
);
input clk;
input rst_n;
input en;
input [31:0] cfg_max;
output [31:0] cnt;
reg [31:0] cnt;
//-----------------状态机局部参数的定义----------------------
localparam IDLE = 2'd0;
localparam INC = 2'd1;
localparam DEC = 2'd2;
//----------------------------------------------------------
reg [1:0] state;
reg [1:0] state_next;
wire max_pre_vld;
wire one_vld;
wire [31:0] cfg_max_limit;
//----------------------------------------------------------
assign cfg_max_limit = (cfg_max == 32'd0) ? 32'd1 : cfg_max;
assign max_pre_vld = (cnt == cfg_max_limit - 32'd1); //注意看波形的变化
assign one_vld = (cnt == 32'd1);
//----------------------------------------------------------
//first segment:state transfer
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
state <= IDLE;
else
state <= state_next;
end
//----------------------------------------------------------
//second segment:state transfer condition
always @ (*)
begin
case (state)
IDLE:
begin
if(en)
state_next = INC;
else
state_next = IDLE;
end
INC:
begin
if(~en)
state_next = IDLE;
else if (max_pre_vld)
state_next = DEC;
else
state_next = INC;
end
DEC:
begin
if(~en)
state_next = IDLE;
else if (one_vld)
state_next = INC;
else
state_next = DEC;
end
default: state_next = IDLE;
endcase
end
//-------------------------------------------------------------
//third segment:state output
always @ (posedge clk or negedge rst_n)
begin
if (!rst_n)
cnt <= 32'd0;
else
case(state)
IDLE: cnt <= 32'd0;
INC: cnt <= cnt + 32'd1;
DEC: cnt <= cnt - 32'd1;
endcase
end
endmodule
symme_timer_tb.v
代码如下:
// Create Date: 2024/04/14
// Module Name: symme_timer_tb.v
module symme_timer_tb;
logic clk ;
logic rstn ; //绿皮书:logic类型只能有一个驱动
reg en ;
initial
begin
$fsdbDumpfile("tb.fsdb");
$fsdbDumpvars(0);
end
initial
begin
clk = 1'b0;
forever
begin
#(1e9/(2.0*40e6)) clk = ~clk; //时间单位为ns,时钟频率为40MHz
end
end
initial
begin
rstn = 0;
#30 rstn = 1; //仿真开始时刻复位信号为0,30ns过后,复位信号为1
end
initial
begin
en = 0;
@(posedge rstn);
#100;
@(posedge clk);
#1;
en = 1; //使能信号是在时钟上升沿1n后,从0变为1
#1000;
@(posedge clk);
#1;
en = 0; //使能信号是在时钟上升沿1n后,从1变为0
#100;
$finish;
end
symme_timer symme_timer_u1(
.clk (clk ),
.rst_n (rstn ),
.en (en ),
.cfg_max(5 ), //例化时可以将端口信号直接赋值?
.cnt ( ) //例化时不使用的端口可以悬空
);
endmodule
波形图
要重点关注标记M1、M2、M3、M4
点,clk、en、state、cnt
的变化情况!!!
ps:源代码来自《数字IC设计入门》白栎旸◎编著