用狀態機實現交通燈控制器,仿真通過,有代碼以及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 begin 6 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 end 100 else if(change) 101 begin 102 //start <= 1'b1; 103 change = 1'b0; 104 nxt_state = Y1_2; 105 end 106 Y1_2 : if(trigger) 107 begin 108 //start <= 1'b1; 109 nxt_state = G1; 110 end 111 else if(change) 112 begin 113 //start <= 1'b1; 114 change = 1'b0; 115 nxt_state = G2; 116 end 117 G2 : if(trigger) 118 begin 119 //start <= 1'b1; 120 nxt_state = G1; 121 end 122 else if(change) 123 begin 124 //start <= 1'b1; 125 change = 1'b0; 126 nxt_state = Y2_1; 127 end 128 Y2_1 : if(trigger) 129 begin 130 //start <= 1'b1; 131 nxt_state = G1; 132 end 133 else if(change) 134 begin 135 //start <= 1'b1; 136 change = 1'b0; 137 nxt_state= L2; 138 end 139 L2 : if(trigger) 140 begin 141 //start <= 1'b1; 142 nxt_state = G1; 143 end 144 else if(change) 145 begin 146 //start <= 1'b1; 147 change = 1'b0; 148 nxt_state = Y2_2; 149 end 150 Y2_2 : if(trigger) 151 begin 152 //start <= 1'b1; 153 nxt_state = G1; 154 end 155 else if(change) 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 case(cur_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 if(start) 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 if(light_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 if(start) 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 if(light_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 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 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仿真結果:
可以看到,兩個交通燈從給定的狀態間循環,且時間同步。