module carlight(sw,clk,rst_n,led);
input [3:0] sw;//拨码开关控制
input clk;//50MHz时钟
input rst_n;//sw8为1时正常工作
output reg [5:0] led;//两个三色led
wire clk_1Hz;
//分频得到频率为1khz的时钟
divide #(.WIDTH(32),.N(25000000))u2(
.clk(clk),
.rst_n(rst_n),
.clkout(clk_1Hz)
);
parameter[3:0]
START=4'b0000,//初始状态
S1=4'b0001,//前行
S2=4'b0011,//左转
S3=4'b0101,//右转
S4=4'b1001,//临时停车或故障
S5=4'b1111;//倒车
reg [3:0]
current_state,
next_state;
initial
begin
current_state<=4'b0000;
next_state<=4'b0000;
end
//第一个always块,描述在时钟驱动下次态迁移到现态。
always @ (posedge clk_1Hz or negedge rst_n) //异步复位
if (!rst_n)
current_state<= START;
else
current_state<= next_state; //注意,使用非阻塞赋值
//第二个always块,描述状态的转移关系
always @ (current_state or sw) //电平触发,将现态作为敏感信号
begin
case (current_state)
START: case(sw)
S1: next_state = S1;
S2: next_state = S2;
S3: next_state = S3;
S4: next_state = S4;
S5: next_state = S5;
default: next_state = START;
endcase
S1: next_state = START ; //阻塞赋值
S2: next_state = START; //阻塞赋值
S3: next_state = START;
S4: next_state = START;
S5: next_state = START;
default: next_state = START; //对多余状态的处理
endcase
end
//第三个always块,描述每个状态对应的输出
always @ (current_state)
begin
led=6'b111111;
case (current_state)
START: led = 6'b111111; //对输出进行赋值
S1: led = 6'b111111;
S2: led = 6'b011111;
S3: led = 6'b111011;
S4: led = 6'b011011;
S5: led = 6'b110011;
default: led = 6'b111111;// 避免综合出锁存器。
endcase
end
endmodule
module divide ( clk,rst_n,clkout);
input clk,rst_n; //输入信号,其中 clk 连接到 FPGA 的 M1 脚,频率为 50MHz
output clkout; //输出信号,可以连接到 LED 观察分频的时钟
//parameter 是 verilog 里常数语句
parameter WIDTH = 3; //计数器的位数,计数的最大值为 2**WIDTH-1
parameter N = 5; //分频系数,请确保 N < 2**WIDTH-1,否则计数会溢出
reg [WIDTH-1:0] cnt_p,cnt_n; //cnt_p 为上升沿触发时的计数器,cnt_n 为下降沿触发时的计数器
reg clk_p,clk_n; //clk_p 为上升沿触发时分频时钟,clk_n 为下降沿触发时分频时钟
//上升沿触发时计数器的控制
always @ (posedge clk or negedge rst_n ) //posedge 和 negedge 是 verilog 表示信号上升沿和下降沿
//当 clk 上升沿来临或者 rst_n 变低的时候执行一次 always 里的语句
begin
if(!rst_n)
cnt_p<=0;
else if (cnt_p==(N-1))
cnt_p<=0;
else cnt_p<=cnt_p+1; //计数器一直计数,当计数到 N-1 的时候清零,这是一个模 N 的计数器
end
//上升沿触发的分频时钟输出,如果 N 为奇数得到的时钟占空比不是 50%;如果 N 为偶数得到的时钟占空比为 50%
always @ (posedge clk or negedge rst_n)
begin
if(!rst_n)
clk_p<=0;
else if (cnt_p<(N>>1))//N>>1 表示右移一位,相当于除以 2 去掉余数
clk_p<=0;
else
clk_p<=1; //得到的分频时钟正周期比负周期多一个 clk 时钟
end
//下降沿触发时计数器的控制
always @ (negedge clk or negedge rst_n)
begin
if(!rst_n)
cnt_n<=0;
else if (cnt_n==(N-1))
cnt_n<=0;
else cnt_n<=cnt_n+1;
end
//下降沿触发的分频时钟输出,和 clk_p 相差半个时钟
always @ (negedge clk)
begin
if(!rst_n)
clk_n<=0;
else if (cnt_n<(N>>1))
clk_n<=0;
else
clk_n<=1; //得到的分频时钟正周期比负周期多一个 clk 时钟
end
assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p; //条件判断表达式
//当 N=1 时,直接输出 clk
//当 N 为偶数也就是 N 的最低位为 0,N(0)=0,输出 clk_p
//当 N 为奇数也就是 N 最低位为 1,N(0)=1,输出 clk_p&clk_n。正周期多所以是相与
endmodule