完整程序(点击下载) 虚拟机:VMware -14.0.0.24051 环?境:ubuntu 18.04.1 脚?本:makefile(点击直达) 工?具:vcs 和 verdi
文章目录
- 一、Overview
- (1)Demand
- (2)Theory
- 二、Interface Description
- 三、EW_FSM
- 四、Design and Functional Verification
- (1)RTL
- (2)Test Bench
- 五、Result
- (1)复位
- (2)倒计时
- (3)最终结果
一、Overview
(1)Demand
??设计交通信号灯控制器,东、西、北各有四盏灯,分别是左转灯、绿灯、黄灯和红灯。东西方向的信号灯是红灯55s,绿灯40s,黄灯5s,左拐灯15s;南北信号灯65红灯s,绿灯30s,黄灯5s,左拐灯15s.
(2)Theory
二、Interface Description
Signal Name | Width | Direction | Description |
---|---|---|---|
clk | 1 | input | System clk signal, 1Hz |
rst_n | 1 | input | System reset signal |
light_ew_ZRYG | 4 | output | 左转、红、黄、绿灯 |
light_ns_ZRYG | 4 | output | 南北左转、红、黄、绿灯 |
time_ew_cnt | 6 | output | 东西方向倒计时 |
time_ns_cnt | 7 | output | 南北倒计时 |
三、EW_FSM
交通信号灯非常适合使用状态机。该模块采用状态机,东西方向有6个状态,包括南北方向和东西方向
时间上
互补状态。(为什么不使用两个状态机,因为它们时间上是互补的状态,只要其中一个方向的状态和倒计时已知,就可以控制另外一个方向的灯,在资源上,节省一个方向的状态机资源。)
四、Design and Functional Verification
(1)RTL
//-- modified by xlinxdu, 2022/05/08 module traffic_lights #( //-- time parameter G_TIME_EW = 6'd40, parameter Y_TIME_EW = 6'd5 , parameter R_TIME_EW = 6'd55, parameter Z_TIME_EW = 6'd15, parameter G_TIME_NS = 7'd30, parameter Y_TIME_NS = 7'd5 , parameter R_TIME_NS = 7'd65, parameter Z_TIME_NS = 7'd15 )( input clk_i , input rst_n_i , //-- ew: east and west direction
//-- ns: north-south direction
//-- light,[3:0] green\yellow\red\left turn light
output reg [3:0] light_ew_ZRYG,
output reg [3:0] light_ns_ZRYG,
//-- counter
output reg [5:0] time_ew_cnt ,
output reg [6:0] time_ns_cnt
);
//FSM signal
parameter G_LIGHT_EW = 6'b00_0001;
parameter Y_LIGHT_EW1 = 6'b00_0010;
parameter R_LIGHT_EW = 6'b00_0100;
parameter Y_LIGHT_EW2 = 6'b00_1000;
parameter Z_LIGHT_EW = 6'b01_0000;
parameter Y_LIGHT_EW3 = 6'b10_0000;
reg [5:0] cur_state_ew;
reg [5:0] nxt_state_ew;
reg ew_cnt_en ;
reg ns_cnt_en ;
/*-----------------------------------------------\
------------------- ew counter ----------------
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
ew_cnt_en <= 1'b1 ;
time_ew_cnt <= 0;
end
else if (ew_cnt_en && (nxt_state_ew == R_LIGHT_EW)) begin
time_ew_cnt <= R_TIME_EW;
ew_cnt_en <= 1'b0 ;
end
else if (ew_cnt_en && (nxt_state_ew == Y_LIGHT_EW1 )) begin
time_ew_cnt <= Y_TIME_EW;
ew_cnt_en <= 1'b0 ;
end
else if (ew_cnt_en && (nxt_state_ew == Y_LIGHT_EW2 )) begin
time_ew_cnt <= Y_TIME_EW;
ew_cnt_en <= 1'b0 ;
end
else if (ew_cnt_en && (nxt_state_ew == Y_LIGHT_EW3)) begin
time_ew_cnt <= Y_TIME_EW;
ew_cnt_en <= 1'b0 ;
end
else if (ew_cnt_en && (nxt_state_ew == G_LIGHT_EW)) begin
time_ew_cnt <= G_TIME_EW;
ew_cnt_en <= 1'b0 ;
end
else if (ew_cnt_en && (nxt_state_ew == Z_LIGHT_EW)) begin
time_ew_cnt <= Z_TIME_EW;
ew_cnt_en <= 1'b0 ;
end
else if(time_ew_cnt == 6'd2) begin
ew_cnt_en <= 1'b1;
time_ew_cnt <= time_ew_cnt - 1'b1;
end
else begin
time_ew_cnt <= time_ew_cnt - 1'b1;
end
end
/*-----------------------------------------------\
-------------------- ns counter ---------------
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
ns_cnt_en <= 1'b1 ;
time_ns_cnt <= 0;
end
else if (ns_cnt_en && (nxt_state_ew == R_LIGHT_EW)) begin
if(ew_cnt_en) time_ns_cnt <= G_TIME_NS;
else if(time_ew_cnt == 6'd26) time_ns_cnt <= Y_TIME_NS;
else if(time_ew_cnt == 6'd21) time_ns_cnt <= Z_TIME_NS;
else if(time_ew_cnt == 6'd6 ) time_ns_cnt <= Y_TIME_NS;
ns_cnt_en <= 1'b0 ;
end
else if (ns_cnt_en && (nxt_state_ew == Y_LIGHT_EW1)) begin
time_ns_cnt <= R_TIME_NS;
ns_cnt_en <= 1'b0 ;
end
else if (ns_cnt_en && (nxt_state_ew == Y_LIGHT_EW3)) begin
time_ns_cnt <= Y_TIME_NS;
ns_cnt_en <= 1'b0 ;
end
else if(time_ns_cnt == 7'd2) begin
ns_cnt_en <= 1'b1;
time_ns_cnt <= time_ns_cnt - 1'b1;
end
else begin
time_ns_cnt <= time_ns_cnt - 1'b1;
end
end
/*-----------------------------------------------\
--------------------- FSM --------------------
\-----------------------------------------------*/
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
cur_state_ew <= R_LIGHT_EW;
end
else begin
cur_state_ew <= nxt_state_ew;
end
end
always @ (*) begin
case(cur_state_ew)
R_LIGHT_EW :if(time_ew_cnt == 1'b1) begin
nxt_state_ew = Y_LIGHT_EW1;
end
else begin
nxt_state_ew = R_LIGHT_EW ;
end
Y_LIGHT_EW1:if(time_ew_cnt == 1'b1) begin
nxt_state_ew = G_LIGHT_EW ;
end
else begin
nxt_state_ew = Y_LIGHT_EW1;
end
G_LIGHT_EW :if(time_ew_cnt == 1'b1) begin
nxt_state_ew = Y_LIGHT_EW2;
end
else begin
nxt_state_ew = G_LIGHT_EW ;
end
Y_LIGHT_EW2:if(time_ew_cnt == 1'b1) begin
nxt_state_ew = Z_LIGHT_EW ;
end
else begin
nxt_state_ew = Y_LIGHT_EW2;
end
Z_LIGHT_EW :if(time_ew_cnt == 1'b1) begin
nxt_state_ew = Y_LIGHT_EW3;
end
else begin
nxt_state_ew = Z_LIGHT_EW ;
end
Y_LIGHT_EW3:if(time_ew_cnt == 1'b1) begin
nxt_state_ew = R_LIGHT_EW ;
end
else begin
nxt_state_ew = Y_LIGHT_EW3;
end
default :begin
nxt_state_ew = R_LIGHT_EW ;
end
endcase
end
//-- ew out
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
light_ew_ZRYG <= 4'b1111;
end
else if ((time_ew_cnt != 6'b1) && (cur_state_ew == R_LIGHT_EW)) begin
light_ew_ZRYG <= 4'b0100;
end
else if ((time_ew_cnt != 6'b1) && (cur_state_ew == (Y_LIGHT_EW1 || Y_LIGHT_EW2 || Y_LIGHT_EW3))) begin
light_ew_ZRYG <= 4'b0010;
end
else if ((time_ew_cnt != 6'b1) && (cur_state_ew == G_LIGHT_EW)) begin
light_ew_ZRYG <= 4'b0001;
end
else if ((time_ew_cnt != 6'b1) && (cur_state_ew == Z_LIGHT_EW)) begin
light_ew_ZRYG <= 4'b1000;
end
end
//-- ns out
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
light_ns_ZRYG <= 4'b1111;
end
else if ((time_ns_cnt != 6'b1) && (cur_state_ew == R_LIGHT_EW)) begin
if (time_ew_cnt > 6'd25) light_ns_ZRYG <= 4'b0001;
else if(time_ew_cnt > 6'd20) light_ns_ZRYG <= 4'b0010;
else if(time_ew_cnt > 6'd5 ) light_ns_ZRYG <= 4'b1000;
else if(time_ew_cnt >= 6'd1 ) light_ns_ZRYG <= 4'b0010;
end
else if ((time_ns_cnt != 6'b1) && (cur_state_ew != (R_LIGHT_EW || Y_LIGHT_EW3))) begin
light_ns_ZRYG <= 4'b0100;
end
else if ((time_ns_cnt != 6'b1) && (cur_state_ew == Y_LIGHT_EW3)) begin
light_ns_ZRYG <= 4'b0010;
end
end
endmodule
(2)Test Bench
`timescale 1ms/1ms
module tb_traffic_lights;
reg clk_i;
reg rst_n_i;
wire [3:0] light_ew_ZRYG;
wire [3:0] light_ns_ZRYG;
wire [5:0] time_ew_cnt ;
wire [6:0] time_ns_cnt ;
initial begin
clk_i = 0 ;
rst_n_i = 1;
#10 rst_n_i = 0;
#10 rst_n_i = 1;
end
always #500 clk_i = ~clk_i;
traffic_lights tb(
.clk_i (clk_i ),
.rst_n_i (rst_n_i ),
.light_ns_ZRYG(light_ns_ZRYG),
.light_ew_ZRYG(light_ew_ZRYG),
.time_ew_cnt (time_ew_cnt ),
.time_ns_cnt (time_ns_cnt )
);
initial begin
#200000 $finish;
$fsdbDumpfile("traffic.fsdb");
$fsdbDumpvars ;
$fsdbDumpMDA ;
end
endmodule
五、Result
(1)复位
设置初始状态为东西方向为红灯状态,南北方向设置为绿灯状态。在复位信号来临的时候,东西和南北方向的所有信号灯均亮起来,并且使能东西、南北方向的倒计时计数,波形正确。
(2)倒计时
bug1:在东西方向红灯倒计时结束后,红灯没有立即变为黄灯,而是保持红灯状态,不符合要求。
//-- ew out
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
light_ew_ZRYG <= 4'b1111;
end
else if ((time_ew_cnt != 6'b1) && (cur_state_ew == R_LIGHT_EW)) begin
light_ew_ZRYG <= 4'b0100;
end
else if ((time_ew_cnt != 6'b1) && (cur_state_ew == (Y_LIGHT_EW1 || Y_LIGHT_EW2 || Y_LIGHT_EW3))) begin
light_ew_ZRYG <= 4'b0010;
end
else if ((time_ew_cnt != 6'b1) && (cur_state_ew == G_LIGHT_EW)) begin
light_ew_ZRYG <= 4'b0001;
end
else if ((time_ew_cnt != 6'b1) && (cur_state_ew == Z_LIGHT_EW)) begin
light_ew_ZRYG <= 4'b1000;
end
end
定位:定位到东西方向的输出代码块,把输出的产生条件改为和倒计时产生的条件一致,输出结果正常
//-- ew out
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
light_ew_ZRYG <= 4'b1111;
end
else if (ew_cnt_en && (nxt_state_ew == R_LIGHT_EW)) begin
light_ew_ZRYG <= 4'b0100;
end
else if (ew_cnt_en && (nxt_state_ew == Y_LIGHT_EW1)) begin
light_ew_ZRYG <= 4'b0010;
end
else if (ew_cnt_en && (nxt_state_ew == Y_LIGHT_EW2)) begin
light_ew_ZRYG <= 4'b0010;
end
else if (ew_cnt_en && (nxt_state_ew == Y_LIGHT_EW3)) begin
light_ew_ZRYG <= 4'b0010;
end
else if (ew_cnt_en && (nxt_state_ew == G_LIGHT_EW)) begin
light_ew_ZRYG <= 4'b0001;
end
else if (ew_cnt_en && (nxt_state_ew == Z_LIGHT_EW)) begin
light_ew_ZRYG <= 4'b1000;
end
end
bug2:由仿真波形可以知道,南北方向的亮灯输出与倒计时不匹配,延迟了一秒,不符合时序。
//-- ns out
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
light_ns_ZRYG <= 4'b1111;
end
else if ((time_ns_cnt != 6'b1) && (cur_state_ew == R_LIGHT_EW)) begin
if (time_ew_cnt > 6'd25) light_ns_ZRYG <= 4'b0001;
else if(time_ew_cnt > 6'd20) light_ns_ZRYG <= 4'b0010;
else if(time_ew_cnt > 6'd5 ) light_ns_ZRYG <= 4'b1000;
else if(time_ew_cnt >= 6'd1 ) light_ns_ZRYG <= 4'b0010;
end
else if ((time_ns_cnt != 6'b1) && (nxt_state_ew != (R_LIGHT_EW || Y_LIGHT_EW3))) begin
light_ns_ZRYG <= 4'b0100;
end
else if ((time_ns_cnt != 6'b1) && (cur_state_ew == Y_LIGHT_EW3)) begin
light_ns_ZRYG <= 4'b0010;
end
end
定位:定位到南北方向的信号灯输出代码块,发现输出逻辑的条件不对,改为和倒计时一致的条件,即可同步输出。
//-- ns out
always @ (posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
light_ns_ZRYG <= 4'b1111;
end
else if (ns_cnt_en && (nxt_state_ew == R_LIGHT_EW)) begin
if(ew_cnt_en) light_ns_ZRYG <= 4'b0001;
else if(time_ew_cnt == 6'd26) light_ns_ZRYG <= 4'b0010;
else if(time_ew_cnt == 6'd21) light_ns_ZRYG <= 4'b1000;
else if(time_ew_cnt == 6'd6 ) light_ns_ZRYG <= 4'b0010;
end
else if (ns_cnt_en && (nxt_state_ew == Y_LIGHT_EW1)) begin
light_ns_ZRYG <= 4'b0100;
end
else if (ns_cnt_en && (nxt_state_ew == Y_LIGHT_EW3)) begin
light_ns_ZRYG <= 4'b0010;
end
end
(3)最终结果
结论:倒计时和信号灯的状态都符合要求,并且两个方向实现了互补的状态。
作者:xlinxdu 版权:本文是作者原创,版权归作者所有。 转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。