硬件语言Verilog HDL牛客刷题day06 跨时钟域传输部分

1.VL45 异步FIFO

1.题目:

请根据题目中给出的双口RAM代码和接口描述,实现异步FIFO,要求FIFO位宽和深度参数化可配置。电路的接口如下图所示。


2.解题思路

        2.1 格雷码 的4 位表格

         2.2 格雷码的 得到的公式

        

 

         2.3 没搞的太懂。

一个链接:FIFO设计-异步FIFO篇 - 知乎 (zhihu.com)

                自己的理解:

                a.首先是计数到 格雷码的转换。

                b.然后是为什么要使用格雷码,为了异步时钟 要延时 2个周期, 因为 这里是 4 的地址存储。


3.解题代码

`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
);
    
    reg[$clog2(DEPTH):0] waddr, raddr;
    reg[$clog2(DEPTH):0] gray_waddr, gray_raddr;
    
/***************二进制地址自增,格雷码转换*****************************************/
    always@(posedge wclk or negedge wrstn)begin
        if(~wrstn)begin
            waddr <= 0;
            gray_waddr <= 0;
        end
        else begin 
            waddr <= wenc? waddr+1: waddr;
            gray_waddr <= (waddr>>1) ^ waddr;     
        end
    end
    
    always@(posedge rclk or negedge rrstn)begin
        if(~rrstn) begin
            raddr <= 0;
            gray_raddr <= 0;
        end
        else begin
            raddr <= renc? raddr+1: raddr;
            gray_raddr <= (raddr>>1) ^ raddr;      
        end
    end
    
/******************将格雷码地址打两拍同步时钟域*****************************************/
    wire wenc, renc;
    reg[$clog2(DEPTH):0] gray_waddr1, gray_waddr2;
    always@(posedge rclk or negedge rrstn)begin
        if(~rrstn)begin
            gray_waddr1 <= 0;
            gray_waddr2 <= 0;
        end
        else begin
            gray_waddr1 <= gray_waddr;
            gray_waddr2 <= gray_waddr1;
        end
    end
    assign rempty = gray_raddr==gray_waddr2? 1: 0;  //读空信号
    assign renc = rinc && (!rempty);  //读使能
    
    reg[$clog2(DEPTH):0] gray_raddr1, gray_raddr2;
    always@(posedge wclk or negedge wrstn)begin
        if(~wrstn)begin
            gray_raddr1 <= 0;
            gray_raddr2 <= 0;
        end
        else begin
            gray_raddr1 <= gray_raddr;
            gray_raddr2 <= gray_raddr1;
        end
    end
    assign wfull = gray_waddr==(gray_raddr2+DEPTH+(DEPTH>>1))? 1: 0;  //写满信号
    assign wenc = winc && (!wfull);  //写使能
    
    
    dual_port_RAM #(DEPTH, WIDTH) dut(
        .wclk(wclk)
        ,.wenc(wenc)
        ,.waddr(waddr[$clog2(DEPTH)-1 : 0])  //深度对2取对数,得到地址的位宽。
        ,.wdata(wdata)      	//数据写入
        ,.rclk(rclk)
        ,.renc(renc)
        ,.raddr(raddr[$clog2(DEPTH)-1 : 0])  //深度对2取对数,得到地址的位宽。
        ,.rdata(rdata));
    
endmodule


2.VL46 同步FIFO

1.题目:

根据题目提供的双口RAM代码和接口描述,实现同步FIFO,要求FIFO位宽和深度参数化可配置。电路的接口如下图所示。

 

 


2.解题思路

        2.1 这个比上一个好理解。主要是是在同步时钟周期里面,我们可以很好的比较数据。

        2.2  再开计数器的时候注意 开大一点,不然不好比较 写满不好判断。

        


3.解题代码

`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
);

//同步FIFO  是可以直接根据同步时序来对比
//首先计数, 分别计数, 写计数, 读计数,
//创建一个寄存器
reg[$clog2(DEPTH):0] waddr; //写的地址寄存器
reg[$clog2(DEPTH):0] raddr; //读的地址寄存器

