31. 数据累加输出
题目
实现串行输入数据累加输出,输入端输入 8 bit 数据,每当模块接收到4个输入数据后,输出端输出4个接收到数据的累加结果。输入端和输出端与上下游的交互采用valid-read
双向握手机制。要求上下游均能满速传输时,数据传输无气泡,不能由于本模块的设计原因产生额外的性能损失。
电路的接口如下图所示。valid_a
用来指示数据输入data_in
的有效性,valid_b
用来指示数据输出 data_out
的有效性;ready_a
用来指示本模块是否准备好接收上游数据,ready_b
表示下游是否准备好接收本模块的输出数据;clk
是时钟信号;rst_n
是异步复位信号。
输入描述:
input clk
,
input rst_n
,
input [7:0] data_in
,
input valid_a
,
input ready_b
输出描述:
output ready_a
,
output reg valid_b
,
output reg [9:0] data_out
解法
input clk ,
input rst_n ,
input [7:0] data_in ,
input valid_a ,
input ready_b ,
output ready_a ,
output reg valid_b ,
output reg [9:0] data_out
reg [1:0] count;
assign ready_a = ~valid_b | ready_b;
always @ (posedge clk or negedge rst_n) begin
if( ~rst_n ) begin
count <= 2'b0;
end
else begin
if(valid_a & ready_a) begin
if( count == 2'd3)
count <= 2'd0;
else
count <= count + 2'd1;
end
end
end
always @ (posedge clk or negedge rst_n) begin
if( ~rst_n ) begin
data_out <= 10'b0;
end
else begin
if(valid_a && ready_a) begin
if(count == 2'd0) begin
data_out <= data_in;
end
else begin
data_out <= data_out + data_in;
end
end
end
end
always @ (posedge clk or negedge rst_n) begin
if( ~rst_n ) begin
valid_b <= 1'b0;
end
else begin
if(count == 2'd3 && valid_a && ready_a) begin
valid_b <= 1'b1;
end
else if(valid_b && ready_b)begin
valid_b <= 1'b0;
end
else begin
valid_b <= valid_b;
end
end
end
32. 非整数倍数据位宽转换 24 to 128
题目
实现数据位宽转换电路,实现 24 bit 数据输入转换为 128 bit 数据输出。其中,先到的数据应置于输出的高bit位。
电路的接口如下图所示。valid_in
用来指示数据输入data_in
的有效性,valid_out
用来指示数据输出data_out
的有效性;clk
是时钟信号;rst_n
是异步复位信号。
输入描述:
input clk,
input rst_n,
input valid_in,
input [23:0] data_in,
输出描述:
output reg valid_out ,
output reg [127:0] data_out
解法
入数据是 24 bit ,输出数据是 128 bit 。因为 128×3=24×16,所以每输入 16 个有效数据,就可以产生三个完整的输出。因此设置一个仅在输入数据有效时工作的计数器 cnt
,计数范围是 0-15。
设置一个数据暂存器 data_lock
,每当输入有效时,将数据从低位移入。
每当计数器 cnt
计数到5、10、15时,data_out
要进行更新,并拉高 valid_out
一个周期。
reg [3:0] cnt;
reg [127:0] data_lock;
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
cnt <= 0;
else
cnt <= ~valid_in? cnt:cnt+1;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
valid_out <= 0;
else
valid_out <= (cnt==5 || cnt==10 || cnt==15)&&valid_in;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
data_lock <= 0;
else
data_lock <= valid_in? {data_lock[103:0], data_in}: data_lock;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
data_out <= 0;
else if(cnt==5)
data_out <= valid_in? {data_lock[119:0], data_in[23:16]}: data_out;
else if(cnt==10)
data_out <= valid_in? {data_lock[111:0], data_in[23: 8]}: data_out;
else if(cnt==15)
data_out <= valid_in? {data_lock[103:0], data_in[23: 0]}: data_out;
else
data_out <= data_out;
end
33. 非整数倍数据位宽转换8to12
题目
实现数据位宽转换电路,实现 8 bit 数据输入转换为 12 bit 数据输出。其中,先到的数据应置于输出的高 bi t位。
电路的接口如下图所示。valid_in
用来指示数据输入 data_in
的有效性, valid_out
用来指示数据输出 data_out
的有效性;clk
是时钟信号;rst_n
是异步复位信号。
解法
input clk ,
input rst_n ,
input valid_in ,
input [7:0] data_in ,
output reg valid_out,
output reg [11:0] data_out
非整数倍的数据位宽转换 题型,第一,找最大倍数关系,比如 24->128,则中间缓存大小为 24*5=120 ,本题 8->12,中间缓存大小为 8。采用缓存 buff
保存data_in
。第二,确定计数周期,在每次 valid
有效的时钟下, cnt
自增1,当cnt=1,2
时,data_out
都会输出一次,那么计数周期就为0-2。第三,在每次输出时,使用拼接运算符拼接buff
和data_in
来输出data_out
,并将未输出的data_in保存在缓存buff
(在 line 34 没有保存是因为此时data_in全部输出)。
reg[7:0] buff;
reg[1:0] cnt;
always@(negedge rst_n or posedge clk)begin
if(~rst_n)
cnt <= 0;
else if(valid_in)
if(cnt == 2)
cnt <= 0;
else
cnt <= cnt + 1;
end
always@(negedge rst_n or posedge clk)begin
if(~rst_n)begin
buff <= 0;
data_out <= 0;
end
else if(valid_in)begin
if(cnt == 1)begin
data_out <= {buff, data_in[7:4]};
buff[3:0] <= data_in[3:0];
end
else if(cnt == 2)begin
data_out <= {buff[3:0], data_in};
end
else
buff <= data_in;
end
end
always@(negedge rst_n or posedge clk)begin
if(~rst_n)
valid_out <= 0;
else if(valid_in && (cnt == 1 || cnt == 2))
valid_out <= 1;
else
valid_out <= 0;
end
34. 整数倍数据位宽转换 8 to 16
题目
实现数据位宽转换电路,实现 8 bit 数据输入转换为 16 bit 数据输出。其中,先到的 8 bit 数据应置于输出 16 bit 的高 8 位。
电路的接口如下图所示。valid_in
用来指示数据输入 data_in
的有效性,valid_out
用来指示数据输出 data_out
的有效性;clk
是时钟信号;rst_n
是异步复位信号。
解法
input clk ,
input rst_n ,
input valid_in ,
input [7:0] data_in ,
output reg valid_out,
output reg [15:0] data_out
reg cnt;
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
cnt <= 1'b0;
end
else begin
if(valid_in) begin
cnt <= cnt + 1'b1; // cnt = ~cnt;
end
end
end
reg [7:0] data_reg;
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_out <= 16'b0;
valid_out <= 1'b0;
end
else begin
if(valid_in) begin
if(cnt == 1'b0) begin
data_reg <= data_in;
valid_out <= 1'b0;
end
else begin
data_out <= {data_reg, data_in};
valid_out <= 1'b1;
end
end
else begin
valid_out <= 1'b0;
end
end
end
35.状态机-非重叠的序列检测
题目
设计一个状态机,用来检测序列 10111,要求:
1、进行非重叠检测 即 101110111
只会被检测通过一次
2、寄存器输出且同步输出结果
注意 rst 为低电平复位
解法
input wire clk ,
input wire rst ,
input wire data ,
output reg flag
reg [1:0] cnt;
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
cnt <= 2'b0;
end
else begin
if(valid_in) begin
if(cnt == 2'd2)
cnt <= 2'd0;
else
cnt <= cnt + 2'd1;
end
end
end
reg [7:0] data_reg;
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
data_out <= 12'b0;
valid_out <= 1'b0;
data_reg <= 8'b0;
end
else begin
if(valid_in) begin
case(cnt)
2'd0 : begin
data_reg <= data_in;
valid_out <= 1'b0;
end
2'd1 : begin
data_out <= {data_reg[7:0], data_in[7:4]};
data_reg[3:0] <= data_in[3:0];
valid_out <= 1'b1;
end
2'd2 : begin
data_out <= {data_reg[3:0], data_in[7:0]};
valid_out <= 1'b1;
end
default : begin
data_reg <= 8'b0;
data_out <= 12'b0;
valid_out <= 1'b0;
end
endcase
end
else begin
valid_out <= 1'b0;
end
end
end
36. 状态机-重叠序列检测
题目
设计一个状态机,用来检测序列 1011,要求:
1、进行重叠检测 即 10110111
会被检测通过 2 次
2、寄存器输出,在序列检测完成下一拍输出检测有效
注意 rst 为低电平复位
解法
input wire clk ,
input wire rst ,
input wire data ,
output reg flag
parameter S0=0, S1=1, S2=2, S3=3, S4=4;
reg [2:0] state, nstate;
always@(posedge clk or negedge rst) begin
if(~rst)
state <= S0;
else
state <= nstate;
end
always@(*) begin
if(~rst)
nstate <= S0;
else
case(state)
S0 : nstate <= data? S1: S0;
S1 : nstate <= data? S1: S2;
S2 : nstate <= data? S3: S0;
S3 : nstate <= data? S4: S2;
S4 : nstate <= data? S1: S2;
default: nstate <= S0;
endcase
end
always@(posedge clk or negedge rst) begin
if(~rst)
flag <= 0;
else
flag <= state==S4;
end
37.时钟分频(偶数)
题目
请使用 D 触发器设计一个同时输出 2/4/8
分频的 50% 占空比的时钟分频器
注意 rst 为低电平复位
解法
input wire rst ,
input wire clk_in,
output wire clk_out2,
output wire clk_out4,
output wire clk_out8
reg clk_out2_r, clk_out4_r, clk_out8_r;
always@(posedge clk_in or negedge rst) begin
if(~rst)
clk_out2_r <= 0;
else
clk_out2_r <= ~clk_out2_r;
end
always@(posedge clk_out2 or negedge rst) begin
if(~rst)
clk_out4_r <= 0;
else
clk_out4_r <= ~clk_out4_r;
end
always@(posedge clk_out4 or negedge rst) begin
if(~rst)
clk_out8_r <= 0;
else
clk_out8_r <= ~clk_out8_r;
end
assign clk_out2 = clk_out2_r;
assign clk_out4 = clk_out4_r;
assign clk_out8 = clk_out8_r;
38. 自动贩售机1
题目
设计一个自动贩售机,输入货币有三种,为 0.5/1/2 元,饮料价格是 1.5 元,要求进行找零,找零只会支付 0.5 元。
ps:
投入的货币会自动经过边沿检测并输出一个在时钟上升沿到 1,在下降沿到 0 的脉冲信号
注意 rst 为低电平复位
解法
信号 | 含义 |
---|---|
d1 | 0.5元 |
d2 | 1元 |
d3 | 2元 |
ou1 | 饮料 |
out2 | 零钱 |
输入描述:
输入信号 clk rst d1 d2 d3
类型 wire
输出描述:
输出信号 out1 , [1:0] out2
类型 reg
parameter S0 = 'd0, S1 = 'd1, S2 = 'd2, S3 = 'd3 , S4 = 'd4, S5 = 'd5 , S6 = 'd6;
reg [2:0] current_state;
reg [2:0] next_state;
wire [2:0] input_state;//将输入组合起来
assign input_state = {d1,d2,d3};
always@(posedge clk or negedge rst)begin
if(rst == 1'b0)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end
always@(*)begin
case(current_state)
S0:begin
case(input_state)
3'b100:next_state = S1 ;
3'b010:next_state = S2 ;
3'b001:next_state = S4 ;
default:next_state = next_state;
endcase
end
S1:begin
case(input_state)
3'b100:next_state = S2 ;
3'b010:next_state = S3 ;
3'b001:next_state = S5 ;
default:next_state = next_state;
endcase
end
S2:begin
case(input_state)
3'b100:next_state = S3 ;
3'b010:next_state = S4 ;
3'b001:next_state = S6 ;
default:next_state = next_state;
endcase
end
default:begin
next_state = S0;
end
endcase
end
always@(posedge clk or negedge rst)begin
if(rst == 1'b0)begin
out1 <= 1'b0;
out2 <= 2'b0;
end
else begin
case(next_state)
S3: begin out1 <= 1'b1;out2 <= 2'b0; end
S4: begin out1 <= 1'b1;out2 <= 2'b1; end
S5: begin out1 <= 1'b1;out2 <= 2'b10; end
S6: begin out1 <= 1'b1;out2 <= 2'b11; end
default: begin out1 <= 1'b0;out2 <= 2'b0; end
endcase
end
end
39.自动贩售机2
题目
设计一个自动贩售机,输入货币有两种,为 0.5/1 元,饮料价格是 1.5/2.5 元,要求进行找零,找零只会支付 0.5 元。
ps:
1、投入的货币会自动经过边沿检测并输出一个在时钟上升沿到1,在下降沿到0的脉冲信号
2、此题忽略出饮料后才能切换饮料的问题
注意 rst 为低电平复位
信号示意图:
解法
信号 | 含义 |
---|---|
d1 | 0.5元 |
d2 | 1元 |
sel | 选择饮料 |
ou1 | 饮料1 |
out2 | 饮料2 |
out3 | 零钱 |
input wire clk ,
input wire rst ,
input wire d1 ,
input wire d2 ,
input wire sel ,
output reg out1,
output reg out2,
output reg out3
parameter S0=0, S0_5=1, S1=2, S1_5=3, S2=4, S2_5=5, S3=6;
reg[2:0] state, nstate;
always@(posedge clk or negedge rst) begin
if(~rst)
state <= 0;
else
state <= nstate;
end
always@(*) begin
case(state)
S0 : nstate = d1? S0_5:
d2? S1:
nstate;
S0_5 : nstate = d1? S1:
d2? S1_5:
nstate;
S1 : nstate = d1? S1_5:
d2? S2:
nstate;
S1_5 : nstate = ~sel? S0:
d1? S2:
d2? S2_5:
nstate;
S2 : nstate = ~sel? S0:
d1? S2_5:
d2? S3:
nstate;
default: nstate = S0;
endcase
end
always@(*) begin
if(~rst) begin
{out1, out2, out3} = 3'b000;
end
else begin
case(state)
S0, S0_5, S1: {out1, out2, out3} = 0;
S1_5 : {out1, out2, out3} = ~sel? 3'b100: 3'b000;
S2 : {out1, out2, out3} = ~sel? 3'b101: 3'b000;
S2_5 : {out1, out2, out3} = ~sel? 3'b101: 3'b010;
S3 : {out1, out2, out3} = ~sel? 3'b101: 3'b011;
default : {out1, out2, out3} = 3'b000;
endcase
end
end
40. 占空比50%的奇数分频
题目
设计一个同时输出 7 分频的时钟分频器,占空比要求为 50%
注意 rst 为低电平复位
输入描述:
输入信号 clk_in rst
类型 wire
输出描述:
输出信号 clk_out7
类型 wire
解法
input wire rst ,
input wire clk_in,
output wire clk_out7
对于奇数分频电路,主要难点在于 50% 占空比的实现。单触发沿在奇数分频中是没有办法实现 50% 占空比的,因此需要考虑使用双边沿加组合逻辑实现50%占空比
通过简单的状态转移表就能够得出,clkout7
的翻转第一次是在上升沿,第二次是在下降沿。
clkin | clkout7 | cnt |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
0 | 1 | 1 |
1 | 1 | 2 |
0 | 1 | 2 |
1 | 1 | 3 |
0 | 1 | 3 |
1 | 1 | 4 |
0 | 0 | 4 |
1 | 0 | 5 |
0 | 0 | 5 |
1 | 0 | 6 |
0 | 0 | 6 |
1 | 0 | 7 |
0 | 0 | 7 |
1 | 1 | 8 |
要让两个不同的时钟边沿触发的分频信号进行组合逻辑,分析应该有下面的时许构成:
很容易看出,做法应该是将两个信号进行或操作
reg [3:0] cnt ;
always @(posedge clk_in or negedge rst) begin
if (!rst) begin
cnt <= 'b0 ;
end
else if (cnt == N-1) begin
cnt <= 'b0 ;
end
else begin
cnt <= cnt + 1'b1 ;
end
end
reg clkp ;
always @(posedge clk_in or negedge rst) begin
if (!rst) begin
clkp <= 1'b0 ;
end
else if (cnt == (N>>1)) begin
clkp <= 1 ;
end
else if (cnt == N-1) begin
clkp <= 0 ;
end
end
reg clkn ;
always @(negedge clk_in or negedge rst) begin
if (!rst) begin
clkn <= 1'b0 ;
end
else if (cnt == (N>>1) ) begin
clkn <= 1 ;
end
else if (cnt == N-1) begin
clkn <= 0 ;
end
end
assign clk_out7 = clkp | clkn ;
41.任意小数分频
题目
请设计一个可以实现任意小数分频的时钟分频器,比如说8.7分频的时钟信号
注意rst为低电平复位
提示:
其实本质上是一个简单的数学问题,即如何使用最小公倍数得到时钟周期的分别频比。
设小数为 n
,此处以 8.7 倍分频的时钟周期为例。
首先,由于不能在硬件上进行小数的运算(比如 2.1 个时钟这种是不现实的,也不存在 3.3个寄存器),小数分频不能做到分频后每个时钟周期都是源时钟的 nn
倍,也无法实现占空比为 1/2,因此,考虑小数分频,其实现方式应当为 53 个 clkout
时钟周期是10个 clkin
时钟周期的8.7倍。
解法
input wire clk_in,
input wire rst,
output wire clk_out
parameter M_N = 8'd87;
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期
假设输出 clk_out
是输入clk_in
的N分频。首先要将分频系数N化为分数形式,比如 4.75→19/44 , 3.4→34/10。本题中,8.7 可以化为 87/10。这意味着在 87 个clk_in
周期内输出 10 个clk_out
周期就可以实现分频。
然后采用若干种(一般是两种)整数分频在87个原周期clk_in
内产生10个新时钟周期clk_out
。整数分频的分频系数有很多种选择,但要尽可能接近,提高clk_out
的均匀度。一般推荐在小数分频系数N的附近选取。因为8<N<9,所以两个整数分频系数是8和9。接着要计算87个clk_out
周期分别有多少个是8分频和9分频的。设每10个clk_out
中有x个8分频输出和y个9分频输出,则可列出如下方程:
可得 x=3,y=7。也就是 3 个 8 分频 和 7 个 9 分频一组,循环输出,就等效于 8.7 分频。 最后安排组内 8 分频和 9 分频的位置。这里的方法也不固定,不过本题要求 3 个8分频先输出,再输出 7 个 9 分频,如下图。
题目提供的:
- M_N=87。一组
clk_out
输出需要的clk_in
时钟数量。 - c89=24。切换分频系数的时间点。从这里可以看出,本题要求先输出3个8分频。
- div_e=8。分频系数1。
- div_o=9。分频系数2。
自定义的:
- cyc_cnt。对
clk_in
进行计数,达到M_N后清零。 - div_flag。8/9分频标志。当
div_flag==0
时是8分频;当div_flag==1
时是9分频。cyc_cnt==M_N-1
或者cyc_cnt==c89-1
时该标志位翻转。 - clk_cnt。用于产生分频输出。当
div_flag==0
时,计数最大值是div_e-1
;当div_flag==1
时,计数最大值是div_o-1
。 - clk_out_r。根据
clk_cnt
和div_flag
产生分频输出。
reg [3:0] clk_cnt;
reg [6:0] cyc_cnt;
reg div_flag;
reg clk_out_r;
assign clk_out = clk_out_r;
always@(posedge clk_in or negedge rst) begin
if(~rst)
clk_cnt <= 0;
else if(~div_flag)
clk_cnt <= clk_cnt==(div_e-1)? 0: clk_cnt+1;
else
clk_cnt <= clk_cnt==(div_o-1)? 0: clk_cnt+1;
end
always@(posedge clk_in or negedge rst) begin
if(~rst)
cyc_cnt <= 0;
else
cyc_cnt <= cyc_cnt==(M_N-1)? 0: cyc_cnt+1;
end
always@(posedge clk_in or negedge rst) begin
if(~rst)
div_flag <= 0;
else
div_flag <= cyc_cnt==(M_N-1)||cyc_cnt==(c89-1)? ~div_flag: div_flag;
end
always@(posedge clk_in or negedge rst) begin
if(~rst)
clk_out_r <= 0;
else if(~div_flag)
clk_out_r <= clk_cnt<=((div_e>>2)+1);
else
clk_out_r <= clk_cnt<=((div_o>>2)+1);
end
42.无占空比要去的奇数分频
题目
请设计一个同时输出 5 分频的时钟分频器,本题对占空比没有要求
注意 rst 为低电平复位
解法
input wire rst ,
input wire clk_in,
output wire clk_out5
parameter q=6;
reg[3:0] cnt;
reg out;
assign clk_out5 = out;
always@(clk_in or rst)begin
if(~rst) cnt <= 0;
else cnt <= cnt==9? 0: cnt+1;
end
always@(*)begin
if(~rst) out <= 0;
else if(cnt==2 || cnt==q) out = ~out;
end
43.根据状态转移写状态机-三段式
题目
如图所示为两种状态机中的一种,请根据状态转移图写出代码,状态转移线上的0/0
等表示的意思是过程中 data/flag
的值。
要求:
1、 必须使用对应类型的状态机
2、 使用三段式描述方法,输出判断要求要用到对现态的判断
解法
input wire clk ,
input wire rst ,
input wire data ,
output reg flag
三段式包含三个进程:
第一个进程(同步时序always),描述次态到现态的转移
第二个进程(组合逻辑always),描述状态转移条件的判断
第三个进程(同步时序always),描述状态的寄存器输出
摩尔机和米勒机的区别:主要区别在于状态机的输出与当前的状态是否有关,下面用两段式来描述两者的区别。
需要注意的是,如果使用三段式描述法,两者的区别主要聚焦于第三段的判断是基于现态还是次态。
摩尔状态机要比米勒状态机少一个状态,摩尔状态机慢一个周期;米勒状态机使用当前输入和当前状态共同判断,摩尔状态机不需要当前输入。
parameter S0 = 'd0, S1 = 'd1, S2 = 'd2, S3 = 'd3 ;
reg [2:0] current_state;
reg [2:0] next_state;
always@(posedge clk or negedge rst)begin
if(rst == 1'b0)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end
always@(*)begin
case(current_state)
S0:begin
next_state = data ? S1 : S0;
end
S1:begin
next_state = data ? S2 : S1;
end
S2:begin
next_state = data ? S3 : S2;
end
S3:begin
next_state = data ? S0 : S3;
end
default:begin next_state = S0; end
endcase
end
always@(posedge clk or negedge rst)begin
if(rst == 1'b0)begin
flag <= 1'b0;
end
else begin
if(current_state == S3)begin
if (data) flag <= 1'b1;
else flag <= 1'b0;
end
else begin
flag <= 1'b0;
end
end
end
44.根据状态转移写状态机-二段式
题目
如图所示为两种状态机中的一种,请根据状态转移图写出代码,状态转移线上的0/0
等表示的意思是过程中data/flag
的值。
要求:
1、 必须使用对应类型的状态机
2、 使用二段式描述方法
解法
input wire clk ,
input wire rst ,
input wire data ,
output reg flag
parameter S0 = 'd0, S1 = 'd1, S2 = 'd2, S3 = 'd3 ,S4 = 'd4 ;
reg [2:0] current_state;
reg [2:0] next_state;
always@(posedge clk or negedge rst)begin
if(rst == 1'b0)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end
always@(*)begin
case(current_state)
S0:begin
next_state = data ? S1 : S0;
flag = 1'b0;
end
S1:begin
next_state = data ? S2 : S1;
flag = 1'b0;
end
S2:begin
next_state = data ? S3 : S2;
flag = 1'b0;
end
S3:begin
next_state = data ? S4 : S3;
flag = 1'b0;
end
S4:begin
next_state = data ? S1 : S0;
flag = 1'b1;
end
default:begin
next_state = S0;
flag = 1'b0;
end
endcase
end
45.异步FIFO
题目
请根据题目中给出的双口 RAM 代码和接口描述,实现异步 FIFO,要求 FIFO 位宽和深度参数化可配置。
双口 RAM 端口说明:
端口名 | I/O | 描述 |
---|---|---|
wclk | input | 写数据时钟 |
wenc | input | 写使能 |
waddr | input | 写地址 |
wdata | input | 输入数据 |
rclk | input | 读数据时钟 |
renc | input | 读使能 |
raddr | input | 读地址 |
rdata | output | 输出数据 |
同步 FIFO 端口说明:
端口名 | I/O | 描述 |
---|---|---|
wclk | input | 写时钟 |
rclk | input | 读时钟 |
wrstn | input | 写时钟域异步复位 |
rrstn | input | 读时钟域异步复位 |
winc | input | 写使能 |
rinc | input | 读使能 |
wdata | input | 写数据 |
wfull | output | 写满信号 |
rempty | output | 读空信号 |
rdata | output | 读数据 |
双口 RAM 代码如下,可在本题答案中添加并例化此代码。
module dual_port_RAM #(parameter DEPTH = 16,
parameter WIDTH = 8)(
input wclk
,input wenc
,input [$clog2(DEPTH)-1:0] waddr //深度对2取对数,得到地址的位宽。
,input [WIDTH-1:0] wdata //数据写入
,input rclk
,input renc
,input [$clog2(DEPTH)-1:0] raddr //深度对2取对数,得到地址的位宽。
,output reg [WIDTH-1:0] rdata //数据输出
);
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
always @(posedge wclk) begin
if(wenc)
RAM_MEM[waddr] <= wdata;
end
always @(posedge rclk) begin
if(renc)
rdata <= RAM_MEM[raddr];
end
endmodule
输入描述:
input wclk ,
input rclk ,
input wrstn ,
input rrstn ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata
输出描述:
output wire wfull ,
output wire rempty ,
output wire [WIDTH-1:0] rdata
解法
parameter ADDR_WIDTH = $clog2(DEPTH);
/**********************addr bin gen*************************/
reg [ADDR_WIDTH:0] waddr_bin;
reg [ADDR_WIDTH:0] raddr_bin;
always @(posedge wclk or negedge wrstn) begin
if(~wrstn) begin
waddr_bin <= 'd0;
end
else if(!wfull && winc)begin
waddr_bin <= waddr_bin + 1'd1;
end
end
always @(posedge rclk or negedge rrstn) begin
if(~rrstn) begin
raddr_bin <= 'd0;
end
else if(!rempty && rinc)begin
raddr_bin <= raddr_bin + 1'd1;
end
end
/**********************addr gray gen*************************/
wire [ADDR_WIDTH:0] waddr_gray;
wire [ADDR_WIDTH:0] raddr_gray;
reg [ADDR_WIDTH:0] wptr;
reg [ADDR_WIDTH:0] rptr;
assign waddr_gray = waddr_bin ^ (waddr_bin>>1);
assign raddr_gray = raddr_bin ^ (raddr_bin>>1);
always @(posedge wclk or negedge wrstn) begin
if(~wrstn) begin
wptr <= 'd0;
end
else begin
wptr <= waddr_gray;
end
end
always @(posedge rclk or negedge rrstn) begin
if(~rrstn) begin
rptr <= 'd0;
end
else begin
rptr <= raddr_gray;
end
end
/**********************syn addr gray*************************/
reg [ADDR_WIDTH:0] wptr_buff;
reg [ADDR_WIDTH:0] wptr_syn;
reg [ADDR_WIDTH:0] rptr_buff;
reg [ADDR_WIDTH:0] rptr_syn;
always @(posedge wclk or negedge wrstn) begin
if(~wrstn) begin
rptr_buff <= 'd0;
rptr_syn <= 'd0;
end
else begin
rptr_buff <= rptr;
rptr_syn <= rptr_buff;
end
end
always @(posedge rclk or negedge rrstn) begin
if(~rrstn) begin
wptr_buff <= 'd0;
wptr_syn <= 'd0;
end
else begin
wptr_buff <= wptr;
wptr_syn <= wptr_buff;
end
end
/**********************full empty gen*************************/
assign wfull = (wptr == {~rptr_syn[ADDR_WIDTH:ADDR_WIDTH-1],rptr_syn[ADDR_WIDTH-2:0]});
assign rempty = (rptr == wptr_syn);
46.同步FIFO
题目
根据题目提供的双口 RAM 代码和接口描述,实现同步 FIFO,要求 FIFO 位宽和深度参数化可配置。
输入描述:
input clk ,
input rst_n ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata
输出描述:
output reg wfull ,
output reg rempty ,
output wire [WIDTH-1:0] rdata
解法
关于读写地址以及 fifo_cnt
的产生,在 FIFO 里经典的做法读写地址均做循环累加:地址指针 waddr
和 raddr
均比实际地址多一位,最高位用来指示套圈情况。当 waddr
和 raddr
的最高位相同时,fifo_cnt = waddr-raddr
;当 waddr
和 raddr
的最高位相反时,fifo_cnt = DEPTH + waddr[ADDR_WIDTH-1:0] - raddr[ADDR_WIDTH-1:0]
。这种用最高位表示套圈的思路是没问题的,但是参考答案里的做法是有问题的,他的做法是直接定义 waddr[ADDR_WIDTH:0]
然后从 0开始加,加到高位自动翻转:
always @(posedge clk or negedge rst_n) begin
if(~rst_n) begin
waddr <= 'd0;
end
else if(!wfull && winc)begin
waddr <= waddr + 1'd1;
end
end
这样的做法只适用于深度为 2^N 的 fifo,一旦深度非 2^N 那么 addr
就乱了。如果还要使用最高位标志套圈的思路,那么需要做出修改如下:
reg [DP_WD :0]waddr;
wire wenc;
wire waddr_d_h;
wire [DP_WD -1:0]waddr_d_l;
assign wenc = winc & (!wfull);
assign waddr_d_h = (waddr[DP_WD-1:0] == DEPTH-1) ? ~waddr[DP_WD] : waddr[DP_WD];
assign waddr_d_l = (waddr[DP_WD-1:0] == DEPTH-1) ? 0 : waddr[DP_WD-1:0] + 1;
always @(posedge clk or negedge rst_n)begin
if(~rst_n) waddr <= 0;
else if(wenc) waddr <= {waddr_d_h, waddr_d_l};
end
最高位彻底的作为标志位,当低位计数到 DEPTH-1
时,高位翻转。如此一来仍旧可以用经典的方式计算 fifo_cnt
:
wire [DP_WD :0]fifo_cnt = (waddr[DP_WD] == raddr[DP_WD]) ? waddr[DP_WD-1:0] - raddr[DP_WD-1:0]: (waddr[DP_WD-1:0] + DEPTH - raddr[DP_WD-1:0]);
当高位值一致时就 waddr-raddr
,当高位值不一样时就 waddr+DEPTH-raddr
,得到了目前的 fifo 内数据量,注意,此时计算的 fifo_cnt
在时序上处于 winc/rinc
的下一拍,也就是数据真正写入/读出的那一拍。
下一步空满信号,空满信号的产生原理也很简单,fifo_cnt==0
时为空,fifo_cnt==DEPTH
时为满。但是网站上给出的参考括对照波形,wfull/rempty
信号的输出都是在 fifo_cnt
的下一拍,即 winc/rinc
的延后两拍,这是有问题的,下面这段代码是按照对比波形写出来的,也就是对比 pass 的行为:
wire rempty_d = (fifo_cnt == 0);
always @(posedge clk or negedge rst_n)begin
if(~rst_n) rempty <= 0;
else rempty <= rempty_d;
end
wire wfull_d = (fifo_cnt == DEPTH);
always @(posedge clk or negedge rst_n)begin
if(~rst_n) wfull <= 0;
else wfull <= wfull_d;
end
而后ram的wenc/renc的产生与wfull/rempty相关:
assign wenc = winc & (!wfull);
assign renc = rinc & (!rempty);
wenc/renc
的产生思路没有什么,当满时不再写入ram空时不再读取ram这个行为是合理的,当然fifo内部不看wfull/rempty
也没有什么问题,毕竟 fifo 把wfull/rempty
给到外面就是为了让控制器做处理的:wfull
置起后,winc
不能为高,否则写行为不可控可能数据覆盖;rempty
置起后,rinc
不能为高,否则读行为不可控读数据不准。
那么话题回到wfull/rempty
,根据上面的分析,wfull/rempty
这两个信号的反馈必须在winc/rinc
的下一拍得到,否则控制器无法及时的调整winc/rinc
逻辑。举个例子,当前深度16的FIFO内已有15个数,cyc0
winc
起请求写下一个数,cyc1
数据写入,cys2
wfull
信号起。那么在cyc1
winc
仍然可以置起写数(因为没看到wfull
),哪怕这个时候在 fifo 内做了ram保护 wenc = winc & (!wfull)
也没有用,因此cyc1
在内部也没有看到wfull
信号。
用上面那个通过了网站测评的代码做以下测试,向深度 16 的 fifo 里写了 18 个数,然后读数,可以看到第一个数据已经被覆盖:
同时后续的 wfull/rempty
信号也乱了,因为 fifo_cnt
连带着都跳乱了。
因此 wfull/rempty
的产生必须在 winc/rinc
的下一拍:
wire rempty = (fifo_cnt == 0);
wire wfull = (fifo_cnt == DEPTH);
当然了,这样也会导致 fifo 的 winc/rinc
时序变差(要看wfull/rempty
),因此可以考虑winc/rinc
当拍产生wfull_d/rempty_d
,然后打拍得到wfull/rempty
,代价是wfull_d/rempty_d
的产生逻辑比较深,而且做起来稍微复杂了一点点(其实不复杂,就是要做一些选择信号啥的,我懒得做了)看取舍吧。我就用上面那种了:
这才是一个合理的fifo波形。
/**********************************SFIFO************************************/
module sfifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input clk ,
input rst_n ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata ,
output reg wfull ,
output reg rempty ,
output wire [WIDTH-1:0] rdata
);
localparam DP_WD = $clog2(DEPTH);
reg [DP_WD :0]waddr;
wire wenc;
wire waddr_d_h;
wire [DP_WD -1:0]waddr_d_l;
assign wenc = winc & (!wfull);
assign waddr_d_h = (waddr[DP_WD-1:0] == DEPTH-1) ? ~waddr[DP_WD] : waddr[DP_WD];
assign waddr_d_l = (waddr[DP_WD-1:0] == DEPTH-1) ? 0 : waddr[DP_WD-1:0] + 1;
always @(posedge clk or negedge rst_n)begin
if(~rst_n) waddr <= 0;
else if(wenc) waddr <= {waddr_d_h, waddr_d_l};
end
reg [DP_WD :0]raddr;
wire renc;
wire raddr_d_h;
wire [DP_WD -1:0]raddr_d_l;
assign renc = rinc & (!rempty);
assign raddr_d_h = (raddr[DP_WD-1:0] == DEPTH-1) ? ~raddr[DP_WD] : raddr[DP_WD];
assign raddr_d_l = (raddr[DP_WD-1:0] == DEPTH-1) ? 0 : raddr[DP_WD-1:0] + 1;
always @(posedge clk or negedge rst_n)begin
if(~rst_n) raddr <= 0;
else if(renc) raddr <= {raddr_d_h, raddr_d_l};
end
wire [DP_WD :0]fifo_cnt = (waddr[DP_WD] == raddr[DP_WD]) ? waddr[DP_WD-1:0] - raddr[DP_WD-1:0]:
(waddr[DP_WD-1:0] + DEPTH - raddr[DP_WD-1:0]);
wire rempty_d = (fifo_cnt == 0);
always @(posedge clk or negedge rst_n)begin
if(~rst_n) rempty <= 0;
else rempty <= rempty_d;
end
wire wfull_d = (fifo_cnt == DEPTH);
always @(posedge clk or negedge rst_n)begin
if(~rst_n) wfull <= 0;
else wfull <= wfull_d;
end
dual_port_RAM #(.DEPTH(DEPTH), .WIDTH(WIDTH))
u_ram (
.wclk (clk),
.wenc (wenc),
.waddr (waddr),
.wdata (wdata),
.rclk (clk),
.renc (renc),
.raddr (raddr),
.rdata (rdata)
);
endmodule
47.格雷码计数器
题目
实现 4 bit 位宽的格雷码计数器。
解法
input clk,
input rst_n,
output reg [3:0] gray_out
reg[4:0] count;
reg rev;
always@(posedge clk or negedge rst_n)
if(!rst_n)
count <= 5'b0;
else
count <= count + 1'b1;
always@(*)
if(!rst_n)
gray_out = 4'b0;
else
gray_out = count[4:1] ^ (count[4:1] >> 1);
48.多 bit MUX 同步器
题目
在data_en
为高期间,data_in
将保持不变,data_en
为高至少保持 3 个 B 时钟周期。表明,当data_en
为高时,可将数据进行同步。
本题中data_in
端数据变化频率很低,相邻两个数据间的变化,至少间隔 10 个 B 时钟周期。
端口 | I/O | 描述 |
---|---|---|
clk_a | input | A时钟域时钟 |
clk_b | input | B时钟域时钟 |
arstn | input | A时钟域异步复位 |
brstn | input | B时钟域异步复位 |
data_in | input | A时钟数据输入 |
data_en | input | A时钟数据有效信号,高电平有效 |
data_out | output | B时钟域数据输出 |
解法
input clk_a ,
input clk_b ,
input arstn ,
input brstn ,
input [3:0] data_in ,
input data_en ,
output reg [3:0] dataout
输入数据暂存data_reg
中,使能信号data_en
用打两拍的方式跨时钟域传输到时钟域B,最后data_out
根据使能信号更新数据。
data_en
信号在A时钟域用一个D触发器暂存,然后打两拍传输到B时钟域。
根据同步到B时钟域的使能信号data_en_b1
,更新输出。
reg [3:0] data_reg;
reg data_en_a, data_en_b0, data_en_b1;
always@(posedge clk_a or negedge arstn) begin
if(~arstn)
data_reg <= 0;
else
data_reg <= data_in;
end
always@(posedge clk_a or negedge arstn) begin
if(~arstn)
data_en_a <= 0;
else
data_en_a <= data_en;
end
always@(posedge clk_b or negedge brstn) begin
if(~brstn) begin
data_en_b0 <= 0;
data_en_b1 <= 0;
end
else begin
data_en_b0 <= data_en_a;
data_en_b1 <= data_en_b0;
end
end
always@(posedge clk_b or negedge brstn) begin
if(~brstn)
dataout <= 0;
else
dataout <= data_en_b1? data_reg: dataout;
end
49.脉冲同步电路
题目
从 A 时钟域提取一个单时钟周期宽度脉冲,然后在新的时钟域 B 建立另一个单时钟宽度的脉冲。
A 时钟域的频率是 B 时钟域的 10 倍;A时钟域脉冲之间的间隔很大,无需考虑脉冲间隔太小的问题。
电路的接口如下图所示。data_in
是脉冲输入信号,data_out
是新的脉冲信号;clk_fast
是 A 时钟域时钟信号,clk_slow
是 B 时钟域时钟信号;rst_n
是异步复位信号。
解法
input clk_fast ,
input clk_slow ,
input rst_n ,
input data_in ,
output dataout
总体思路是将 A 时钟域的脉冲信号转换为电平信号,打两拍后再转换为 B 时钟域的脉冲信号。
reg data_level, data_level1, data_level2, data_level3;
assign dataout = data_level3^data_level2;
// 脉冲信号转电平信号
always@(posedge clk_fast or negedge rst_n) begin
if(~rst_n)
data_level <= 0;
else
data_level <= data_in? ~data_level: data_level;
end
// 电平信号打两拍再转为脉冲信号
always@(posedge clk_slow or negedge rst_n) begin
if(~rst_n) begin
data_level1 <= 0;
data_level2 <= 0;
data_level3 <= 0;
end
else begin
data_level1 <= data_level;
data_level2 <= data_level1;
data_level3 <= data_level2;
end
end
50. 简易秒表
题目
请编写一个模块,实现简易秒表的功能:具有两个输出,当输出端口 second
从 1-60 循环计数,每当second
计数到 60,输出端口minute
加一,一直到minute=60
,暂停计数。
输入描述:
clk
:系统时钟信号
rst_n
:异步复位信号,低电平有效
输出描述:
second
:6比特位宽,秒表的秒读数
minute
:6比特位宽,秒表的分读数
解法
input clk,
input rst_n,
output reg [5:0]second,
output reg [5:0]minute
首先确定 second
的取值逻辑:当 minute=60
时停止计数,即保持 second
为0;当 second=60
时,下一个周期 second
置为1。其余情况 second
等于前一时刻的值加一。
然后明确 minute
的取值逻辑:当 second=60
,minute
等于前一时刻的值加一。其余情况,minute
保持不变。
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
minute <= 6'd0;
end
else if (second == 6'd60)
begin
minute <= minute+1;
end
else
begin
minute <= minute;
end
always @(posedge clk or negedge rst_n)
if (!rst_n)
begin
second <= 6'd0;
end
else if(second == 6'd60)
begin
second <= 6'd1;
end
else if (minute == 60)
second <= 0;
else
second <= second+1'd1;
51.可置位计数器
题目
请编写一个十六进制计数器模块,计数器输出信号递增每次到达 0,给出指示信号zero
,当置位信号 set
有效时,将当前输出置为输入的数值 set_num
。
输入描述:
clk
:时钟信号
rst_n
:复位信号,低电平有效
set
:置位指示信号,当该信号有效时,表示将输出信号强制置为 set_num
set_num
:4比特信号,当set信号有效时,将该信号的数字赋予输出信号 number
输出描述:
zero
:过零指示信号,当 number 计数到0时,该信号为1,其余时刻为0
number
:4比特位宽,表示计数器的当前读数
解法
input clk,
input rst_n,
input set,
input [3:0] set_num,
output reg [3:0]number,
output reg zero
多了一个num
中间计数器,使时序后移了一个周期。这里也加入了一个中间计数器。
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
num <= 0;
else
num <= set? set_num: num+1;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
number <= 0;
else
number <= num;
end
always@(*) begin
if(~rst_n)
zero <= 0;
else
zero <= number==0;
end
52.加减计数器
题目
请编写一个十进制计数器模块,当 mode 信号为 1,计数器输出信号递增,当 mode 信号为 0,计数器输出信号递减。每次到达 0,给出指示信号 zero。
输入描述:
clk
:系统时钟信号
rst_n
:复位信号,低电平有效
mode
:模式选择信号,当该信号为 1,计数器每个时钟加一;为 0,则每个时钟减一。
输出描述:
number
:4 比特位宽,计数器当前输出读数。
zero
:过零指示信号,当 numbe r为 0 时,该信号为 1,其他时刻为0.
解法
input clk,
input rst_n,
input mode,
output reg [3:0]number,
output reg zero
根据波形要求,要添加一个中间计数器num
。
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
num <= 0;
else if(mode)
num <= num==9? 0: num+1;
else
num <= num==0? 9: num-1;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
number <= 0;
else
number <= num;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
zero <= 0;
else
zero <= num==0;
end
53.单端口RAM
题目
设计一个单端口 RAM ,它有: 写接口,读接口,地址接口,时钟接口和复位;存储宽度是 4 位,深度 128。
注意 rst为低电平复位
解法
input clk,
input rst,
input enb,
input [6:0]addr,
input [3:0]w_data,
//在testbench中,clk为周期5ns的时钟,rst为低电平复位
output wire [3:0]r_data
reg [3:0] myRAM [127:0];
reg [3:0] r_data_r;
// 写入RAM
genvar i;
generate
for(i=0;i<128;i=i+1)
always@(posedge clk or negedge rst) begin
if(~rst)
myRAM[i] <= 0;
else
myRAM[addr] <= enb? w_data: myRAM[addr];
end
endgenerate
// 读取RAM
always@(*) begin
if(~rst)
r_data_r <= 0;
else
r_data_r <= ~enb? myRAM[addr]: r_data_r;
end
assign r_data = r_data_r;
54. RAM的简单实现
题目
实现一个深度为 8,位宽为 4 bit 的双端口 RAM,数据全部初始化为 0000。具有两组端口,分别用于读数据和写数据,读写操作可以同时进行。当读数据指示信号read_en
有效时,通过读地址信号 read_addr
读取相应位置的数据 read_data
,并输出;当写数据指示信号 write_en
有效时,通过写地址信号 write_addr
和写数据 write-data
,向对应位置写入相应的数据。
输入描述:
clk
:系统时钟信号
rst_n
:异步复位信号,低电平有效
read_en
,write_en
:单比特信号,读/写使能信号,表示进行读/写操作
read_addr
,write_addr
:8比特位宽的信号,表示读/写操作对应的地址
write_data
:4比特位宽的信号,在执行写操作时写入RAM的数据
输出描述:
read_data
:4比特位宽的信号,在执行读操作时从RAM中读出的数据
解法
input clk,
input rst_n,
input write_en,
input [7:0]write_addr,
input [3:0]write_data,
input read_en,
input [7:0]read_addr,
output reg [3:0]read_data
//prameter
parameter WIDTH = 4;
parameter DEPTH = 8;
//defination
reg [WIDTH - 1 : 0] RAM [0 : DEPTH - 1];
//output
integer i;
always@(posedge clk or negedge rst_n)begin
if(!rst_n) begin
for(i = 0; i < DEPTH; i = i + 1) begin
RAM[i] <= 'd0;
end
end
else if(write_en) RAM[write_addr] <= write_data;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n) read_data <= 'd0;
else if(read_en) read_data <= RAM[read_addr];
else read_data <= 'd0;
end
55. Johnson Counter
题目
请用 Verilog 实现 4 位约翰逊计数器(扭环形计数器),计数器的循环状态如下。
解法
input clk ,
input rst_n,
output reg [3:0] Q
always@(posedge clk or negedge rst_n)begin
if(!rst_n) Q <= 'd0;
else Q <= {~Q[0], Q[3 : 1]};
end
56.流水线乘法器
题目
实现 4 bit 无符号数流水线乘法器设计。
解法
input clk ,
input rst_n ,
input [size-1:0] mul_a ,
input [size-1:0] mul_b ,
output reg [size*2-1:0] mul_out
//parameter
parameter N = size * 2;
//defination
wire [N - 1 : 0] temp [0 : 3];
reg [N - 1 : 0] adder_0;
reg [N - 1 : 0] adder_1;
//output
genvar i;
generate
for(i = 0; i < 4; i = i + 1)begin : loop
assign temp[i] = mul_b[i] ? mul_a << i : 'd0;
end
endgenerate
always@(posedge clk or negedge rst_n)begin
if(!rst_n) adder_0 <= 'd0;
else adder_0 <= temp[0] + temp[1];
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n) adder_1 <= 'd0;
else adder_1 <= temp[2] + temp[3];
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n) mul_out <= 'd0;
else mul_out <= adder_0 + adder_1;
end
57.交通灯
题目
要求实现一个交通红绿灯,具有红黄绿三个小指示灯和一个行人按钮,正常情况下,机动车道指示灯按照60时钟周期绿灯,5个时钟周期黄灯,10个时钟周期红灯循环。当行人按钮按下,如果剩余绿灯时间大于10个时钟,则缩短为10个时钟,小于10个时钟则保持不变。
注:机动车道的指示灯和人行道指示灯应该是配对的,当机动车道的灯为绿或者黄时,人行道的灯为红;当机动车道的灯为红时,人行道的灯为绿,为简便起见,只考虑机动车道的指示灯。
输入描述:
clk
:系统时钟信号
rst_n
:复位信号,低电平有效
pass_request
:行人按钮信号,当该信号为 1,表示按钮按下,如果剩余绿灯时间大于 10 个时钟,则缩短为 10 个时钟,小于 10 个时钟则保持不变。
输出描述:
clock
:交通灯倒计时读数
red
:该信号为1,表示红灯亮,为0表示红灯不亮
yellow
:该信号为1,表示黄灯亮,为0表示黄灯不亮
green
:该信号为1,表示黄灯亮,为0表示黄灯不亮
解法
该题为交通灯切换,但题目描述错误,该题的实际信号流程为红灯 10 个周期=>黄灯 5 个周期 => 绿灯 60 周期,题解需要再 rst 状态等待两个时钟周期再跳转到第一状态,故需要在复位后等待两个时钟再进入工作状态,且等待过程中时钟计数不停止
input rst_n, //异位复位信号,低电平有效
input clk, //时钟信号
input pass_request,
output wire[7:0]clock,
output reg red,
output reg yellow,
output reg green
parameter RST=3'd0,GREEN=3'd1,YELLOW=3'd2,RED=3'd3;
reg [1:0] state,next_state;
reg [7:0] clock_1;
always @(*)
case(state)
RST :next_state = clock_1==8'd8?RED : RST;
GREEN :next_state = clock_1==8'd1?RED : GREEN;
YELLOW :next_state = clock_1==8'd1? GREEN:YELLOW;
RED :next_state = clock_1==8'd1?YELLOW: RED;
default:next_state = RST;
endcase
always @(posedge clk or negedge rst_n)
if(!rst_n)
state <= RST;
else
state <= next_state;
always @(posedge clk or negedge rst_n)
if(!rst_n)
clock_1 <= 8'd10;
else begin
if(clock_1==8'd1)
case(state)
GREEN,RST:clock_1 <= 8'd10;
YELLOW :clock_1 <= 8'd60;
RED :clock_1 <= 8'd5 ;
default :clock_1 <= 8'd10;
endcase
else if(clock_1==8'd8&&state==RST)
clock_1 <= 8'd10;
else if(pass_request&&clock_1>8'd10&&state==GREEN)
clock_1 <= 8'd10;
else
clock_1 <= clock_1 - 1'b1;
end
always @(posedge clk or negedge rst_n)
if(!rst_n)begin
red <= 1'd0;
yellow <= 1'd0;
green <= 1'd0;
end else begin
case(next_state)
RST :begin
red <= 1'd0;
yellow <= 1'd0;
green <= 1'd0;
end
GREEN :begin
red <= 1'd0;
yellow <= 1'd0;
green <= 1'd1;
end
YELLOW :begin
red <= 1'd0;
yellow <= 1'd1;
green <= 1'd0;
end
RED :begin
red <= 1'd1;
yellow <= 1'd0;
green <= 1'd0;
end
default:begin
red <= 1'd0;
yellow <= 1'd0;
green <= 1'd0;
end
endcase
end
assign clock = clock_1;
58.游戏机计费程序
题目
要求实现一个游戏机计费模块,某游戏机具有多个模式,价格不同:普通模式每分钟 1 元,畅玩模式每分钟收费 2 元,默认情况下为普通模式,在 boost
按键按下之后进入畅玩模式。
游戏机采用预付费模式,输入端口 money
的数值为预付费用,在 set
信号有效时,将 money
的数值读入。输出端口 remain
的数值为剩余费用,当费用小于 10元时,黄色信号灯 yellow
亮起。当费用不足时,红色信号灯 red
亮起,同时关闭电脑。在游戏过程中可以通过 set
端口续费。每次 set
信号有效将此时刻 money
的数值加到 remain
之中。
注:在程序中以每个时钟周期代表一分钟,每个单位大小表示1元。
输入描述:
clk
:系统时钟信号
rst_n
:复位信号,低电平有效
money
:10bit位宽的数据,表示充值数额,当set信号有效时,将该信号的数值加到游戏余额remain中
set
:充值信号,当信号等于1,表示用户充值。
boost
:游戏机模式切换信号,为 1 时,表示进入畅玩模式,每个时钟扣费 2,即 remain 减二,为 0 时,表示普通模式,remain 每个时钟减一。
输出描述:
remain
:10 bit 位宽的数据,表示余额,根据充值数额和游戏模式变化
yellow
:指示灯,当 remain 大于 0 且小于 10 时,为1。
red
:指示灯,当余额不足时为 1,其余时刻为 0。
解法
input rst_n, //异位复位信号,低电平有效
input clk, //时钟信号
input [9:0]money,
input set,
input boost,
output reg[9:0]remain,
output reg yellow,
output reg red
set==1
时,进行投币,money
会添加到余额remain
上。boost==0
是普通模式,每个时钟周期消耗1元;boost==0
是畅玩模式,每个时钟周期消耗2元。当余额不足时停止计费(余额小于对应模式的要求)。
指示灯
黄灯yellow
在余额小于10元时亮起,余额归零时灭掉;红灯red
在余额不足时亮起。
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
yellow <= 0;
red <= 0;
end
else begin
yellow <= remain<10&&remain;
red <= boost? remain<2: remain<1;
end
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
remain <= 0;
else if(boost)
remain <= set ? remain+money:
remain<2? remain:
remain-2;
else
remain <= set ? remain+money:
remain<1? remain:
remain-1;
end
标签:clk,31,牛客,rst,input,网手,data,reg,时钟
From: https://www.cnblogs.com/accumulagain/p/17226849.html