长时间做方案,还是通过一些练习来保持代码手感。这次的题目是交通灯,经典的状态机题目,但是我不爱写状态机所以还是按自己的思路来组织下。
题目
要求实现一个交通红绿灯,具有红黄绿三个小指示灯和一个行人按钮,正常情况下,机动车道指示灯按照60时钟周期绿灯,5个时钟周期黄灯,10个时钟周期红灯循环。当行人按钮按下,如果剩余绿灯时间大于10个时钟,则缩短为10个时钟,小于10个时钟则保持不变。
注:机动车道的指示灯和人行道指示灯应该是配对的,当机动车道的灯为绿或者黄时,人行道的灯为红;当机动车道的灯为红时,人行道的灯为绿,为简便起见,只考虑机动车道的指示灯。
module triffic_light ( input rst_n, //异位复位信号,低电平有效 input clk, //时钟信号 input pass_request, output [7:0]clock, output red, output yellow, output green ); end
解题思路
写RTL无非就是基于时序写逻辑,所以我还是先把时序单元也就是打拍寄存器做出来:
module dffre#( parameter WIDTH = 1 )( input clk, input rst_n, input [WIDTH -1:0] d, input en, output [WIDTH -1:0] q ); reg [WIDTH -1:0]q; always @(posedge clk or negedge rst_n)begin if(~rst_n) q <= {WIDTH{1'b0}}; else if(en) q <= d; end endmodule module dffse#( parameter WIDTH = 1, parameter SET = {WIDTH{1'b1}} )( input clk, input rst_n, input [WIDTH -1:0] d, input en, output [WIDTH -1:0] q ); reg [WIDTH -1:0]q; always @(posedge clk or negedge rst_n)begin if(~rst_n) q <= SET; else if(en) q <= d; end endmodule
分别做了两种寄存器,一种是复位归零一种是复位置位的;
接下来思考下,这三个灯其实就是寄存器的输出结果,对于绿灯而言,他的复位值应该是1(假设复位后路灯亮),在clock信号跳变为0时 && (当前为绿灯 或 当前为红灯)时,会发生信号的跳变,当前为绿灯则下一时刻绿灯变为0,当前为红灯则下一时刻绿灯变为1:
wire green_en; wire green_d; assign green_en = (red | green) & (cnt_q == 8'b0); assign green_d = ~green; dffse #(.WIDTH(1), .SET(1'b1)) u_green( .clk (clk), .rst_n(rst_n), .d (green_d), .en (green_en), .q (green) );
对于黄灯而言,在clock信号跳变为0时 && (当前为绿灯 或 当前为黄灯)时,会发生信号的跳变,当前为绿灯则下一时刻黄灯变为1,当前为黄灯则下一时刻黄灯变为0:
wire yellow_en; wire yellow_d; assign yellow_en = (green | yellow) & (cnt_q == 8'b0); assign yellow_d = ~yellow; dffse #(.WIDTH(1), .SET(1'b0)) u_yellow( .clk (clk), .rst_n(rst_n), .d (yellow_d), .en (yellow_en), .q (yellow) );
对于红灯而言,在clock信号跳变为0时 && (当前为红灯 或 当前为黄灯)时,会发生信号的跳变,当前为红灯则下一时刻黄灯变为0,当前为黄灯则下一时刻黄灯变为1:
wire red_en; wire red_d; assign red_en = (red | yellow) & (cnt_q == 8'b0); assign red_d = ~red; dffse #(.WIDTH(1), .SET(1'b0)) u_red( .clk (clk), .rst_n(rst_n), .d (red_d), .en (red_en), .q (red) );
可以看到,三个信号灯的逻辑可以说是高度一致的,区别只在于绿灯的复位值为1,一上来咱们先开绿灯。
那么接下来就是clock的逻辑了,clock的逻辑可以分成两种情况考虑:跳变和正常递减。对于跳变的情况,其实就下面这几种,注释里写的很清楚了:
wire cnt_en; wire cnt_rerun_en; wire [8 -1:0]cnt_d; wire [8 -1:0]cnt_rerun_d; wire [8 -1:0]cnt_q; wire bypass = green & pass_request & (cnt_q>=10); assign cnt_rerun_en = (cnt_q == 8'b0) | (green & bypass); assign cnt_rerun_d = {8{red}} & 8'd59 //red -> green | {8{yellow}} & 8'd9 //yello -> red | {8{green & !bypass}} & 8'd4 //green -> yellow | {8{bypass}} & 8'd9; //green -> green
那么接下来就容易了,把正常递减和跳变组合在一起:
assign cnt_d = cnt_rerun_en ? cnt_rerun_d : cnt_q - 8'd1; assign clock = cnt_q; dffse #(.WIDTH(8), .SET(8'd59)) u_cnt( .clk (clk), .rst_n(rst_n), .d (cnt_d), .en (1'b1), .q (cnt_q) );
全部代码完成!
波形验证