我是靠谱客的博主 故意世界,最近开发中收集的这篇文章主要介绍《牛客刷verilog》Part II Verilog进阶挑战前言Part I Verilog快速入门Part II Verilog进阶挑战Part III Verilog企业真题后记,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言

  • 之前刷过HDLbits上面的题目,点击链接可以查看详细笔记:verilog练习:hdlbits网站系列完结!
  • 最近又想刷一下牛客上面的题目,可以点击链接与小编一起刷题:牛客刷题
  • 小编不才,文中如有不当之处,可以在评论中互相交流。此处题目推荐看牛客的评论区,再提一嘴,注意积累自己的基本功算法、设计模式、软件等

Part I Verilog快速入门

  • 此处内容,点击链接跳转:《牛客刷verilog》Part I Verilog快速入门

Part II Verilog进阶挑战

01 序列检测

VL25 输入序列连续的序列检测

在这里插入图片描述

答案1:状态机法

`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
    
    parameter IDLE = 4'b0000;
    parameter S0   = 4'b0001;
    parameter S1   = 4'b0011;
    parameter S2   = 4'b0010;
    parameter S3   = 4'b0110;
    parameter S4   = 4'b0111;
    parameter S5   = 4'b0101;
    parameter S6   = 4'b0100;
    parameter S7   = 4'b1100;

    reg [3:0] cur_state, next_state;
    
    always@(posedge clk or negedge rst_n)
        if (!rst_n) begin
            cur_state <= IDLE;
            next_state <= IDLE;
        end else begin
           cur_state <= next_state; 
        end
    
    always@(*)
        case(cur_state)
            IDLE   :    next_state = (a==0)?S0:IDLE;
            S0     :    next_state = (a==1)?S1:S0;
            S1     :    next_state = (a==1)?S2:S0;
            S2     :    next_state = (a==1)?S3:S0;
            S3     :    next_state = (a==0)?S4:IDLE;
            S4     :    next_state = (a==0)?S5:IDLE;
            S5     :    next_state = (a==0)?S6:IDLE;
            S6     :    next_state = (a==1)?S7:IDLE;
            S7     :    next_state = (a==0)?S0:IDLE;
            default:    next_state = IDLE;
        endcase
        
        always@(posedge clk or negedge rst_n)
        if (!rst_n)
            match <= 1'b0;
        else if (cur_state == S7)
            match <= 1'b1;
        else
            match <= 1'b0;
  
endmodule

在这里插入图片描述

  • 再说一个方法,这个方法我会称之为移位寄存器法

答案2:移位寄存器法

// `define SIZE 8 
// `define SEQ 8'b01110001
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
    
    reg [7:0] reg_8_a;
    always@(posedge clk or negedge rst_n)
        if (!rst_n)
            reg_8_a <= 'b0;
        else 
            reg_8_a <={reg_8_a[6:0],a};
    
        
    always@(posedge clk or negedge rst_n)
        if (!rst_n)begin
            match <= 1'b0;
        end
        else if (reg_8_a == 8'b01110001)
            match <= 1'b1;
        else
            match <= 1'b0;

  
endmodule
  • 为了可重用,想引入参数化设计。优化代码如下:
`define SIZE 8 
`define SEQ 8'b01110001
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
    
    reg [`SIZE - 1:0] reg_a;
    always@(posedge clk or negedge rst_n)
        if (!rst_n)
            reg_a <= 'b0;
        else 
            reg_a <={reg_a[`SIZE - 2:0],a};
    
        
    always@(posedge clk or negedge rst_n)
        if (!rst_n)begin
            match <= 1'b0;
        end
        else if (reg_a == `SEQ)
            match <= 1'b1;
        else
            match <= 1'b0;

  
endmodule

复盘

  • 题目不难,基本思想就是实现如下的状态转移图。(等熟练了,也可以不画)
    在这里插入图片描述

  • 上述的状态编码,使用了格雷码,并没有使用8421码,也没有使用独热码
    在这里插入图片描述

  • 为了可重用设计,推荐使用移位寄存器法

VL26 含有无关项的序列检测

在这里插入图片描述

答案1:状态机法

`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
    parameter IDLE = 4'b0000;
    parameter S0   = 4'b0001;
    parameter S1   = 4'b0011;
    parameter S2   = 4'b0010;
    parameter S3   = 4'b0110;
    parameter S4   = 4'b0111;
    parameter S5   = 4'b0101;
    parameter S6   = 4'b0100;
    parameter S7   = 4'b1100;
    parameter S8   = 4'b1101;

    reg [3:0] cur_state, next_state;
    
    always@(posedge clk or negedge rst_n)
        if (!rst_n) begin
            cur_state <= IDLE;
            next_state <= IDLE;
        end else begin
           cur_state <= next_state; 
        end
    
    always@(*)
        case(cur_state)
            IDLE   :    next_state = (a==0)?S0:IDLE;
            S0     :    next_state = (a==1)?S1:S0;
            S1     :    next_state = (a==1)?S2:S0;
            S2     :    next_state = S3;
            S3     :    next_state = S4;
            S4     :    next_state = S5;
            S5     :    next_state = (a==1)?S6:IDLE;
            S6     :    next_state = (a==1)?S7:IDLE;
            S7     :    next_state = (a==0)?S8:IDLE;
            S8     :    next_state = (a==0)?S0:IDLE;
            default:    next_state = IDLE;
        endcase
        
     always@(posedge clk or negedge rst_n)
        if (!rst_n)
            match <= 1'b0;
        else if (cur_state == S8)
            match <= 1'b1;
        else
            match <= 1'b0;
  
  
endmodule

在这里插入图片描述

答案2:移位寄存器法

`define SIZE 9
`define SEQ 9'011XXX110
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
    
    reg [`SIZE - 1:0] reg_a;
    always@(posedge clk or negedge rst_n)
        if (!rst_n)
            reg_a <= 'b0;
        else 
            reg_a <={reg_a[`SIZE - 2:0],a};
    
        
    always@(posedge clk or negedge rst_n)
        if (!rst_n)begin
            match <= 1'b0;
        end
    else if (reg_a[8:6] == 3'b011 && reg_a[2:0] == 3'b110)
            match <= 1'b1;
        else
            match <= 1'b0;

  
endmodule

复盘

  • 和上题一样

VL27 不重叠序列检测

在这里插入图片描述

答案

`define SIZE 6
`define SEQ 6'b011100

`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input data,
	output reg match,
	output reg not_match
	);
    
        
    reg [`SIZE - 1:0] reg_a;
    always@(posedge clk or negedge rst_n)
        if (!rst_n)
            reg_a <= 'b0;
        else 
            reg_a <={reg_a[`SIZE - 2:0],data};
    

    
    reg [2:0] cnt;
    always@(posedge clk or negedge rst_n)
        if (!rst_n)begin
            cnt <= 1'b0;
        end
        else if (cnt == 3'd5)
            cnt <= 1'b0;
        else
            cnt <= cnt + 1'b1;
    
    
    always@(posedge clk or negedge rst_n)begin
        if (!rst_n)begin
            match <= 1'b0;
            not_match <= 1'b0;
        end
        else if (cnt == 3'd5)begin
            if ({reg_a[`SIZE - 2:0],data} == `SEQ)begin
                    match <= 1'b1;
            end
            else begin
                    not_match <= 1'b1;
            end
        end
        else begin
            match <= 1'b0;
            not_match <= 1'b0;
        end
    end

endmodule

在这里插入图片描述

复盘

  • 注意,此题是要求最后一个数,输入最后1bit立马输出标志位,不是下一个时钟周期输出!
  • 前面两题是两个时钟周期才输出。
    在这里插入图片描述

VL28 输入序列不连续的序列检测

在这里插入图片描述

答案

`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input data,
	input data_valid,
	output reg match
	);
    
    reg [3:0] reg_data;
    always@(posedge clk or negedge rst_n) 
        if(!rst_n)
            reg_data <= 'd0;
        else if(data_valid)
            reg_data <= {reg_data[2:0],data};
        else 
            reg_data <= data;
    
    always@(posedge clk or negedge rst_n)
        if (!rst_n)
            match <= 'b0;
        else if({reg_data[2:0],data} == 'b0110)
            match <= 'b1;
        else 
            match <= 'b0;
    
endmodule

复盘

  • 如果你习惯这种写法,就不用费尽心思去写状态机了。

02 时序逻辑

VL29 信号发生器

在这里插入图片描述

答案

`timescale 1ns/1ns
module signal_generator(
	input clk,
	input rst_n,
	input [1:0] wave_choise,
	output reg [4:0]wave
	);

    reg [4:0] cnt;
    reg flag;
    
  	// 方波模式下,计数器控制
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            cnt <= 0;
        else
            cnt <= wave_choise!=0 ? 0:
                   cnt        ==19? 0:
                   cnt + 1;
    end
    
  	// 三角波模式下,标志位控制
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            flag <= 0;
        else
            flag <= wave_choise!=2 ? 0:
                    wave       ==1 ? 1:
                    wave       ==19? 0:
                    flag;
    end
    
  
  	// 更新wave信号
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n) 
            wave <= 0;
        else 
            case(wave_choise)
                0      : wave <= cnt == 9? 20    : 
                                 cnt ==19? 0     :
                                 wave;
                1      : wave <= wave==20? 0     : wave+1;
                2      : wave <= flag==0 ? wave-1: wave+1;
                default: wave <= 0;
            endcase
    end
endmodule

在这里插入图片描述

复盘

  • 没看明白题目
  • 看题解

VL30 数据串转并电路

在这里插入图片描述

答案1

`timescale 1ns/1ns

module s_to_p(
	input 				clk 		,   
	input 				rst_n		,
	input				valid_a		,
	input	 			data_a		,
 
 	output	reg 		ready_a		,
 	output	reg			valid_b		,
	output  reg [5:0] 	data_b
);

    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
            ready_a <= 1'b0;
        end
        else begin
            ready_a <= 1'b1;
        end  
    
    reg [5:0] data_b_reg;  
    reg [2:0] cnt;    
    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
            data_b_reg <= 'd0;
            data_b <= 'd0;
            valid_b <= 1'b0;
            cnt <= 1'b0;
        end
        else if (valid_a)begin
            data_b_reg <= {data_a, data_b_reg[5:1]};
            if(cnt == 3'd5)begin
                data_b <= {data_a, data_b_reg[5:1]};
                valid_b <= 1'b1;
                cnt <= 'd0;
            end
            else begin
                valid_b <= 1'b0;
                data_b <= data_b;
                cnt <= cnt + 1'b1;
            end
        end
        else begin
            data_b_reg <= data_b_reg; 
        end

endmodule

答案2

`timescale 1ns/1ns

module s_to_p(
	input 				clk 		,   
	input 				rst_n		,
	input				valid_a		,
	input	 			data_a		,
 
 	output	reg 		ready_a		,
 	output	reg			valid_b		,
	output  reg [5:0] 	data_b
);

    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
             ready_a <= 1'b0;
        end
        else begin
             ready_a <= 1'b1;
        end  
   
    reg [5:0] data_b_reg;
    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
            data_b_reg <= 'd0;
        end
        else if (valid_a)begin
            data_b_reg <= {data_a, data_b_reg[5:1]};
        end
        else begin
            data_b_reg <= data_b_reg; 
        end  
    
    reg [2:0] cnt; 
    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
            cnt <= 'd0;
        end
        else if (valid_a)begin
            if (cnt == 'd5)
                cnt <= 'd0;
            else
                cnt <= cnt + 1'b1;
        end
    
    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
            valid_b <= 1'b0;
        end
        else if(cnt == 3'd5)begin
            valid_b <= 1'b1;
        end
        else begin
            valid_b <= 1'b0;
        end      
    
    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
            data_b <= 'd0;
        end
        else if(cnt == 3'd5)begin
            data_b <= {data_a, data_b_reg[5:1]};
        end else begin
            data_b <= data_b; 
        end

endmodule

在这里插入图片描述

复盘

  • 仔细点就行。

VL31 数据累加输出

在这里插入图片描述

答案

`timescale 1ns/1ns

module valid_ready(
	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] cnt;
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            cnt <= 'b0;
        else if(ready_a && valid_a)begin 
            if(cnt == 2'd3)
                cnt <= 'd0;
            else 
                cnt <= cnt + 1'b1;
        end
        else begin
           cnt <= cnt; 
        end
    
    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
            valid_b <= 'b0;
        end
        else if(cnt==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
    
    always@(posedge clk or negedge rst_n)
        if(!rst_n)begin
            data_out <= 'b0;
        end
        else if(ready_a && valid_a)begin 
            if(cnt == 2'd0 )begin 
            data_out <= data_in;
            end 
            else begin
                data_out <= data_out + data_in ;
            end
        end
    
    assign ready_a = ~valid_b | ready_b;
    
endmodule

在这里插入图片描述

复盘

  • 注意观察各个信号间的关系

VL32 非整数倍数据位宽转换24to128

在这里插入图片描述

答案

`timescale 1ns/1ns

module width_24to128(
	input 				clk 		,   
	input 				rst_n		,
	input				valid_in	,
	input	[23:0]		data_in		,
 
 	output	reg			valid_out	,
	output  reg [127:0]	data_out
);
    
    reg [3:0] cnt; 
    always@(posedge clk or negedge rst_n)
        if (!rst_n)
            cnt <= 'b0;
        else if (valid_in) begin
            if(cnt == 'd15)
                cnt <= 4'd0;
            else
                cnt <= cnt + 1'b1;
        end
        else 
            cnt <= cnt;
    
     always@(posedge clk or negedge rst_n)
        if (!rst_n) begin
            valid_out <= 1'b0;
        end
        else if (valid_in && (cnt == 'd5 || cnt == 'd10 || cnt == 'd15)) begin
            valid_out <= 1'b1;
        end else begin
            valid_out <= 1'b0;
        end 
    
    reg [127:0] data_out_reg;
    always@(posedge clk or negedge rst_n)
        if (!rst_n) begin
            data_out<= 'd0;
            data_out_reg <= 'd0;
        end
        else if (valid_in) begin
           if (cnt == 'd5) begin
                data_out <= {data_out_reg,data_in[23:16]};
                data_out_reg <= data_in[15:0];
            end if (cnt == 'd10)begin
                data_out <= {data_out_reg,data_in[23:8]};
                data_out_reg <= data_in[7:0];
            end if (cnt == 'd15)begin
                data_out <= {data_out_reg,data_in};
                data_out_reg <= 'd0;
            end else begin
            data_out_reg <= {data_out_reg,data_in};
            end 
        end
        else begin
            data_out_reg <= data_out_reg;
        end
            
endmodule

在这里插入图片描述

复盘

  • 注意理解5、10、15处的逻辑位置
  • 我们通常使用计数器来判断输出条件
    在这里插入图片描述

VL33 非整数倍数据位宽转换8to12

在这里插入图片描述

答案

`timescale 1ns/1ns

module width_8to12(
	input 				   clk 		,   
	input 			      rst_n		,
	input				      valid_in	,
	input	[7:0]			   data_in	,
 
 	output  reg			   valid_out,
	output  reg [11:0]   data_out
);
    
    reg [2:0] cnt; 
    always@(posedge clk or negedge rst_n)
        if (!rst_n)
            cnt <= 'd0;
        else if (valid_in) begin
            if(cnt == 'd2)
                cnt <= 'd0;
            else
                cnt <= cnt + 1'b1;
        end
        else 
            cnt <= cnt;
    
     always@(posedge clk or negedge rst_n)
        if (!rst_n) begin
            valid_out <= 1'b0;
        end
    else if (valid_in && (cnt == 'd1 || cnt == 'd2)) begin
            valid_out <= 1'b1;
        end else begin
            valid_out <= 1'b0;
        end 
    
    reg [11:0] data_out_reg;
    always@(posedge clk or negedge rst_n)
        if (!rst_n) begin
            data_out<= 'd0;
            data_out_reg <= 'd0;
        end
        else if (valid_in) begin
            if (cnt == 'd1) begin
                data_out <= {data_out_reg,data_in[7:4]};
                data_out_reg <= data_in[3:0];
            end if (cnt == 'd2)begin
                data_out <= {data_out_reg,data_in};
                data_out_reg <= 'd0;
            end else begin
                data_out_reg <= {data_out_reg,data_in};
            end 
        end
        else begin
            data_out_reg <= data_out_reg;
        end
endmodule

在这里插入图片描述

复盘

  • 和上题思路一致

VL34 整数倍数据位宽转换8to16

在这里插入图片描述

答案

`timescale 1ns/1ns

module width_8to16(
	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)
        if (!rst_n)
            cnt <= 'd0;
        else if (valid_in) begin
            if(cnt == 'd1)
                cnt <= 'd0;
            else
                cnt <= cnt + 1'b1;
        end
        else 
            cnt <= cnt;
    
     always@(posedge clk or negedge rst_n)
        if (!rst_n) begin
            valid_out <= 1'b0;
        end
        else if (valid_in && cnt == 'd1) begin
            valid_out <= 1'b1;
        end else begin
            valid_out <= 1'b0;
        end 
    
    reg [15:0] data_out_reg;
    always@(posedge clk or negedge rst_n)
        if (!rst_n) begin
            data_out<= 'd0;
            data_out_reg <= 'd0;
        end
        else if (valid_in) begin
            if (cnt == 'd1) begin
                data_out <= {data_out_reg,data_in};
                data_out_reg <= data_in;
            end else begin
                data_out_reg <= {data_out_reg,data_in};
            end 
        end
        else begin
            data_out_reg <= data_out_reg;
        end
endmodule

在这里插入图片描述

复盘

  • 和上题思路一致

VL35 状态机-非重叠的序列检测

在这里插入图片描述

答案1:寄存器写法

`define SEQR 5'b10111

`timescale 1ns/1ns

module sequence_test1(
	input wire clk  ,
	input wire rst  ,
	input wire data ,
	output reg flag
);
//*************code***********//
    reg [4:0] data_reg;
    always@(posedge clk or negedge rst)
        if(!rst)
            data_reg <= 'd0;
        else 
            data_reg <= {data_reg,data};
    
    reg flag_bit = 1'b1;
    always@(posedge clk or negedge rst)
        if(!rst)
            flag <= 1'b0;
        else if(flag_bit && {data_reg,data} == `SEQR)begin
            flag <= 1'b1;
            flag_bit <= 1'b0;
        end
        else
            flag <= 1'b0;
//*************code***********//
endmodule

在这里插入图片描述

答案2:状态机法

`define SEQR 5'b10111

`timescale 1ns/1ns

module sequence_test1(
	input wire clk  ,
	input wire rst  ,
	input wire data ,
	output reg flag
);
//*************code***********//
    parameter IDLE = 4'b0000;
    parameter S0   = 4'b0001;
    parameter S1   = 4'b0011;
    parameter S2   = 4'b0010;
    parameter S3   = 4'b0110;
    parameter S4   = 4'b0111;
    parameter S5   = 4'b0101;
    parameter S6   = 4'b0100;
    parameter S7   = 4'b1100;

    reg [3:0] cur_state, next_state;
    
    always@(posedge clk or negedge rst)
        if (!rst) begin
            cur_state <= IDLE;
            next_state <= IDLE;
        end else begin
           cur_state <= next_state; 
        end
    
    always@(*)
        case(cur_state)
            IDLE   :    next_state = (data==1)?S0:IDLE;
            S0     :    next_state = (data==0)?S1:IDLE;
            S1     :    next_state = (data==1)?S2:IDLE;
            S2     :    next_state = (data==1)?S3:S1;
            S3     :    next_state = (data==1)?S4:S1;
            S4     :    next_state = S5;
            default:    next_state = IDLE;
        endcase
        
    always@(*)
        if (cur_state == S4)
            flag <= 1'b1;
        else
            flag <= 1'b0;
  
//*************code***********//
endmodule

复盘

  • 这和1、2、3、4题一致

VL36 状态机-重叠序列检测

在这里插入图片描述

答案1:寄存器法

`timescale 1ns/1ns
`define SEQR 4'b1011
module sequence_test2(
	input wire clk  ,
	input wire rst  ,
	input wire data ,
	output reg flag
);
//*************code***********//
    reg [3:0] data_reg;
    always@(posedge clk or negedge rst)
        if(!rst)
            data_reg <= 'd0;
        else 
            data_reg <= {data_reg,data};
    
    reg cnt = 1'b1;
    always@(posedge clk or negedge rst)
        if(!rst)
            flag <= 1'b0;
        else if(data_reg == `SEQR)begin
            flag <= 1'b1;
        end
        else
            flag <= 1'b0;

//*************code***********//
endmodule

方法2:状态机法

`timescale 1ns/1ns
`define SEQR 4'b1011
module sequence_test2(
	input wire clk  ,
	input wire rst  ,
	input wire data ,
	output reg flag
);
//*************code***********//
    parameter IDLE = 4'b0000;
    parameter S0   = 4'b0001;
    parameter S1   = 4'b0011;
    parameter S2   = 4'b0010;
    parameter S3   = 4'b0110;
    parameter S4   = 4'b0111;
    parameter S5   = 4'b0101;
    parameter S6   = 4'b0100;
    parameter S7   = 4'b1100;

    reg [3:0] cur_state, next_state;
    
    always@(posedge clk or negedge rst)
        if (!rst) begin
            cur_state <= IDLE;
            next_state <= IDLE;
        end else begin
           cur_state <= next_state; 
        end
    
    always@(*)
        case(cur_state)
            IDLE   :    next_state = (data==1)?S0:IDLE;
            S0     :    next_state = (data==0)?S1:IDLE;
            S1     :    next_state = (data==1)?S2:IDLE;
            S2     :    next_state = (data==1)?S3:S0;
            S3     :    next_state = (data==1)?S0:S1;
            default:    next_state = IDLE;
        endcase
        
    always@(posedge clk or negedge rst)
        if (!rst)
             flag <= 1'b0;
        else if (cur_state == S3)
            flag <= 1'b1;
        else
            flag <= 1'b0;

//*************code***********//
endmodule

在这里插入图片描述

VL37 时钟分频(偶数)

在这里插入图片描述

答案1:计数器法

`timescale 1ns/1ns

module even_div
    (
    input     wire rst ,
    input     wire clk_in,
    output    wire clk_out2,
    output    wire clk_out4,
    output    wire clk_out8
    );
//*************code***********//
/*-------------------------------------------------------------
                            二分频
-----------------------------------------------------------*/
    reg clk_out2_reg;
    always@(posedge clk_in or negedge rst)
        if(!rst)
            clk_out2_reg <= 1'b0;
        else
            clk_out2_reg <= ~clk_out2_reg;
    
    assign clk_out2 = clk_out2_reg;
/*-------------------------------------------------------------
                            四分频
-----------------------------------------------------------*/   
    reg [1:0] cnt_4;
    always@(posedge clk_in or negedge rst)
        if(!rst)
            cnt_4 <= 'd0;
//         else if(cnt_4 == 'd3)
//             cnt_4 <= 1'b0;
        else
            cnt_4 <= cnt_4 + 1'b1;
    
    reg clk_out4_reg;
    always@(posedge clk_in or negedge rst)
        if(!rst)
            clk_out4_reg <= 1'b0;
        else if(cnt_4 < 2'd2)
            clk_out4_reg <= 1'b1;
        else
            clk_out4_reg <= 1'b0;
    
    assign clk_out4 = clk_out4_reg;
/*-------------------------------------------------------------
                            八分频
-----------------------------------------------------------*/  
    reg [2:0] cnt_8;
    always@(posedge clk_in or negedge rst)
        if(!rst)
            cnt_8 <= 'b0;
//         else if(cnt_8 == 'd7)
//             cnt_8 <= 1'b0;
        else
            cnt_8 <= cnt_8 + 1'b1;
    
    reg clk_out8_reg;
    always@(posedge clk_in or negedge rst)
        if(!rst)
            clk_out8_reg <= 1'b0;
    else if(cnt_8 < 3'd4)
            clk_out8_reg <= 1'b1;
        else
            clk_out8_reg <= 1'b0;
    
    assign clk_out8 = clk_out8_reg;
    
//*************code***********//
endmodule

答案2:寄存器级联法

`timescale 1ns/1ns

module even_div
    (
    input     wire rst ,
    input     wire clk_in,
    output    wire clk_out2,
    output    wire clk_out4,
    output    wire clk_out8
    );
//*************code***********//
    reg clk_out2_reg, clk_out4_reg, clk_out8_reg;
    
    always@(posedge clk_in or negedge rst) begin
        if(~rst)
            clk_out2_reg <= 0;
        else
            clk_out2_reg <= ~clk_out2_reg;
    end
    
    always@(posedge clk_out2 or negedge rst) begin
        if(~rst)
            clk_out4_reg <= 0;
        else
            clk_out4_reg <= ~clk_out4_reg;
    end
    
    always@(posedge clk_out4 or negedge rst) begin
        if(~rst)
            clk_out8_reg <= 0;
        else
            clk_out8_reg <= ~clk_out8_reg;
    end
    
    assign clk_out2 = clk_out2_reg;
    assign clk_out4 = clk_out4_reg;
    assign clk_out8 = clk_out8_reg;
//*************code***********//
endmodule

答案3:取巧思路

`timescale 1ns/1ns

module even_div
    (
    input     wire rst ,
    input     wire clk_in,
    output    wire clk_out2,
    output    wire clk_out4,
    output    wire clk_out8
    );
//*************code***********//
reg [2:0] cnt8;
always @ (posedge clk_in or negedge rst) begin
    if(~rst) begin
        cnt8 <= 3'b0;
    end 
    else begin
        cnt8 <= cnt8 - 3'd1;
    end 
end 

assign clk_out2 = cnt8[0];
assign clk_out4 = cnt8[1];
assign clk_out8 = cnt8[2];

endmodule

复盘

  • 推荐一篇文章【数字IC手撕代码】Verilog偶数分频|题目|原理|设计|仿真(二分频,四分频,六分频,八分频,偶数分频及特殊占空比)

VL38 自动贩售机1

在这里插入图片描述

答案

`timescale 1ns/1ns
module seller1(
	input wire clk  ,
	input wire rst  ,
	input wire d1 ,
	input wire d2 ,
	input wire d3 ,
	
	output reg out1,
	output reg [1:0]out2
);
//*************code***********//
    parameter IDLE = 4'b0000;
    parameter S0   = 4'b0001;
    parameter S1   = 4'b0011;
    parameter S2   = 4'b0010;
    parameter S3   = 4'b0110;
    parameter S4   = 4'b0111;
    parameter S5   = 4'b0101;
//     parameter S6   = 4'b0100;
//     parameter S7   = 4'b1100;

    reg [3:0] cur_state, next_state;
    
    always@(posedge clk or negedge rst)
        if (!rst) begin
            cur_state <= IDLE;
        end else begin
           cur_state <= next_state; 
        end
    
    always@(*)
        case(cur_state)
            IDLE   :
                begin
                    if(d1)
                        next_state = S0;
                    else if(d2)
                        next_state = S1;
                    else if(d3)
                        next_state = S3;
                    else
                        next_state = next_state;
                end
            S0     : 
                begin
                    if(d1)
                        next_state = S1;
                    else if(d2)
                        next_state = S2;
                    else if(d3)
                        next_state = S4;
                    else
                        next_state = next_state;
                end
            S1     :    
                begin
                    if(d1)
                        next_state = S2;
                    else if(d2)
                        next_state = S3;
                    else if(d3)
                        next_state = S5;
                    else
                        next_state = next_state;
                end
            default:    next_state = IDLE;
        endcase
        
    always@(posedge clk or negedge rst)
        if (!rst)begin
            out1 <= 'd0;
            out2 <= 'd0;
        end
        else begin
            case(next_state)
                S2:begin out1 <= 1'b1;out2 <= 2'b00;end
                S3:begin out1 <= 1'b1;out2 <= 2'b01;end
                S4:begin out1 <= 1'b1;out2 <= 2'b10;end
                S5:begin out1 <= 1'b1;out2 <= 2'b11;end
                default:begin out1 <= 1'b0;out2 <= 2'b00;end
            endcase
        end

//*************code***********//
endmodule

复盘

本题可以抽象为三输入二输出,存在状态0 0.5 1 1.5 2 2.5 3 七个状态,

这样输出信号的特性应该为: out1饮料为一位,out2找零为2位(0 1 2 3个0.5元)

因此先做状态转移图:

  • 注意,0.5/1/2是不会同时给信号的;在钱数大于2.5时就不能再继续投币了,在下一拍就要回到IDLE。

在这里插入图片描述

VL39 自动贩售机2

在这里插入图片描述

答案

`timescale 1ns/1ns

module seller2(
	input wire clk  ,
	input wire rst  ,
	input wire d1 ,
	input wire d2 ,
	input wire sel ,
	
	output reg out1,
	output reg out2,
	output reg out3
);
//*************code***********//
    parameter IDLE = 4'b0000;
    parameter S0   = 4'b0001;
    parameter S1   = 4'b0011;
    parameter S2   = 4'b0010;
    parameter S3   = 4'b0110;
    parameter S4   = 4'b0111;
    parameter S5   = 4'b0101;
//     parameter S6   = 4'b0100;
//     parameter S7   = 4'b1100;

    reg [3:0] cur_state, next_state;
    
    always@(posedge clk or negedge rst)
        if (!rst) begin
            cur_state <= IDLE;
        end else begin
           cur_state <= next_state; 
        end
    
    always@(*)
        case(cur_state)
            IDLE   :
                begin
                    if(d1)
                        next_state = S0;
                    else if(d2)
                        next_state = S1;
                    else
                        next_state = next_state;
                end
            S0     : 
                begin
                    if(d1)
                        next_state = S1;
                    else if(d2)
                        next_state = S2;
                    else
                        next_state = next_state;
                end
            S1     :    
                begin
                    if(d1)
                        next_state = S2;
                    else if(d2)
                        next_state = S3;
                    else
                        next_state = next_state;
                end
           S2     :    
                begin
                    if(sel == 0)begin
                        next_state = IDLE;
                    end else begin
                        if(d1)
                            next_state = S3;
                        else if(d2)
                            next_state = S4;
                        else
                            next_state = next_state;
                    end
                end
            S3     :    
                begin
                    if(sel == 0)begin
                        next_state = IDLE;
                    end else begin
                        if(d1)
                            next_state = S4;
                        else if(d2)
                            next_state = S5;
                        else
                            next_state = next_state;
                    end
                end           
            default:    next_state = IDLE;
        endcase
        
    always@(posedge clk or negedge rst)
        if (!rst)begin
            out1 <= 'd0;
            out2 <= 'd0;
            out3 <= 'd0;
        end
        else begin
            case(next_state)
                S2:
                    begin 
                        if(sel == 0) begin 
                            out1 <= 1'b1;
                            out2 <= 1'b0;
                            out3 <= 1'b0;
                        end else begin
                            out1 <= 1'b0;
                            out2 <= 1'b0;
                            out3 <= 1'b0;
                        end
                    end
                S3:
                   begin 
                        if(sel == 0) begin 
                            out1 <= 1'b1;
                            out2 <= 1'b0;
                            out3 <= 1'b1;
                        end else begin
                            out1 <= 1'b0;
                            out2 <= 1'b0;
                            out3 <= 1'b0;
                        end
                    end

                S4:
                    begin
                        out1 <= 1'b0;
                        out2 <= 1'b1;
                        out3 <= 1'b0;
                    end
                S5:
                    begin
                        out1 <= 1'b0;
                        out2 <= 1'b1;
                        out3 <= 1'b1;
                    end
                default:
                    begin
                        out1 <= 1'b0;
                        out2 <= 1'b0;
                        out3 <= 1'b0;
                    end
            endcase
        end

//*************code***********//
endmodule

在这里插入图片描述

复盘

  • 经典的三段式,状态转移图如下:
  • 第一段,时序逻辑寄存下一个状态
  • 第二段,组合逻辑计算下一个状态
  • 第三段,输出当前状态需要输出的条件

在这里插入图片描述

  • 如果觉得代码不好看,可以继续更改格式

VL40 占空比50%的奇数分频

在这里插入图片描述

答案

`define CNT 3'd7

`timescale 1ns/1ns

module odo_div_or (
    input    wire  rst ,
    input    wire  clk_in,
    output   wire  clk_out7
);

//*************code***********//
reg [2:0] count_p;	//上升沿计数
reg [2:0] count_n;	//下降沿计数
reg clk_p;			//上升沿分频
reg clk_n;			//下降沿分频

//上升沿计数
always @ ( posedge clk_in or negedge rst )
begin 
	if( !rst ) 
		count_p <= 3'b0;
	else if( count_p == `CNT - 1 ) 
		count_p <= 3'b0;
	else  
		count_p <= count_p + 1'b1;
end

//上升沿分频
always  @ ( posedge clk_in or negedge rst )
begin 
	if( !rst ) begin 
		clk_p <= 1'b0;
	end 
	else begin 
		if( count_p == `CNT / 2 || count_p == `CNT - 1 ) begin 
			clk_p <= ~clk_p;
		end
	end
end

//下降沿计数
always @ ( negedge clk_in or negedge rst )
begin 
	if( !rst ) 
		count_n <= 3'b0;
    else if( count_n == `CNT - 1)
		count_n <= 3'b0;
	else  
		count_n <= count_n + 1'b1;
end

//下降沿分频
always  @ ( negedge clk_in or negedge rst )
begin 
	if( !rst ) begin 
		clk_n <= 1'b0;
	end 
	else begin 
        if( count_n == `CNT / 2 || count_n == `CNT - 1 ) begin 
			clk_n <= ~clk_n;
		end
	end
end 

assign clk_out7 = clk_p | clk_n;

endmodule

复盘

  • 前面的答案使用了上升沿计数器下降沿计数器分别分别生成了两个时钟,然后将其即可

  • 参考 Verilog笔记——奇数分频和小数分频
    在这里插入图片描述

  • 其实,我们也可以不使用两个计数器

`define CNT 3'd7

`timescale 1ns/1ns

module odo_div_or (
    input    wire  rst ,
    input    wire  clk_in,
    output   wire  clk_out7
);

//*************code***********//
reg [2:0] count;	//计数
reg clk_p;			//上升沿分频
reg clk_n;			//下降沿分频

//沿计数
always @ ( posedge clk_in or negedge rst )
begin 
	if( !rst ) 
		count <= 3'b0;
	else if( count == `CNT - 1 ) 
		count <= 3'b0;
	else  
		count <= count + 1'b1;
end

//上升沿分频
always  @ ( posedge clk_in or negedge rst )
begin 
	if( !rst ) begin 
		clk_p <= 1'b0;
	end 
	else begin 
		if( count == `CNT / 2 || count == `CNT - 1 ) begin 
			clk_p <= ~clk_p;
		end
	end
end

//下降沿分频
always  @ ( negedge clk_in or negedge rst )
begin 
	if( !rst ) begin 
		clk_n <= 1'b0;
	end 
	else begin 
        if( count == `CNT / 2 || count == `CNT - 1 ) begin 
			clk_n <= ~clk_n;
		end
	end
end 

assign clk_out7 = clk_p | clk_n;

endmodule
  • 推荐文章 【数字IC手撕代码】Verilog奇数分频|题目|原理|设计|仿真(三分频,五分频,奇数分频及特殊占空比)

VL41 任意小数分频

在这里插入图片描述

答案

复盘

VL42 无占空比要去的奇数分频

在这里插入图片描述

答案

复盘

VL43 根据状态转移写状态机-三段式

在这里插入图片描述

答案

复盘

VL44 根据状态转移写状态机-二段式

在这里插入图片描述

03 跨时钟域传输

VL45 异步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
);
 
    localparam ADDR_WIDTH = $clog2(DEPTH) - 1 + 1;
    //通过深度换算出需要多少位宽,因为fifo在最高位需要一个标志位产生满信号,上述再+1
    
    
    //第二步:给RAM申明缺少的端口信号
    /// 
    reg [ADDR_WIDTH:0] waddr,raddr;//写地址,读地址
    wire wenc,renc;//写使能信号,读使能信号
    
    
    //第三步:写地址操作,什么时候写
    /// 
    always@(posedge wclk or negedge wrstn) begin
        if(!wrstn) begin
            waddr <= 'd0;
        end
        else begin
            if(wenc) begin
                waddr <= waddr + 1'b1;
            end 
            else begin
                waddr <= waddr;
            end         
        end
    end
    
    assign wenc  = winc && ~wfull;//写使能信号
    
    
    //第四步:读地址操作,什么时候读
    /// 
    always@(posedge rclk or negedge rrstn) begin
        if(!rrstn) begin
            raddr <= 'd0;
        end
        else begin
            if(renc) begin
                raddr <= raddr + 1'b1;
            end 
            else begin
                raddr <= raddr;
            end         
        end
    end
    
    assign renc  = rinc && ~rempty;//读使能信号
    
    
    //第五步:产生空满信号---->异步 涉及到 跨时钟域同步
    //空信号是在读时钟域产生,所以需要将写时钟域的写地址同步到读时钟域
    //满信号是在写时钟域产生,所以需要将读时钟域的读地址同步到写时钟域
    //---->推荐将跨时钟域的地址转换为格雷码再同步
    ///
        ///
        //5.1 生成格雷码地址
        ///  
        reg [ADDR_WIDTH:0] waddr_gray,raddr_gray;//写地址格雷码,读地址格雷码

        always@(posedge wclk or negedge wrstn) begin
            if(!wrstn) begin
                waddr_gray <= 'd0;
            end
            else begin
                waddr_gray <= waddr ^ (waddr >> 1);
            end
        end
        always@(posedge rclk or negedge rrstn) begin
            if(!rrstn) begin
                raddr_gray<= 'd0;
            end
            else begin
                raddr_gray <= raddr ^ (raddr >> 1);
            end
        end
        ///
        //5.2 将格雷码地址同步到相应的时钟域
        ///      
        reg [ADDR_WIDTH:0] addr_w2r_reg,addr_w2r;
        always@(posedge rclk or negedge rrstn) begin
            if(!rrstn) begin
                addr_w2r_reg <= 'd0;
                addr_w2r <= 'd0;
            end
            else begin
                addr_w2r_reg <= waddr_gray;
                addr_w2r <= addr_w2r_reg;
            end
        end

        reg [ADDR_WIDTH:0] addr_r2w_reg,addr_r2w;
        always@(posedge wclk or negedge wrstn) begin
            if(!wrstn) begin
                addr_r2w_reg <= 'd0;
                addr_r2w <= 'd0;
            end
            else begin
                addr_r2w_reg <= raddr_gray;
                addr_r2w <= addr_r2w_reg;
            end
        end
        ///
        //5.3 产生空满信号
        ///    
        assign wfull  = (waddr_gray == {~addr_r2w[ADDR_WIDTH:ADDR_WIDTH-1],addr_r2w[ADDR_WIDTH-2:0]});
        assign rempty = (raddr_gray == addr_w2r);
    
    //第一步:例化双口RAM
    ///
    dual_port_RAM 
    #(
        .DEPTH(DEPTH)
       ,.WIDTH(WIDTH)
     )
    dual_port_RAM_inst
    (
         .wclk  (wclk                         )
        ,.wenc  (wenc                         )    
        ,.waddr (waddr[ADDR_WIDTH - 1 : 0]    )       //深度对2取对数,得到地址的位宽。
        ,.wdata (wdata                        )       //数据写入
        ,.rclk  (rclk                         )    
        ,.renc  (renc                         )    
        ,.raddr (raddr[ADDR_WIDTH - 1 : 0]    )       //深度对2取对数,得到地址的位宽。
        ,.rdata (rdata                        )	      //数据输出
    );
    
    
endmodule

在这里插入图片描述

复盘

  • 异步fifo,而且还是通过双口RAM充当存储结构
  • 常规的思路就是,先进行例化,缺什么信号,补什么信号。具体的步骤已经在答案中做了详细的注释
  • 推荐查看【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现了解不使用双口RAM如何手撕。

VL46 同步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
);
    
    localparam ADDR_WIDTH = $clog2(DEPTH) - 1 + 1;
    //通过深度换算出需要多少位宽,因为fifo在最高位需要一个标志位产生满信号,上述再+1
    
    
    //第二步:给RAM申明缺少的端口信号
    /// 
    reg [ADDR_WIDTH:0] waddr,raddr;//写地址,读地址
    wire wenc,renc;//写使能信号,读使能信号
    
    
    
    
    //第三步:写地址操作,什么时候写
    /// 
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            waddr <= 'd0;
        end
        else begin
            if(wenc) begin
                waddr <= waddr + 1'b1;
            end 
            else begin
                waddr <= waddr;
            end         
        end
    end
    
    assign wenc  = winc && ~wfull;//写使能信号
    
    
    //第四步:读地址操作,什么时候读
    /// 
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            raddr <= 'd0;
        end
        else begin
            if(renc) begin
                raddr <= raddr + 1'b1;
            end 
            else begin
                raddr <= raddr;
            end         
        end
    end
    
    assign renc  = rinc && ~rempty;//读使能信号
    
    
    //第五步:产生空满信号
    ///
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            wfull <= 'd0;
            rempty <= 'd0;
        end
        else begin
            wfull <= ((waddr[ADDR_WIDTH] != raddr[ADDR_WIDTH]) && (waddr[ADDR_WIDTH-1:0] == raddr[ADDR_WIDTH-1:0]));
            rempty <= raddr == waddr;
        end
    end

    
    
    //第一步:例化双口RAM
    ///
    dual_port_RAM 
    #(
        .DEPTH(DEPTH)
       ,.WIDTH(WIDTH)
     )
    dual_port_RAM_inst
    (
         .wclk  (clk     )
        ,.wenc  (wenc     )    
        ,.waddr (waddr   )       //深度对2取对数,得到地址的位宽。
        ,.wdata (wdata   )       //数据写入
        ,.rclk  (clk    )    
        ,.renc  (renc    )    
        ,.raddr (raddr   )       //深度对2取对数,得到地址的位宽。
        ,.rdata (rdata   )	    //数据输出
    );
    
    
endmodule

复盘

  • 同步fifo,而且还是通过双口RAM充当存储结构
  • 常规的思路就是,先进行例化,缺什么信号,补什么信号。具体的步骤已经在答案中做了详细的注释
  • 推荐查看【FPGA——基础篇】同步FIFO与异步FIFO——Verilog实现了解不使用双口RAM如何手撕。

VL47 格雷码计数器

在这里插入图片描述

答案

  • 什么逆天计数器,两个时钟周期
`timescale 1ns/1ns
module gray_counter(
   input   clk,
   input   rst_n,
   output  reg [3:0] gray_out
);
    
    reg [4:0] bin_cnt = 'd0;
    always @ (posedge clk or negedge rst_n) 
    begin
        if( ~rst_n ) begin
            bin_cnt <= 'd0;
        end 
        else begin
            bin_cnt <= bin_cnt + 'd1;
        end 
    end 

    wire [3:0] bin;
    assign bin = bin_cnt[4:1];
    always @ (*) 
    begin
        gray_out = bin ^ (bin>>1);
//         gray_out[3] = bin[3];
//         gray_out[2] = bin[3] ^ bin[2];
//         gray_out[1] = bin[2] ^ bin[1];
//         gray_out[0] = bin[1] ^ bin[0];

    end 
    
endmodule

在这里插入图片描述

进行修改

`timescale 1ns/1ns
module gray_counter(
   input   clk,
   input   rst_n,
   output  reg [3:0] gray_out
);
    
    reg [4:0] bin_cnt = 'd0;
    always @ (posedge clk or rst_n) 
    begin
        if( ~rst_n ) begin
            bin_cnt <= 'd0;
            gray_out <= 'd0;
        end 
        else begin
            bin_cnt <= bin_cnt + 'd1;
            gray_out <= bin_cnt[4:1] ^ (bin_cnt[4:1]>>1);
        end 
    end  
endmodule

既然是两个时钟周期出结果,我们可以先讲时钟进行二分频,在该时钟下完成计数器累加

`timescale 1ns/1ns
module gray_counter(
   input   clk,
   input   rst_n,
   output  reg [3:0] gray_out
);
    reg clk2;
    always @ (posedge clk or negedge rst_n) 
    if(!rst_n)
        clk2 <= 'b0;
    else 
        clk2 <= ~clk2;
    
        
    reg [3:0] bin_cnt = 'd0;
    always @ (posedge clk2 or negedge rst_n) 
    begin
        if( !rst_n ) begin
            bin_cnt <= 'd0;
        end 
        else begin
            bin_cnt <= bin_cnt + 'd1;
        end 
    end  
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            gray_out <= 0;
        end
        else begin
            gray_out = (bin_cnt >> 1) ^ bin_cnt;
        end
    end
endmodule

复盘

  • 格雷码与二进制的转换

在这里插入图片描述

在这里插入图片描述

VL48 多bit MUX同步器

在这里插入图片描述

答案

`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
);
    
    reg data_en_b,data_en_b_reg;
    reg [3:0] data_in_b,data_in_b_reg;
    
    always@(posedge clk_b or negedge brstn) begin
        if(!brstn)begin
            {data_en_b,data_en_b_reg} <= 'd0;
            {data_in_b,data_in_b_reg} <= 'd0;
        end 
        else begin
            {data_en_b,data_en_b_reg} <= {data_en_b_reg,data_en};
            {data_in_b,data_in_b_reg} <= {data_in_b_reg,data_in};
        end
    end
    
    always@(posedge clk_b or negedge brstn) begin
        if(!brstn)begin
            dataout <= 'd0;
        end 
        else if (data_en_b)begin
            dataout <= data_in_b;
        end 
        else begin
            dataout <= dataout;
        end
    end    
endmodule

在这里插入图片描述

复盘

  • 注意对比异步FIFO的格雷码跨时钟域同步

VL49 脉冲同步电路

在这里插入图片描述

答案

`timescale 1ns/1ns

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

	output  		 	dataout
);
    reg data_in_fast;
    always @ (posedge clk_fast or negedge rst_n) 
    begin
        if(~rst_n) begin
            data_in_fast <= 1'b0;
        end
        else begin
            if(data_in == 1'b1)
                data_in_fast <= ~data_in_fast;
            else 
                data_in_fast <= data_in_fast;
        end 
    end 

    reg data_in_slow_t1;
    reg data_in_slow_t2;
    reg data_in_slow_t3;
    always @ (posedge clk_slow or negedge rst_n) 
    begin
        if(~rst_n) begin
            data_in_slow_t1 <= 1'b0;
            data_in_slow_t2 <= 1'b0;
            data_in_slow_t3 <= 1'b0;
        end
        else begin
            data_in_slow_t1 <= data_in_fast;
            data_in_slow_t2 <= data_in_slow_t1;
            data_in_slow_t3 <= data_in_slow_t2;
        end 
    end

    assign dataout = data_in_slow_t2 ^ data_in_slow_t3;
endmodule

复盘

04 计数器

VL50 简易秒表

在这里插入图片描述

答案

`timescale 1ns/1ns

module count_module(
	input clk,
	input rst_n,

    output reg [5:0]second,
    output reg [5:0]minute
	);
    
	
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            second <= 'd0;
        end
        else if (minute == 'd60)begin
            second <= 'd0;
        end
        else if (second == 'd60)begin
            second <= 'd1;
        end 
        else begin
            second <= second + 'd1;
        end
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            minute <= 'd0;
        end
//         else if (minute == 'd60)begin
//             minute <= minute;
//         end 
        else if(second == 'd60)begin
            minute <= minute + 'd1;
        end
        else begin
            minute <= minute;
        end 
    end	
endmodule

复盘

  • 就是简单的计数器
  • 注意思考,被注释掉的三行代码,

VL51 可置位计数器

在这里插入图片描述

答案

`timescale 1ns/1ns

module count_module(
	input clk,
	input rst_n,
	input set,
	input [3:0] set_num,
	output reg [3:0]number,
	output reg zero
	);
    
    reg [3:0] cnt;
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            cnt <= 'd0;
        end
        else if(set) begin
           cnt <= set_num; 
        end
        else begin
            cnt <= cnt + 1'b1;
        end
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            zero <= 'd0;
        end   
        else if(cnt == 'd0)begin
            zero <= 1'b1;
        end else begin
            zero <= 1'b0;
        end
    end

    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            number <= 'd0;
        end   
        else begin
            number <= cnt;
        end
    end
endmodule

在这里插入图片描述

复盘

  • 计数器的基础上,增加了设置计数器数值的功能

VL52 加减计数器

在这里插入图片描述

答案

`timescale 1ns/1ns

module count_module(
	input clk,
	input rst_n,
	input mode,
	output reg [3:0]number,
	output reg zero
	);
    
    reg [3:0] cnt;
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            cnt <= 'd0;
        end
        else if(mode) begin
            if(cnt == 'd9) begin
                cnt <= 'd0;
            end 
            else begin
                cnt <= cnt + 1'b1;
            end
        end
        else begin
            if(cnt == 'd0) begin
                cnt <= 'd9;
            end 
            else begin
                cnt <= cnt - 1'b1;
            end
        end
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            zero <= 'd0;
        end   
        else if(cnt == 'd0)begin
            zero <= 1'b1;
        end else begin
            zero <= 1'b0;
        end
    end

    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            number <= 'd0;
        end   
        else begin
            number <= cnt;
        end
    end
endmodule

在这里插入图片描述

复盘

  • 就是在计数器的逻辑上增加了一些基本条件

05 存储器

VL53 单端口RAM

在这里插入图片描述

答案

`timescale 1ns/1ns

module RAM_1port(
    input clk,
    input rst,
    input enb,
    input [6:0]addr,
    input [3:0]w_data,
    output wire [3:0]r_data
);
//*************code***********//
    reg[3:0] mem [127:0];
    
    reg[3:0] data;
    integer i;
    always@(posedge clk or negedge rst) begin
        if(!rst)begin
            for(i = 0; i < 128; i = i + 1)begin
                mem[i] = 'd0;
            end
        end
        else if(enb)
            mem[addr] <= w_data;
    end
    
    reg [3:0] r_data_reg;
    always@(*) begin
        r_data_reg = 'd0;
        if(~enb)
            r_data_reg = mem[addr];
    end
    
//     always@(negedge clk or negedge rst) begin
//         if(!rst)begin
//             r_data_reg <= 'd0;
//         end
//         else if(~enb)
//             r_data_reg <= mem[addr];
//     end
    
    assign r_data = r_data_reg;
//*************code***********//
endmodule

复盘

  • enb=1的时候是写,addr就是写地址,enb=0的时候读,addr就是读地址。
  • 深度是128bit, 宽度是4bit. 使用reg [3:0] ram_reg[127:0]表示。这个初始化不能用 ram_reg <= 'd0; 需要用一个for循环来实现初始化。
  • 注意思考被注释的逻辑与上述组合逻辑的区别

VL54 RAM的简单实现

在这里插入图片描述

答案

`timescale 1ns/1ns
module ram_mod(
	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
);
    
    reg [3:0] RAM [255:0];
     
    integer i; // 不可综合
//     reg [8:0] i;
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            for(i=0;i<256;i=i+1)
                RAM[i] <= 0;
        else if(write_en) 
            RAM[write_addr] <= write_data;
        else
            RAM[write_addr] <= RAM[write_addr];
    end
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            read_data <= 0;
        else if(read_en) 
            read_data <= RAM[read_addr];
        else
            read_data <= read_data;
    end	
endmodule

复盘

  • 最基本的双口RAM

06 综合

VL55 Johnson Counter

在这里插入图片描述

答案

`timescale 1ns/1ns

module JC_counter(
   input                clk ,
   input                rst_n,
 
   output reg [3:0]     Q  
);
    always@(posedge clk or negedge rst_n)
        if(!rst_n)
            Q <= 'd0;
        else
            Q <= {~Q[0],Q[3:1]};

endmodule

复盘

  • 实现思路:将最后一位取反挪到最高位

VL56 流水线乘法器

在这里插入图片描述

答案

`timescale 1ns/1ns

module multi_pipe#(
	parameter size = 4
)(
	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		
);


    reg [7:0]	addr01;
    reg [7:0]	addr23;

    wire [7:0]	temp0 ;
    wire [7:0]	temp1 ;
    wire [7:0]	temp2 ;
    wire [7:0]	temp3 ;

    assign temp0 = mul_b[0]? {4'b0, mul_a} : 'd0;
    assign temp1 = mul_b[1]? {3'b0, mul_a, 1'b0} : 'd0;
    assign temp2 = mul_b[2]? {2'b0, mul_a, 2'b0} : 'd0;
    assign temp3 = mul_b[3]? {1'b0, mul_a, 3'b0} : 'd0;

    always @(posedge clk or negedge rst_n) begin 
        if(~rst_n) begin
            addr01  <= 'd0;
            addr23  <= 'd0;
            mul_out <= 'd0;
        end 
        else begin
            addr01 <= temp0 + temp1;
            addr23 <= temp2 + temp3;

            mul_out <= addr01 + addr23;
        end
    end
endmodule

复盘

VL57 交通灯

在这里插入图片描述

答案

复盘

VL58 游戏机计费程序

在这里插入图片描述

答案

复盘

Part III Verilog企业真题

  • 如果你想练习真题,可以点击链接:《牛客刷verilog》Part III Verilog企业真题

后记

推荐相关文章

  • 校招Verilog篇刷题
  • B站-FPGA数字IC牛客Verilog刷题
  • FPGA数字IC刷题58道Verilog题解代码及视频讲解【FPGA探索者】【同步/异步FIFO】【跨时钟】

最后

以上就是故意世界为你收集整理的《牛客刷verilog》Part II Verilog进阶挑战前言Part I Verilog快速入门Part II Verilog进阶挑战Part III Verilog企业真题后记的全部内容,希望文章能够帮你解决《牛客刷verilog》Part II Verilog进阶挑战前言Part I Verilog快速入门Part II Verilog进阶挑战Part III Verilog企业真题后记所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(181)

评论列表共有 0 条评论

立即
投稿
返回
顶部