《牛客刷verilog》Part II Verilog进阶挑战

前言

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

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

复盘

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

复盘

`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

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

在这里插入图片描述

复盘

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

复盘

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企业真题

后记

推荐相关文章

猜你喜欢

转载自blog.csdn.net/haojie_duan/article/details/125438272