Verilog 学习之路(三)——牛客刷题篇

1.输入序列连续的序列检测

  • 题面
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kJH9kHFH-1690301233803)(https://s2.loli.net/2023/07/26/HJPXR2mhbaVCG6d.png)]
  • 思路

对于序列检测题目,常规的解法有两种:状态机法和序列缓存对比法。
状态机法的过程类似于:在初始状态中,先判断第一位是否符合,若符合则进入下一个状态,判断第二位是否符合;若第一位不符合则保持在初始状态,直到第一位匹配。如前两位匹配,则判断第三位是否符合,若第一位匹配,最新输入的数值和目标序列的第二位不匹配,则根据最新一位是否匹配第一位,进入第一位匹配状态或者初始状态。依次类推。
序列缓存对比法,则是将八个时刻的数据缓存,作为一个数组,每个时刻的输入位于数组的末尾,数组其它元素左移,把最早输入的数据移出。然后将数组和目标序列对比,如果数组和目标序列相等,则说明出现目标序列。
序列缓存对比法在实现上比较简单。首先声明一个数组,缓存八个时刻的a输入的数值。移位可以通过位截取操作和位拼接操作实现:a_tem[6:0]表示截取a_tem的低7位,{a_tem[6:0],a}表示把a_tem[6:0]和新输入的数值a拼接,a位于低位。

  • 代码
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
	reg [7:0] a_tem;
	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			match <=1'b0;
		end
		else if (a_tem == 8'b0111_0001) begin
			match <= 1'b1;
		end
		else begin
			match <= 1'b0;
		end
	end

	always @(posedge clk or negedge rst_n) begin
		if (!rst_n) begin
			a_tem <= 8'b0;
		end
		else begin
			a_tem <= {a_tem[6:0],a};
		end
	end
  
endmodule

2.含有无关项的序列检测

  • 题面
    Verilog 学习之路(三)——牛客刷题篇_第1张图片
  • 思路

序列缓存对比法,则是将九个时刻的数据缓存,作为一个数组,每个时刻的输入位于数组的末尾,数组其它元素左移,把最早输入的数据移出。然后截取数组的前三位和目标序列011对比,截取数组的后三位和目标序列110对比,如果两段数组都和目标序列相等,则说明出现目标序列。
序列缓存对比法在实现上比较简单,本题采用该方法实现。首先声明一个数组,缓存九个时刻的a输入的数值。移位可以通过位截取操作和位拼接操作实现:a_tem[7:0]表示截取a_tem的低7位,{a_tem[7:0],a}表示把a_tem[7:0]和新输入的数值a拼接,a位于低位。

  • 代码
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output match
	);
    reg [8:0] a_tem;
    reg match_f;
    reg match_b;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            match_f <=1'b0;
        end
        else if (a_tem[8:6] == 3'b011) begin
            match_f <= 1'b1;
        end
        else begin
            match_f <= 1'b0;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            match_b <= 1'b0;
        end
        else if (a_tem[2:0] == 3'b110) begin
            match_b <= 1'b1;
        end
        else begin
            match_b <= 1'b0;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            a_tem <= 9'b0;
        end 
        else begin
            a_tem <= {a_tem[7:0],a};
        end
    end

    assign match = match_b && match_f;
endmodule
  • 解法2
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
    reg [8:0] sequence;

	always @(posedge clk or negedge rst_n) begin
		if (~rst_n) begin
			sequence <= 9'b0;
		end
		else begin
			sequence <= {sequence[7:0],a};
		end
	end

	always @(posedge clk or negedge rst_n) begin
		if (~rst_n) begin
			match <= 0;
		end
		else if (sequence[8:6] == 3'b011 && sequence[2:0] == 3'b110) begin
			match <= 1;
		end
		else begin
			match <= 0;
		end
	end

endmodule
  • 解法3
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input a,
	output reg match
	);
    reg [8:0] val;
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            val <= 9'b0;
        end else begin
            val <= {val[7:0],a};
        end
    end
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            match <= 1'b0;
        end else begin
            casex (val)
                9'b011xxx110 : match <= 1'b1;
                default : match <= 1'b0;
            endcase
        end
    end
  
endmodule

3. 不重叠序列检测

  • 题意
    Verilog 学习之路(三)——牛客刷题篇_第2张图片
    题目描写错误,应该是001110,而题目是011100,差评。
  • 思路

使用数选器选择出来对应的位,再做同或与最后做与运算,资源用的也很少。

  • 代码
`timescale 1ns/1ns
module sequence_detect(
	input clk,
	input rst_n,
	input data,
	output reg match,
	output reg not_match
	);
    
    reg [2:0] cnt;
    reg cmp;
    reg  detect_cmp;
    parameter detect = 6'b011100;
    
    always@(posedge clk or negedge rst_n)begin
        if(! rst_n) 
            cnt  <=  3'd0;
        else if(cnt == 3'd5)
            cnt  <=  3'd0;
        else 
            cnt  <=  cnt  + 3'd1;
    end
    always@(*)begin
        case(cnt)
        3'd0: cmp = 1'd0;
        3'd1: cmp = 1'd1;
        3'd2: cmp = 1'd1;
        3'd3: cmp = 1'd1;
        3'd4: cmp = 1'd0;
        3'd5: cmp = 1'd0;
        default: cmp = 1'd0;
        endcase
    end
    
    always@(posedge clk or negedge rst_n) begin
        if(! rst_n)
                detect_cmp <= 1'd1;
                else if(cnt == 3'd5)
                        detect_cmp  <=  1'd1;
        else
            detect_cmp <= detect_cmp && (~( cmp^ data));
    end
                
    always@(posedge clk or negedge rst_n) begin
        if(! rst_n)
                match <= 1'd0;
        else if((detect_cmp )&&(cnt == 3'd5))
                match <= 1'd1;
        else
                match <= 1'd0;
    end
                
     always@(posedge clk or negedge rst_n) begin
        if(! rst_n)
                not_match <= 1'd0;
         else if((!detect_cmp)&&(cnt == 3'd5))
                not_match <= 1'd1;
        else
                not_match <= 1'd0;
    end
endmodule

4. 简易秒表

  • 题意

Verilog 学习之路(三)——牛客刷题篇_第3张图片

  • 思路

首先确定second的取值逻辑:当minute=60时停止计数,即保持second为0;当second=60时,下一个周期second置为1。其余情况second 等于前一时刻的值加一。 然后明确minute的取值逻辑:当second=60,minute等于前一时刻的值加一。其余情况,minute保持不变。

  • 代码
`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
            minute <= 6'd0;
        end 
        else if (second == 6'd60) begin
            minute <= minute + 1;
        end
        else begin
            minute <= minute;
        end
    end
	
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            second <= 6'd0;
        end 
        else if (second == 6'd60) begin
            second <= 6'd1;
        end
        else if (minute == 60) begin
            second <=0;
        end 
        else 
            second <= second + 1'd1;
    end
endmodule

5. 可置位计数器

  • 题意
    Verilog 学习之路(三)——牛客刷题篇_第4张图片
  • 思路

​ 首先明确number的取值逻辑,声明number变量为4位无符号数,数值每个时钟加一,则每次数值达到15,下一个时钟因为位宽的限制,自动变为1,可以实现十六进制计数。当set信号为1时,将set_num的值赋给number。

然后确定zero的取值逻辑,在默认情况下为0,当number=0时,zero值为1。

因为判断number4’d0需要一个时钟,zero信号为1,总是滞后number0一个时钟周期。所以可以考虑将number延迟一个时钟再输出。使用num变量代替上述的number,再通过以下语句实现number延迟一个时钟输出。【因为number是寄存器类型,无法通过组合逻辑进行阻塞赋值同时匹配。 如果直接使用number进行zero的判断来说,zero肯定是比number过“0”时刻慢一拍的; 所以不妨使用num_reg,进行延迟; 因为zero肯定是比num_reg慢一拍的,所以再通过num_reg延迟一拍给number,则number与zero同步输出匹配;】

  • 代码
`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] num;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            zero = 1'd0;
        end 
        else if (num == 4'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
            num <= 4'b0;
        end
        else if (set) begin
            num <= set_num;
        end
        else begin
            num <= num + 1'd1;
        end
    end

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

6. 加减计数器

  • 题意
    Verilog 学习之路(三)——牛客刷题篇_第5张图片

  • 思路

​ 首先确定zero的取值逻辑,在默认情况下为0,当number=0时,zero值为1。 always @(posedge clk or negedge rst_n)

​ 然后将mode的值作为if-else的判断条件,当mode为1时,number每个时钟周期加一,当mode为0时,number每个时钟周期减一。

​ 按照以上代码,因为判断number4’d0需要一个时钟,zero信号为1,总是滞后number0一个时钟周期。所以可以考虑将number延迟一个时钟再输出。使用num变量代替上述的number,再通过以下语句实现number延迟一个时钟输出。

  • 代码
`timescale 1ns/1ns

module count_module(
	input clk,
	input rst_n,
	input mode,
	output reg [3:0]number,
	output reg zero
	);
    reg [3:0] num;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            zero <= 1'd0;
        end
        else if (num == 4'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
            num <= 4'b0;
        end 
        else if (mode) begin
            if (num == 9) 
				num <= 0;
            else 
				num <= num + 1'd1;
        end 
		else if (!mode) begin
            if (num == 0) num <= 9;
            else num <= num - 1'd1;
        end
        else 
			num <= num ;
    end

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

7.RAM的简单实现

  • 题意

Verilog 学习之路(三)——牛客刷题篇_第6张图片

  • 思路

要实现RAM,首先要声明数据的存储空间,例如:[3:0] rom [7:0];变量名称ram之前的[3:0]表示每个数据具有多少位,指位宽;变量名称ram之后的[7:0]表示需要多少个数据,指深度,注意这里深度为8,应该是使用[7:0],而不是[2:0];

​ 声明存储变量之后,需要对ram进行初始化,写入数据,当write_en有效,向write_addr写入write_data,当read_en有效,根据输入的read_addr输出read_data。需要注意的是,题目要求实现真双端口RAM,即可以同时写入和读出,所以需要使用两个always语句块实现写入和读出逻辑,不可以在同一个always块中使用if-else if-else if结果。

  • 代码
`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] myRAM [7:0];
    reg [8:0] i;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            for (i = 0;i < 256;i = i+1)
                myRAM[i] = 0;
        end
        else begin
            myRAM[write_addr] <= write_en ? write_data:myRAM[write_addr];
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (~rst_n) begin
            read_data <= 0;
        end
        else 
            read_data <= read_en?myRAM[read_addr]:read_data;
    end
endmodule

你可能感兴趣的:(FPGA,Verilog,学习,fpga开发)