【芯片前端】保持代码手感——交通灯

长时间做方案,还是通过一些练习来保持代码手感。这次的题目是交通灯,经典的状态机题目,但是我不爱写状态机所以还是按自己的思路来组织下。

题目

要求实现一个交通红绿灯,具有红黄绿三个小指示灯和一个行人按钮,正常情况下,机动车道指示灯按照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)
);

全部代码完成!

波形验证

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尼德兰的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值