用状态机实现交通灯控制器,仿真通过,有代码以及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 : iftrigger) 2 nxt_state = G1; 3 else ifchange) 5 begin 6 change = 1'b0; 7 nxt_state = Y2_1;
代码如下:
1 module traffic_lightrst_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 casecur_state) 67 IDLE : //ifchange) 68 iftrigger) //状态机通过trigger进入状态循环 69 begin 70 //start <= 1'b1; //start与计数值light_t_tmp是同步的,因此一同写在输出控制中 71 nxt_state = G1; //状态变换由组合逻辑实现,采用阻塞赋值即=,而非<= 72 end 73 G1 : iftrigger) 74 begin 75 //start <= 1'b1; 76 nxt_state = G1; 77 end 78 else ifchange) 79 begin 80 //start <= 1'b1; 81 change = 1'b0; 82 nxt_state = Y1_1; 83 end 84 Y1_1 : iftrigger) 85 begin 86 //start <= 1'b1; 87 nxt_state = G1; 88 end 89 else ifchange) 90 begin 91 //start <= 1'b1; 92 change = 1'b0; 93 nxt_state = L1; 94 end 95 L1 : iftrigger) 96 begin 97 //start <= 1'b1; 98 nxt_state = G1; 99 end 100 else ifchange) 101 begin 102 //start <= 1'b1; 103 change = 1'b0; 104 nxt_state = Y1_2; 105 end 106 Y1_2 : iftrigger) 107 begin 108 //start <= 1'b1; 109 nxt_state = G1; 110 end 111 else ifchange) 112 begin 113 //start <= 1'b1; 114 change = 1'b0; 115 nxt_state = G2; 116 end 117 G2 : iftrigger) 118 begin 119 //start <= 1'b1; 120 nxt_state = G1; 121 end 122 else ifchange) 123 begin 124 //start <= 1'b1; 125 change = 1'b0; 126 nxt_state = Y2_1; 127 end 128 Y2_1 : iftrigger) 129 begin 130 //start <= 1'b1; 131 nxt_state = G1; 132 end 133 else ifchange) 134 begin 135 //start <= 1'b1; 136 change = 1'b0; 137 nxt_state= L2; 138 end 139 L2 : iftrigger) 140 begin 141 //start <= 1'b1; 142 nxt_state = G1; 143 end 144 else ifchange) 145 begin 146 //start <= 1'b1; 147 change = 1'b0; 148 nxt_state = Y2_2; 149 end 150 Y2_2 : iftrigger) 151 begin 152 //start <= 1'b1; 153 nxt_state = G1; 154 end 155 else ifchange) 156 begin 157 //start <= 1'b1; 158 change = 1'b0; 159 nxt_state = G1; 160 end 161 default : nxt_state = IDLE; 162 endcase 163 end 164 165 //输出控制,组合逻辑 166 //start与计数值light_t_tmp是同步的,因此一同写在输出控制中 167 //always@posedge clk) //输出为组合逻辑,因此不在clk下动作 168 always@*) 169 if!rst_n) 170 begin 171 start = 1'b0; 172 light_t_tmp = 7'd0; 173 light1_tmp = 4'b0000; 174 light2_tmp = 4'b0000; 175 end 176 else casecur_state) 177 G1 : begin 178 start = 1'b1; 179 light_t_tmp = G1_T; 180 light1_tmp = 4'b1000; 181 light2_tmp = 4'b0001; 182 end 183 Y1_1 : begin 184 start = 1'b1; 185 light_t_tmp = Y1_T; 186 light1_tmp = 4'b0100; 187 light2_tmp = 4'b0001; 188 end 189 L1 : begin 190 start = 1'b1; 191 light_t_tmp = L1_T; 192 light1_tmp = 4'b0010; 193 light2_tmp = 4'b0001; 194 end 195 Y1_2 : begin 196 start = 1'b1; 197 light_t_tmp = Y1_T; 198 light1_tmp = 4'b0100; 199 light2_tmp = 4'b0001; 200 end 201 G2 : begin 202 start = 1'b1; 203 light_t_tmp = G2_T; 204 light1_tmp = 4'b0001; 205 light2_tmp = 4'b1000; 206 end 207 Y2_1 : begin 208 start = 1'b1; 209 light_t_tmp = Y2_T; 210 light1_tmp = 4'b0001; 211 light2_tmp = 4'b0100; 212 end 213 L2 : begin 214 start = 1'b1; 215 light_t_tmp = L2_T; 216 light1_tmp = 4'b0001; 217 light2_tmp = 4'b0010; 218 end 219 Y2_2 : begin 220 start = 1'b1; 221 light_t_tmp = Y2_T; 222 light1_tmp = 4'b0001; 223 light2_tmp = 4'b0100; 224 end 225 endcase 226 227 //灯亮时间倒计时 228 //时序逻辑,要在clk时钟下动作 229 always@posedge clk) 230 if!rst_n) 231 begin 232 light_t <= 7'd0; 233 end 234 else ifstart) 235 begin 236 start <= 1'b0; 237 light_t <= light_t_tmp; 238 end 239 else 240 begin 241 light_t <= light_t - 1'b1; 242 end 243 244 //在light_t减到2时,置为change信号 245 //用组合逻辑 246 always@*) 247 if!rst_n) 248 change = 1'b0; 249 else iflight_t == 4'd2) 250 change = 1'b1; 251 252 //输出寄存 253 always@posedge clk) 254 if!rst_n) 255 begin 256 light1 <= 4'd0; 257 light2 <= 4'd0; 258 end 259 else 260 begin 261 light1 <= light1_tmp; 262 light2 <= light2_tmp; 263 end 264 265 //下面是调试过程中对change以及start的控制 266 //因为使用了时序逻辑,导致各种错误 267 /* 268 always@posedge clk) 269 if!rst_n) 270 begin 271 light_t <= 7'd0; 272 //change <= 1'b0; //复位,开始状态转换 273 end 274 else ifstart) 275 begin 276 start <= 1'b0; 277 //change <= 1'b0; 278 //change_r <= 1'b0; 279 light_t <= light_t_tmp; 280 end 281 //else iflight_t == 4'd1) 282 // change <= 1'b1; 283 else 284 begin 285 light_t <= light_t - 1'b1; 286 //change <= 1'b0; 287 end 288 */ 289 290 /* 291 always@*) 292 if!rst_n) 293 change = 1'b0; //复位,开始状态转换 294 else ifstart) 295 change = 1'b0; 296 else iflight_t == 4'd1) 297 else iflight_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_nrst_n), 15 .clkclk), 16 .triggertrigger), 17 .light1light1), 18 .light2light2) 19 ); 20 21 parameter CLK_PERIOD = 10; 22 23 initial begin 24 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 end 37 38 always #CLK_PERIOD/2) clk = ~clk; 39 40 endmodule
ISIM仿真结果:
可以看到,两个交通灯从给定的状态间循环,且时间同步。