众所周知,车辆上路,通常靠右行驶,所以右转是不需要红绿灯的限制的。首先,我们把十字路口分为东西走向(设为1)和南北走向(设为2)。将东西左转南北设为1r,将南北左转东西设为2r。接着,构思一下红绿灯的状态变换,如下图所示(随便扣了张图,自己做了点补充,字用鼠标写的,不代表本人手写水平):

这里,其实我的设计跟实际生活中的情况是有出入的,通常实际生活中南北或东西方向同一时间只能进行单向通行,以保证更加的安全,而本图中假设南北或东西方向同一时间是能进行双向通行的,以减少工作量,实际上,只要多增加一倍的状态,也能达到生活中红绿灯的效果。
最后,我们再给程序加上24小时时钟功能,通过六个数码管来显示时间。
废话不多说,直接上代码:
头文件:
module ten_word(
input wire clk_i,
input wire sysrst_li,
output wire r1_o,
output wire y1_o,
output wire g1_o,
output wire r2_o,
output wire y2_o,
output wire g2_o,
output wire r1r_o,
output wire y1r_o,
output wire g1r_o,
output wire r2r_o,
output wire y2r_o,
output wire g2r_o,
output [7:0]seg,
output [7:0]seg1,
output [3:0]sel,
output [1:0]sel1
);
wire tick1s;
wire [3:0] data0;
wire [3:0] data1;
wire [3:0] data2;
wire [3:0] data3;
wire [3:0] data4;
wire [3:0] data5;
tick_1s inst_tick_1s (
.clk_i (clk_i),
.rst_i (~sysrst_li),
.tick1s_o (tick1s)
);
tl_fsm inst_tl_fsm (
.clk_i (clk_i),
.rst_i (~sysrst_li),
.tick1s_i (tick1s),
.r1_o (r1_o),
.y1_o (y1_o),
.g1_o (g1_o),
.r2_o (r2_o),
.y2_o (y2_o),
.g2_o (g2_o),
.r1r_o (r1r_o),
.y1r_o (y1r_o),
.g1r_o (g1r_o),
.r2r_o (r2r_o),
.y2r_o (y2r_o),
.g2r_o (g2r_o)
);
digital inst_digital(
.clk_i(clk_i),
.rst_i(~sysrst_li),
.data0(data0),
.data1(data1),
.data2(data2),
.data3(data3),
.data4(data4),
.data5(data5),
.seg(seg),
.seg1(seg1),
.sel(sel),
.sel1(sel1)
);
clock inst_clock(
.clk_i(clk_i),
.rst_i(~sysrst_li),
.tick1s_o(tick1s),
.data0(data0),
.data1(data1),
.data2(data2),
.data3(data3),
.data4(data4),
.data5(data5)
);
endmodule
一秒计时器代码:
module tick_1s (
input wire clk_i,
input wire rst_i,
output wire tick1s_o
);
parameter num_1s = 100_000_000;
reg [26:0]cnt_1s;
always@(posedge clk_i)
begin
if(rst_i) begin
cnt_1s = 0;
end
else
begin
if(cnt_1s == num_1s) cnt_1s <= 0;
else cnt_1s <= cnt_1s+1;
end
end
assign tick1s_o = (cnt_1s == num_1s) ? 1 : 0;
endmodule
数码管(共阴极)显示代码:
module digital(
input clk_i,
input rst_i,
input [3:0]data0,
input [3:0]data1,
input [3:0]data2,
input [3:0]data3,
input [3:0]data4,
input [3:0]data5,
output reg[7:0]seg,
output reg[7:0]seg1,
output reg[3:0]sel,
output reg[1:0]sel1
);
reg[15:0]cnl;
reg clk1k;
always@(posedge clk_i)
begin
if(rst_i)
begin
cnl=0;
clk1k=0;
end
else if(cnl>=49999)
begin
clk1k=~clk1k;
cnl=0;
end
else cnl<=cnl+1;
end
reg [3:0]tub;
reg [3:0]tub1;
reg [5:0]state;
always@(posedge clk1k)
begin
if(rst_i)
begin
tub=0;
state=0;
sel=0;
sel1=0;
tub1=0;
end
else
begin
case(state)
0:begin tub1=data0;sel1=2'b10;state=1;end
1:begin tub1=data1;sel1=2'b01;state=2;end
2:begin tub=data2;sel=4'b1000;state=3;end
3:begin tub=data3;sel=4'b0100;state=4;end
4:begin tub=data4;sel=4'b0010;state=5;end
5:begin tub=data5;sel=4'b0001;state=0;end
default:state<=0;
endcase
end
end
always@(*)
if(rst_i)
seg=8'b00110000;
else
case(tub)
4'd0:seg=8'b00111111;
4'd1:seg=8'b00000110;
4'd2:seg=8'b01011011;
4'd3:seg=8'b01001111;
4'd4:seg=8'b01100110;
4'd5:seg=8'b01101101;
4'd6:seg=8'b01111101;
4'd7:seg=8'b00000111;
4'd8:seg=8'b01111111;
4'd9:seg=8'b01101111;
default:seg=8'b00110000;
endcase
always@(*)
if(rst_i)
seg1=8'b00110000;
else
case(tub1)
4'd0:seg1=8'b00111111;
4'd1:seg1=8'b00000110;
4'd2:seg1=8'b01011011;
4'd3:seg1=8'b01001111;
4'd4:seg1=8'b01100110;
4'd5:seg1=8'b01101101;
4'd6:seg1=8'b01111101;
4'd7:seg1=8'b00000111;
4'd8:seg1=8'b01111111;
4'd9:seg1=8'b01101111;
default:seg1=8'b00110000;
endcase
endmodule
24小时时钟代码:
module clock(
input clk_i,
input rst_i,
input tick1s_o,
output reg[3:0]data0,
output reg[3:0]data1,
output reg[3:0]data2,
output reg[3:0]data3,
output reg[3:0]data4,
output reg[3:0]data5
);
reg [5:0] second;
reg [5:0] minute;
reg [4:0] hour;
always@(posedge clk_i)
begin
if(rst_i) begin
second = 0;
end else begin
if(second==59&&tick1s_o)begin
second = 0;
end else if(tick1s_o)begin
second = second + 1;
end else begin
second = second;
end
end
end
always@(posedge clk_i)
begin
if(rst_i) minute = 0;
else
begin
if(minute==59&&second==59&&tick1s_o) minute = 0;
else if(second==59&&tick1s_o) minute <= minute + 1;
else minute = minute;
end
end
always@(posedge clk_i)
begin
if(rst_i) hour = 0;
else
begin
if(hour==23&&minute==59&&second==59&&tick1s_o) hour=0;
else if(minute==59&&second==59&&tick1s_o) hour <= hour + 1;
else hour <= hour;
end
end
always@(posedge clk_i)
begin
if(rst_i) begin
data0 = 0;
data1 = 0;
data2 = 0;
data3 = 0;
data4 = 0;
data5 = 0;
end else begin
data0 = hour/10;
data1 = hour%10;
data2 = minute/10;
data3 = minute%10;
data4 = second/10;
data5 = second%10;
end
end
endmodule
红绿灯FSM代码:
module tl_fsm (
input wire clk_i,
input wire rst_i,
input wire tick1s_i,
output reg r1_o,
output reg y1_o,
output reg g1_o,
output reg r2_o,
output reg y2_o,
output reg g2_o,
output reg r1r_o,
output reg y1r_o,
output reg g1r_o,
output reg r2r_o,
output reg y2r_o,
output reg g2r_o
);
reg [4:0] cnt1;
reg load1;
reg load2;
reg load3;
reg load4;
reg load5;
reg load6;
reg load7;
reg load8;
always @(posedge clk_i) begin
if(rst_i)
cnt1 <= 5'd0;
else if(load1)
cnt1 <= 5'd20;
else if(load2)
cnt1 <= 5'd3;
else if(load3)
cnt1 <= 5'd20;
else if(load4)
cnt1 <= 5'd3;
else if(load5)
cnt1 <= 5'd20;
else if(load6)
cnt1 <= 5'd3;
else if(load7)
cnt1 <= 5'd20;
else if(load8)
cnt1 <= 5'd3;
else if(tick1s_i)
cnt1 <= cnt1 - 1'b1;
else
cnt1 <= cnt1;
end
//fsm template
reg [8:0] cstate, nstate;
parameter S_IDLE = 9'b0_0000_0001,
S_G2 = 9'b0_0000_0010,
S_Y1 = 9'b0_0000_0100,
S_G2r = 9'b0_0000_1000,
S_Y2 = 9'b0_0001_0000,
S_G1 = 9'b0_0010_0000,
S_Y3 = 9'b0_0100_0000,
S_G1r = 9'b0_1000_0000,
S_Y4 = 9'b1_0000_0000;
always @(posedge clk_i) begin
if(rst_i)
cstate <= S_IDLE;
else
cstate <= nstate;
end
always @(*) begin
r1_o = 1'b0;
y1_o = 1'b0;
g1_o = 1'b0;
r2_o = 1'b0;
y2_o = 1'b0;
g2_o = 1'b0;
r1r_o= 1'b0;
y1r_o= 1'b0;
g1r_o= 1'b0;
r2r_o= 1'b0;
y2r_o= 1'b0;
g2r_o= 1'b0;
load1 = 1'b0;
load2 = 1'b0;
load3 = 1'b0;
load4 = 1'b0;
load5 = 1'b0;
load6 = 1'b0;
load7 = 1'b0;
load8 = 1'b0;
nstate = S_IDLE;
case(cstate)
S_IDLE: begin
y1_o = 1'b1;
y2_o = 1'b1;
r1r_o = 1'b1;
r2r_o = 1'b1;
load1 = 1'b1;
nstate = S_G2;
end
S_G2: begin
g2_o = 1'b1;
r1_o = 1'b1;
r1r_o = 1'b1;
r2r_o = 1'b1;
if(cnt1 == 5'd0) begin
load2 = 1'b1;
nstate = S_Y1;
end else begin
nstate = S_G2;
end
end
S_Y1: begin
y2_o = 1'b1;
y2r_o = 1'b1;
r1r_o = 1'b1;
r1_o = 1'b1;
if(cnt1 == 5'd0) begin
load3 = 1'b1;
nstate = S_G2r;
end else begin
nstate = S_Y1;
end
end
S_G2r: begin
r2_o = 1'b1;
r1_o = 1'b1;
r1r_o = 1'b1;
g2r_o = 1'b1;
if(cnt1 == 5'd0) begin
load4 = 1'b1;
nstate = S_Y2;
end else begin
nstate = S_G2r;
end
end
S_Y2: begin
r2_o = 1'b1;
y1_o = 1'b1;
r1r_o = 1'b1;
y2r_o = 1'b1;
if(cnt1 == 5'd0) begin
load5 = 1'b1;
nstate = S_G1;
end else begin
nstate = S_Y2;
end
end
S_G1: begin
r2_o = 1'b1;
g1_o = 1'b1;
r1r_o = 1'b1;
r2r_o = 1'b1;
if(cnt1 == 5'd0) begin
load6 = 1'b1;
nstate = S_Y3;
end else begin
nstate = S_G1;
end
end
S_Y3: begin
r2_o = 1'b1;
y1_o = 1'b1;
y1r_o = 1'b1;
r2r_o = 1'b1;
if(cnt1 == 5'd0) begin
load7 = 1'b1;
nstate = S_G1r;
end else begin
nstate = S_Y3;
end
end
S_G1r: begin
r2_o = 1'b1;
r1_o = 1'b1;
g1r_o = 1'b1;
r2r_o = 1'b1;
if(cnt1 == 5'd0) begin
load8 = 1'b1;
nstate = S_Y4;
end else begin
nstate = S_G1r;
end
end
S_Y4: begin
y2_o = 1'b1;
r1_o = 1'b1;
y1r_o = 1'b1;
r2r_o = 1'b1;
if(cnt1 == 5'd0) begin
load1 = 1'b1;
nstate = S_G2;
end else begin
nstate = S_Y4;
end
end
default: nstate = S_IDLE;
endcase
end
endmodule
仿真波形图:


代码设置的时间比较长,所以波形只展示出了部分,代码适用于下板子时使用,仿真的时候可以更改一秒定时器的参数大小,压缩一下波形,另外,也可以调整vivado的仿真时间,有些版本(如21.2)无法通过更改方框内的数来改变仿真时间,这时我们可以直接在settings里面进行更改。
testbench代码:
module tb_ten_word(
);
reg clk=1'b0;
reg rst=1'b1;
reg tick1s=1'b0;
wire r1;
wire y1;
wire g1;
wire r2;
wire y2;
wire g2;
wire r1r;
wire y1r;
wire g1r;
wire r2r;
wire y2r;
wire g2r;
wire [7:0]seg;
wire [7:0]seg1;
wire [3:0]sel;
wire [1:0]sel1;
reg [2:0]cnt2;
wire [3:0] data0;
wire [3:0] data1;
wire [3:0] data2;
wire [3:0] data3;
wire [3:0] data4;
wire [3:0] data5;
digital inst_digital(
.clk_i(clk),
.rst_i(rst),
.data0(data0),
.data1(data1),
.data2(data2),
.data3(data3),
.data4(data4),
.data5(data5),
.seg(seg),
.seg1(seg1),
.sel(sel),
.sel1(sel1)
);
clock inst_clock(
.clk_i(clk),
.rst_i(rst),
.tick1s_o(tick1s),
.data0(data0),
.data1(data1),
.data2(data2),
.data3(data3),
.data4(data4),
.data5(data5)
);
tl_fsm inst_tl_fsm (
.clk_i (clk),
.rst_i (rst),
.tick1s_i (tick1s),
.r1_o (r1),
.y1_o (y1),
.g1_o (g1),
.r2_o (r2),
.y2_o (y2),
.g2_o (g2),
.r1r_o (r1r),
.y1r_o (y1r),
.g1r_o (g1r),
.r2r_o (r2r),
.y2r_o (y2r),
.g2r_o (g2r)
);
initial begin
#100 rst=1'b0;
end
always
#10 clk=~clk;
always @(posedge clk) begin
if(rst)
cnt2 <= 5'b00000;
else if(cnt2 == 5'b00101)begin
cnt2 <= 5'b00000;
tick1s = 1'b1;
end
else begin
cnt2 <= cnt2 + 1'b1;
tick1s = 1'b0;
end
end
endmodule
上板测试(添加视频麻烦,不添加了):

最后,声明一下,代码并非全部是我自己敲的,稍微借鉴了一下其他博文的内容,同时也参考了老师上课给的代码模板,发出来留个纪念,虽然能力不强,但通过自己的努力做出一样东西还是挺有成就感的。