习题4
2.请使用本章介绍的有限状态机设计方法设计同步FIFO和异步FIFO。
解:
异步FIFO(转牛客网答案):
`timescale 1ns/1ns
/***************************************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
/***************************************AFIFO*****************************************/
module asyn_fifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
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);
/**********************RAM*************************/
wire wen ;
wire ren ;
wire wren;//high write
wire [ADDR_WIDTH-1:0] waddr;
wire [ADDR_WIDTH-1:0] raddr;
assign wen = winc & !wfull;
assign ren = rinc & !rempty;
assign waddr = waddr_bin[ADDR_WIDTH-1:0];
assign raddr = raddr_bin[ADDR_WIDTH-1:0];
dual_port_RAM #(.DEPTH(DEPTH),
.WIDTH(WIDTH)
)dual_port_RAM(
.wclk (wclk),
.wenc (wen),
.waddr(waddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。
.wdata(wdata), //数据写入
.rclk (rclk),
.renc (ren),
.raddr(raddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。
.rdata(rdata) //数据输出
);
endmodule
同步FIFO(转牛客网答案):
`timescale 1ns/1ns
/**********************************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
/**********************************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
);
reg [$clog2(DEPTH)-1:0] waddr, raddr;
reg [$clog2(DEPTH) :0] cnt;
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
waddr <= 0;
else
waddr <= winc&~wfull? waddr+1: waddr;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
raddr <= 0;
else
raddr <= rinc&~rempty? raddr+1:raddr;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
cnt <= 0;
else if(rinc&~rempty&winc&~wfull)
cnt <= cnt;
else if(winc&~wfull)
cnt <= cnt + 1;
else if(rinc&~rempty)
cnt <= cnt - 1;
else
cnt <= cnt;
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
wfull = 0;
rempty = 0;
end
else begin
wfull = cnt == DEPTH;
rempty = cnt == 0;
end
end
dual_port_RAM #(
.DEPTH(DEPTH ),
.WIDTH(WIDTH )
)
myRAM(
.wclk (clk ),
.wenc (winc&~wfull ),
.waddr(waddr ),
.wdata(wdata ),
.rclk (clk ),
.renc (rinc&~rempty),
.raddr(raddr ),
.rdata(rdata )
);
endmodule
5.实验板共有4个LED灯,请显示它们的组合显示。具体功能如下:
(1)模式1:先点亮奇数位LED灯,即1、3后点亮偶数位LED灯,即2、4,依次循环。
(2)模式2:按照1、2、3、4的顺序依次点亮所有LED灯,然后再按该顺序依次熄灭所有的LED灯。
(3)模式3:每次只点亮一个LED灯,亮灯顺序为1、2、3、4、3、2,按照该顺序循环。
(4)模式4:按照1/4、2/3的顺序依次点亮所有灯,每次同时点亮两个LED灯;然后再按该顺序依次熄灭所有LED灯,每次同时熄灭两个LED灯。
注:点亮与熄灭的时间间隔均为0.25s。在演示过程中,只有当一种模式演示完毕才能转向其他演示模式。
思路:用LED[0]、LED[1]、LED[2]、LED[3]分别对应第1个LED灯,第2个LED灯,第3个LED灯和第4个LED灯,4个模式对应四种状态。
状态1:LED先是0101后是1010.
状态2:LED从0001到0011到0111到1111到1110到1100到1000到0000.
状态3:LED从0001到0010到0100到1000到0100到0010.
状态4:LED从1001到1111到0110到0000.
`timescale 1ms/1ms
module LED(
input clk,
input [2:0] shift,//shift用于切换模式
output reg[3:0] led
);
parameter mode_0=0,mode_1 = 1,mode_2 = 2,mode_3 = 3,mode_4 = 4;
reg [2:0]state,next_state;
always@(posedge clk) begin
state <=next_state;
end
always@(state,shift) begin
led=4'b0000;//初始状态下灯都是熄灭的
case(shift)
1:next_state<=mode_1;
2:next_state<=mode_2;
3:next_state<=mode_3;
4:next_state<=mode_4;
default:next_state<=mode_0;
endcase
case(state)
1: begin
led<=4'b0101;
#250 led=~led;
#250 case(shift)
2:next_state<=mode_2;
3:next_state<=mode_3;
4:next_state<=mode_4;
default next_state<=0;
endcase
end
2: begin
led<=4'b0001;
#250 led<=4'b0011;
#250 led<=4'b0111;
#250 led<=4'b1111;
#250 led<=4'b1110;
#250 led<=4'b1100;
#250 led<=4'b1000;
#250 led<=4'b0000;
#250 case(shift)
1:next_state<=mode_1;
3:next_state<=mode_3;
4:next_state<=mode_4;
default next_state<=0;
endcase
end
3:begin
led<=4'b0001;
#250 led<=4'b0010;
#250 led<=4'b0100;
#250 led<=4'b1000;
#250 led<=4'b0100;
#250 led<=4'b0010;
#250 case(shift)
1:next_state<=mode_1;
2:next_state<=mode_2;
4:next_state<=mode_4;
default next_state<=0;
endcase
end
4:begin
led<=4'b1001;
#250 led<=4'b1111;
#250 led<=4'b0110;
#250 led<=4'b0000;
#250 case(shift)
1:next_state<=mode_1;
2:next_state<=mode_2;
3:next_state<=mode_3;
default next_state<=0;
endcase
end
endcase
end
endmodule
测试:模式2演示两次,然后模式1演示两次,然后模式4演示四次,模式3演示三次,对应tb文件代码如下:
`timescale 1ms/1ms
module tb_LED;
reg clk;
reg [2:0]shift;
wire [3:0] led;
parameter mode_0=0,mode_1 = 1,mode_2 = 2,mode_3 = 3,mode_4 = 4;
//模式1用时500,模式2用时2000,模式3用时1500,模式4用时1000
LED u(
.clk(clk),
.shift(shift),
.led(led)
);
always #5 clk =~clk;
initial begin
clk = 0;
shift = mode_0; #1000//静默1秒
shift <= mode_2; #4000 //演示模式2两次
shift <= mode_1;#1000 //演示模式1 两次
shift <= mode_4;#4000 //演示模式4 四次
shift <= mode_3;#4500 //演示模式3 三次
shift <= mode_0; //回归初始状态
end
endmodule
运行15000ms,其中LED的显示用十六进制数表示。仿真波形如下:
6.设计一个汽车尾灯控制器。汽车尾部左右两侧各有两只尾灯,用作汽车行驶状态的方向指示标志。要求:
(1)汽车正常向前行驶时,4只尾灯全部熄灭。
(2)当汽车要向左或向右转弯时,相应侧的两只尾灯从左向右依次闪烁。每个灯亮1s,每个周期为2s,另一侧的两只灯不亮。
(3)紧急刹车时,4只尾灯全部闪,闪动频率为1HZ。
思路:用taillights[0]表示右边靠右的尾灯,用taillights[1]表示右边靠左的尾灯,用taillights[2]表示左边靠右的尾灯,用taillights[3]表示左边靠左的尾灯。正常向前行驶为模式1,记为keep,向左转弯为模式2,记为left_turn,向右转弯为模式3,记为right_turn,紧急刹车为模式4,记为emegence。
模式1:taillights为0000;
模式2:taillights为1000转0100;
模式3:taillights为0010转0001;
模式4:taillights为1111转0000;
以1ms为时间单位,每隔1000个时间单位变1次。
本题有一个需要斟酌的点在于,当一个模式正在执行的时候,能否在无任何时差的情况下切换为其他模式?考虑到第五题的要求:只有当一种模式演示完毕才能转向其他演示模式。合理理解为只有当一种模式执行完毕才能切换其他模式。逻辑综合文件代码如下:
`timescale 1ms/1ms
module taillights(
input clk,
input reg[1:0] shift,//用于选择模式
output reg[3:0] taillights
);
parameter keep = 0,left_turn =1,right_turn =2,emegence =3;
reg [1:0] state,next_state;
always @(posedge clk) begin
state <=next_state;
end
always @(state,shift) begin
taillights <=4'b0000; //初始状态即正常行驶状态尾灯不亮
case(shift)
left_turn:next_state<=left_turn;
right_turn:next_state<=right_turn;
emegence:next_state<=emegence;
default: next_state<=keep;
endcase //用shift控制下一状态
case(state)
left_turn:begin
taillights <=4'b1000;#1000
taillights <=4'b0100;#1000
case(shift)
right_turn:next_state<=right_turn;
emegence:next_state<=emegence;
default next_state<=keep;
endcase //用shift控制下一状态
end
right_turn:begin
taillights <=4'b0010;#1000
taillights <=4'b0001;#1000
case(shift)
left_turn:next_state<=left_turn;
emegence:next_state<=emegence;
default next_state<=keep;
endcase //用shift控制下一状态
end
emegence:begin
taillights <=4'b1111;#500
taillights <=4'b0000;#500
case(shift)
left_turn:next_state<=left_turn;
right_turn:next_state<=right_turn;
default next_state<=keep;
endcase //用shift控制下一状态
end
default: taillights <=4'b0000; //消锁存器
endcase //根据现态选择输出
end
endmodule
测试:直行2秒后左转弯6秒,然后再直行2秒,右转弯4秒,紧急刹车3秒后恢复直行。
对应tb文件代码如下:
`timescale 1ms/1ms
module tb_taillights;
reg clk;
reg [1:0] shift;
wire [3:0] taillights;
parameter keep = 0,left_turn =1,right_turn =2,emegence =3;
taillights u(
.clk(clk),
.shift(shift),
.taillights(taillights)
);
always #5 clk = ~clk;
initial begin
clk <= 0;
shift <= keep; #2000
shift <= left_turn; #6000
shift <= keep; #2000
shift <= right_turn; #4000
shift <= emegence; #3000
shift <= keep;
end
endmodule
运行18000ms后的波形如下: