用状态机实现交通灯控制器,仿真通过,有代码以及testbench。
要求:
方向1是主干道,绿灯时间较长,交通灯状态循环为:
绿:40
黄:5
左:15
黄:5
红:55
方向2不是主干道,绿灯时间较少,交通灯状态循环为:
红:65
绿:30
黄:5
左:15
黄:5
注意:
- 此处将两个交通灯的状态合在一起,从而便于对两个等进行同步控制。将两个灯的5个状态合为8个状态,即:
绿1:40
黄1:5
左1:15
黄1:5
绿2:30
黄2:5
左2:15
黄2:5
对状态转换以及输出控制采用组合逻辑,在调试的过程中也曾使用时序逻辑来实现,仿真发现各种时序混乱。
在状态转换控制中,在change有效后,马上将其清零,代码如下,这是为了便于控制,调试时,发现这样比较方便,其他方法易出错。
在仿真图上就看不到change有效过,但仿真图上的其他信号是正确的。
1 G2 : if(trigger) 2 nxt_state = G1;3 else if(change) 5 begin6 change = 1'b0;7 nxt_state = Y2_1;
代码如下:
1 module traffic_light(rst_n, 2 clk, 3 trigger, //触发信号,使得状态机进入循环 4 light1, 5 light2 6 ); 7 input rst_n; 8 input clk; 9 input trigger; 10 11 output [3:0] light1; //4bit分别对应绿、黄、左、红灯 12 output [3:0] light2; 13 14 //灯亮时间 15 parameter G1_T = 7'd40; //方向1的亮灯时间 16 parameter Y1_T = 7'd5; 17 parameter L1_T = 7'd15; 18 19 parameter G2_T = 7'd30; //方向2的亮灯时间 20 parameter Y2_T = 7'd5; 21 parameter L2_T = 7'd15; 22 23 //状态编码 24 parameter IDLE = 4'd0; //此处为了方便采用简单的binary编码,可用one-hot或gray提高性能 25 parameter G1 = 4'd1; 26 parameter Y1_1 = 4'd2; 27 parameter L1 = 4'd3; 28 parameter Y1_2 = 4'd4; 29 30 parameter G2 = 4'd5; 31 parameter Y2_1 = 4'd6; 32 parameter L2 = 4'd7; 33 parameter Y2_2 = 4'd8; 34 35 reg [3:0] cur_state; 36 reg [3:0] nxt_state; 37 38 reg [3:0] light1; 39 reg [3:0] light2; 40 41 reg [3:0] light1_tmp; 42 reg [3:0] light2_tmp; 43 44 reg [6:0] light_t_tmp; 45 reg [6:0] light_t; 46 47 reg change; 48 reg start; 49 50 //状态寄存,时序逻辑 51 always@(posedge clk) 52 if(!rst_n) 53 cur_state <= IDLE; 54 else 55 cur_state <= nxt_state; 56 57 //状态转换,组合逻辑 58 always@(*) 59 if(!rst_n) 60 begin 61 //start <= 1'b0; 62 nxt_state = IDLE; 63 end 64 else 65 begin 66 case(cur_state) 67 IDLE : //if(change) 68 if(trigger) //状态机通过trigger进入状态循环 69 begin 70 //start <= 1'b1; //start与计数值light_t_tmp是同步的,因此一同写在输出控制中 71 nxt_state = G1; //状态变换由组合逻辑实现,采用阻塞赋值即=,而非<= 72 end 73 G1 : if(trigger) 74 begin 75 //start <= 1'b1; 76 nxt_state = G1; 77 end 78 else if(change) 79 begin 80 //start <= 1'b1; 81 change = 1'b0; 82 nxt_state = Y1_1; 83 end 84 Y1_1 : if(trigger) 85 begin 86 //start <= 1'b1; 87 nxt_state = G1; 88 end 89 else if(change) 90 begin 91 //start <= 1'b1; 92 change = 1'b0; 93 nxt_state = L1; 94 end 95 L1 : if(trigger) 96 begin 97 //start <= 1'b1; 98 nxt_state = G1; 99 end100 else if(change) 101 begin102 //start <= 1'b1;103 change = 1'b0;104 nxt_state = Y1_2;105 end106 Y1_2 : if(trigger) 107 begin108 //start <= 1'b1;109 nxt_state = G1;110 end111 else if(change) 112 begin113 //start <= 1'b1;114 change = 1'b0;115 nxt_state = G2;116 end117 G2 : if(trigger) 118 begin119 //start <= 1'b1;120 nxt_state = G1;121 end122 else if(change) 123 begin124 //start <= 1'b1;125 change = 1'b0;126 nxt_state = Y2_1;127 end128 Y2_1 : if(trigger) 129 begin130 //start <= 1'b1;131 nxt_state = G1;132 end133 else if(change) 134 begin135 //start <= 1'b1;136 change = 1'b0;137 nxt_state= L2;138 end139 L2 : if(trigger) 140 begin141 //start <= 1'b1;142 nxt_state = G1;143 end144 else if(change) 145 begin146 //start <= 1'b1;147 change = 1'b0;148 nxt_state = Y2_2;149 end150 Y2_2 : if(trigger) 151 begin152 //start <= 1'b1;153 nxt_state = G1;154 end155 else if(change) 156 begin157 //start <= 1'b1;158 change = 1'b0;159 nxt_state = G1;160 end161 default : nxt_state = IDLE;162 endcase163 end 164 165 //输出控制,组合逻辑166 //start与计数值light_t_tmp是同步的,因此一同写在输出控制中 167 //always@(posedge clk) //输出为组合逻辑,因此不在clk下动作168 always@(*)169 if(!rst_n)170 begin171 start = 1'b0;172 light_t_tmp = 7'd0;173 light1_tmp = 4'b0000;174 light2_tmp = 4'b0000;175 end176 else case(cur_state)177 G1 : begin178 start = 1'b1;179 light_t_tmp = G1_T;180 light1_tmp = 4'b1000;181 light2_tmp = 4'b0001; 182 end183 Y1_1 : begin184 start = 1'b1;185 light_t_tmp = Y1_T;186 light1_tmp = 4'b0100;187 light2_tmp = 4'b0001; 188 end189 L1 : begin190 start = 1'b1;191 light_t_tmp = L1_T;192 light1_tmp = 4'b0010;193 light2_tmp = 4'b0001; 194 end195 Y1_2 : begin196 start = 1'b1;197 light_t_tmp = Y1_T;198 light1_tmp = 4'b0100;199 light2_tmp = 4'b0001; 200 end201 G2 : begin202 start = 1'b1;203 light_t_tmp = G2_T;204 light1_tmp = 4'b0001;205 light2_tmp = 4'b1000; 206 end207 Y2_1 : begin208 start = 1'b1;209 light_t_tmp = Y2_T;210 light1_tmp = 4'b0001;211 light2_tmp = 4'b0100; 212 end213 L2 : begin214 start = 1'b1;215 light_t_tmp = L2_T;216 light1_tmp = 4'b0001;217 light2_tmp = 4'b0010; 218 end219 Y2_2 : begin220 start = 1'b1;221 light_t_tmp = Y2_T;222 light1_tmp = 4'b0001;223 light2_tmp = 4'b0100; 224 end225 endcase226 227 //灯亮时间倒计时228 //时序逻辑,要在clk时钟下动作229 always@(posedge clk)230 if(!rst_n)231 begin232 light_t <= 7'd0;233 end234 else if(start)235 begin236 start <= 1'b0;237 light_t <= light_t_tmp;238 end239 else240 begin241 light_t <= light_t - 1'b1;242 end243 244 //在light_t减到2时,置为change信号245 //用组合逻辑246 always@(*)247 if(!rst_n)248 change = 1'b0; 249 else if(light_t == 4'd2)250 change = 1'b1;251 252 //输出寄存 253 always@(posedge clk)254 if(!rst_n)255 begin256 light1 <= 4'd0;257 light2 <= 4'd0;258 end259 else260 begin261 light1 <= light1_tmp;262 light2 <= light2_tmp;263 end264 265 //下面是调试过程中对change以及start的控制 266 //因为使用了时序逻辑,导致各种错误267 /*268 always@(posedge clk)269 if(!rst_n)270 begin271 light_t <= 7'd0;272 //change <= 1'b0; //复位,开始状态转换273 end274 else if(start)275 begin276 start <= 1'b0;277 //change <= 1'b0;278 //change_r <= 1'b0;279 light_t <= light_t_tmp;280 end281 //else if(light_t == 4'd1)282 // change <= 1'b1;283 else284 begin285 light_t <= light_t - 1'b1;286 //change <= 1'b0;287 end288 */289 290 /* 291 always@(*)292 if(!rst_n)293 change = 1'b0; //复位,开始状态转换294 else if(start)295 change = 1'b0;296 else if(light_t == 4'd1)297 else if(light_t == 4'd2)298 change = 1'b1; 299 */ 300 301 endmodule
testbench如下:
1 module traffic_light_tb; 2 3 // Inputs 4 reg rst_n; 5 reg clk; 6 reg trigger; 7 8 // Outputs 9 wire [3:0] light1;10 wire [3:0] light2;11 12 // Instantiate the Unit Under Test (UUT)13 traffic_light uut (14 .rst_n(rst_n), 15 .clk(clk), 16 .trigger(trigger),17 .light1(light1), 18 .light2(light2)19 );20 21 parameter CLK_PERIOD = 10;22 23 initial begin24 rst_n = 0;25 clk = 1;26 trigger = 0;27 28 #100; 29 rst_n = 1;30 trigger = 1; //初始给出trigger信号,使得状态机进行状态循环31 #CLK_PERIOD trigger = 0;32 33 //#1000 trigger = 1; //trigger复位状态机34 //#100 trigger = 0;35 36 end37 38 always #(CLK_PERIOD/2) clk = ~clk;39 40 endmodule
ISIM仿真结果:
可以看到,两个交通灯从给定的状态间循环,且时间同步。