清华大学出版社 Verilog数字系统与FPGA应用——参考答案(习题4)待更

习题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后的波形如下:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值