//防止数据覆盖我们需要 判读写的时候  是否  寄存器是满的。
wire wenc; //可以持续赋值
assign wenc = winc && !wfull; //使能的时候 寄存器是有位置的
//防止读的时候没有数据可以读取,我们需要判读 是否寄存器是空的。
wire renc; //可以持续赋值
assign renc = rinc && !rempty; //使能的时候,寄存器是有数据的。



always@(posedge clk or negedge rst_n)
	begin
		if(~rst_n)
			begin
				waddr <=0;
			end
		else
			begin
				if(wenc ==1'b1)
					begin
						waddr <= waddr +1;
					end
				
			end
	end

always@(posedge clk or negedge rst_n)
	begin
		if(~rst_n)
			begin
				raddr <=0;
			end
		else
			begin
				if(renc == 1'b1)
					begin
						raddr <= raddr +1;
					end
			end
	end




always@(posedge clk or negedge rst_n)
	begin
		if(~rst_n)
			begin
				wfull  <=0;
				rempty <=0;
			end
		else
			begin
				wfull <= (waddr == raddr + DEPTH )? 1:0;
				rempty <= (waddr == raddr) ? 1 :0;
			end
	end

dual_port_RAM #(.DEPTH (DEPTH),
				.WIDTH (WIDTH))
dual_port_RAM(
	.wclk (clk),
	.wenc (wenc),
	.waddr (waddr),
	.wdata (wdata),
	.rclk (clk),
	.renc (renc),
	.raddr (raddr),
	.rdata (rdata)
);




endmodule

        



3.VL 47 格雷码计数器

1.题目:

实现4bit位宽的格雷码计数器。 

电路的接口如下图所示。

 


2. 解题思路

        2.1 了解 格雷码的生成 和推算公式。

        2.2  测试了一下,两个周期计数一下。 这个要注意。


3.解题代码

`timescale 1ns/1ns

module gray_counter(
   input   clk,
   input   rst_n,

   output  reg [3:0] gray_out
);
reg[4:0] cnt;
always@(posedge clk or negedge rst_n)
    begin
        if(~rst_n)
            begin
                cnt <=0;
            end
        else
            begin
                cnt <= cnt+1;
            end
    end
always@(posedge clk or negedge rst_n or cnt)
    begin
        if(~rst_n)
            begin
                gray_out = 4'd0;
            end
        else
            begin
                gray_out = cnt[4:1] ^ ((cnt[4:1]) >>1);
            end
    end


endmodule


4.VL48 多bit MUX同步器

1.题目:

在data_en为高期间,data_in将保持不变,data_en为高至少保持3个B时钟周期。表明,当data_en为高时,可将数据进行同步。

本题中data_in端数据变化频率很低,相邻两个数据间的变化,至少间隔10个B时钟周期。

电路的接口如下图所示。端口说明如下表所示。

 


2.解题思路

 2.1多bit MUX同步器 是什么?

        MUX/DMUX同步器主要是用于带有数据有效标志信号的多比特数据跨时钟域问题,且多比特数据要保持一段时间 

//我不是很详细的知道 MUX 同步器的 意义,

//具体是: 有快慢的时钟 需要我们在不同的时钟 中转换数据。

//具体的 快慢时钟的差有多少,

//题目说  data_in 有效信号为  3 个 B 的时钟周期,

// 可以猜到  一般  data_en 为 1 的时候输出  data_in

// 所以我们的  clk_a  =>  clk_b , 所以我们认为是 3 周期

// 但是 一般的数据输出是在 输入的下一个时钟周期。 所以数据需要延时


3.解题代码

`timescale 1ns/1ns

module mux(
	input 				clk_a	, 
	input 				clk_b	,   
	input 				arstn	,
	input				brstn   ,
	input		[3:0]	data_in	,
	input               data_en ,

	output reg  [3:0] 	dataout
);
//我不是很详细的知道 MUX 同步器的 意义, 
//具体是: 有快慢的时钟 需要我们在不同的时钟 中转换数据。
//具体的 快慢时钟的差有多少, 
//题目说  data_in 有效信号为  3 个 B 的时钟周期, 
// 可以猜到  一般  data_en 为 1 的时候输出  data_in 
// 所以我们的  clk_a  =>  clk_b , 所以我们认为是 3 周期
// 但是 一般的数据输出是在 输入的下一个时钟周期。 所以数据需要延时
//

reg[3:0]  data_in_reg; // 
reg       data_en_a;   //
reg       data_en_b1;  //
reg       data_en_b2;  // 


always@(posedge clk_a or negedge arstn)
	begin
		if(!arstn)
			begin
				data_in_reg <= 4'b0;
				data_en_a <=1'b0;
			end
		else
			begin
				data_in_reg <= data_in;//A时钟数据输入
				data_en_a <= data_en;//A时钟数据有效信号,高电平有效。
			end
	end

always@(posedge clk_b or negedge brstn)
	begin
		if(!brstn)
			begin
				data_en_b1 <=1'b0;
				data_en_b2 <=1'b0;
			end
		else 
			begin
				data_en_b1 <= data_en_a;//推迟一个周期
				data_en_b2 <= data_en_b1;//推迟一个周期
			end
	end

always@(posedge clk_b or negedge brstn)
	begin
		if(!brstn)
			begin
				dataout <= 4'b0;
			end
		else
			begin
				if(data_en_b2)//输入有效的延时3个周期的信号
					begin
						dataout <= data_in_reg;//数据输入
					end
				else
					begin
						dataout <= dataout;
					end
			end
	end



endmodule



5.VL49 脉冲同步电路

1.题目

从A时钟域提取一个单时钟周期宽度脉冲,然后在新的时钟域B建立另一个单时钟宽度的脉冲。

A时钟域的频率是B时钟域的10倍;A时钟域脉冲之间的间隔很大,无需考虑脉冲间隔太小的问题。

电路的接口如下图所示。data_in是脉冲输入信号,data_out是新的脉冲信号;clk_fast是A时钟域时钟信号,clk_slow是B时钟域时钟信号;rst_n是异步复位信号。


 

2.解题思路

        2.1 脉冲同步器的基本原理:

                将src_clk时钟域的输入脉冲转换为src_clk时钟域的电平信号src_state

      对src_state电平信号进行打拍(打两拍)同步到dst_clk时钟域

               对dst_clk时钟域的电平信号进行检测,产生dst_clk时钟域脉冲

        2.2  信号到 脉冲的过程。

        2.3  脉冲到信号的过程。


3.解题代码

`timescale 1ns/1ns

module pulse_detect(
	input 				clk_fast	, 
	input 				clk_slow	,   
	input 				rst_n		,
	input				data_in		,

	output  		 	dataout
);
    
    reg    data_in_reg;
    reg    [2:0]    data_slow_reg;
    
    always@(posedge clk_fast or negedge rst_n)
    begin
        if(!rst_n)
            data_in_reg <= 1'b0;
        else if(data_in)
            data_in_reg <= ~data_in_reg;            //只要信号来就跳转一次,即把其转换为电平信号,然后让slow时钟进行抓取
    end
    
    always@(posedge clk_slow or negedge rst_n)
    begin
        if(!rst_n)
            data_slow_reg <= 3'b0;
        else
            data_slow_reg <= {data_slow_reg[1:0] , data_in_reg};    //这也是打两拍的方式,主要就是把打拍数据放一起,
    end
    
    //打三拍主要是怕第一个数据刚同步过来不稳定,所以选择三拍,特别是我们的输出是组合逻辑,所以必须三拍,两拍同步,一拍取稳定
    assign    dataout = data_slow_reg[2] ^ data_slow_reg[1];    //把第三拍数据和第二拍数据进行求异或就能得到信号的边沿,从而实现输出信号是一个脉冲
    
endmodule

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值