Verilog代码题——基本电路

基本电路

1位全加器

module full_adder(
	input a,
	input b,
	input cin,

	output sum,
	output cout
	);
	assign {cout,sum}=a+b+cin;
endmoudle	

| a |b | cin |sum | cout|
|–|–|–|–|–|–|–|–|–|–|–|–|–|–|
| 0 |0 | 0 |0 | 0|
| 0 |0 | 1 |1| 0|
| 0 |1 | 0 |1| 0|
| 0 |1 | 1 |0| 1|
| 1 |0 | 0 |1 | 0|
| 1 |0 | 1 |0| 1|
| 1 |1 | 0 |0| 1|
| 1 |1 | 1 |1| 1|

module full_adder(
	input a,
	input b,
	input cin,

	output sum,
	output cout
	);
	assign sum=a^b^cin;
	assign cout=(a&b)
				|(cin&b)
				|(cin&b);
endmoudle	

4位全加器

module four_full_adder(
	input [3:0] a;
	input [3:0] b,
	input cin,

	output [3:0] sum,
	output cout
	);
	assign {cout,sum}=a+b+cin;
endmoudle

4-1多路选择器

module four_mux(
	input [3:0] a,
	input [3:0] b,
	input [3:0] c,
	input [3:0] d,
	input [1:0] sel,

	output [3:0] mux_out
	);
	always@(*)
	begin
		case(sel)
			2'b00:mux_out=a;
			2'b01:mux_out=b;
			2'b10:mux_out=c;
			2'b11:mux_out=d;
		endcase
	end
endmodule

分频器

奇数分频器

通过时钟 相或 实现奇数分频

module any_odd_div(
	input clk,
	input rst_n,
	
	output div_clk
	);
	
	parameter odd = 5;
	reg [3:0] cnt1 , cnt2;
	reg clk_tem_pos , clk_tem_neg;
	
	always @ (posedge clk or negedge rst_n)
	begin
	if (!rst_n) begin
		cnt1 <= 4'b00;
		clk_tem_pos <= 1'b0;
		end
	else begin
		cnt1 <= (cnt1  == odd-1)?4'b0:cnt1+1'b1;
		clk_tem_pos <= (cnt1< (odd-1)/2)?1'b1:1'b0; 
		end
	end
	
	always @ (negedge clk or negedge rst_n)
	begin
	if (!rst_n) begin 
		cnt2 <= 4'b00;
		clk_tem_neg <= 1'b0;
		end
	else begin
		cnt2 <= (cnt2  == odd-1)?4'b0:cnt2+1'b1;
		clk_tem_neg <= (cnt2< (odd-1)/2)?1'b1:1'b0; 
		end
	end
	
	assign div_clk = clk_tem_pos|clk_tem_neg;
endmodule

仿真:

`timescale 1ns / 1ps
module any_odd_div_tb( );
    reg clk;
    reg rst;
    //reg clk_out;//concurrent assignment to a non-net 'clk_out' is not permitted
    wire clk_out;
    
    initial
    begin
        clk=0;
        rst=0;
        #50;
        rst=1;
    end
    
    always
    begin
    #5
    clk=~clk;
    end
    
    any_odd_div u1(.clk(clk),.rst_n(rst),.div_clk(clk_out));

endmodule

在这里插入图片描述

偶数分频器

module inst (
    input clk,
    output clk_out);

    //10010         1-0-0-1-0
    
    reg [4:0] ctr;
    
    always@(posedge clk) 
    begin
    if (ctr<4) begin
     ctr <= ctr+1'b1;
     end
     else begin
     ctr <= 4'b1;
     end
     end
    assign clk_out=(ctr<=2)?1'b0:1'b1;

endmodule

边沿检测

检测输入信号的边沿,当检测到信号由低到高时(posdege)对应信号输出为高。
Warning:异步信号打三拍才是边沿检测,前两拍是为了同步异步信号

module edge_detection(
	input x,
	input clk,
	output y_pos,y_neg);

	reg x_fron;
	always@(posedge clk)
		begin
		x_fron<=x;
		end
	
	assign y_pos=~x_fron&x;//posedge
	assign y_neg=x_fron&~x;//nededge
endmodule

仿真

    module edge_dec_tb();
    
    reg clk;
    reg x_in;
    
    wire y_pos,y_neg;
    
    initial
    begin
    clk=0;
    x_in=0;
    #40
    x_in=1;
    #40
    x_in=0;
    end
    
    always #5 clk=~clk;
    
    edge_detection u2(.x(x_in),.clk(clk),.y_pos(y_pos),.y_neg(y_neg));
    
    
endmodule

序列检测

题目来源于网上
序列检测:要求检测输入码流中的“10010”,检测到则data_out输出1,。同时需要排除重叠检测,例如:
输入:1-0-0-1-0_0-1-0-0-1-0-XXXXX
输出:0-0-0-0-1_0-0-1-0-0-1-XXXXX
输出码流中的第二个1,就是重叠输出,需要排除。

二段式状态机:
有两个always block,把时序逻辑和组合逻辑分隔开来。时序逻辑里进行当前状态和下一状态的切换,组合逻辑实现各个输入、输出以及状态判断。

三段式状态机:
coding style:cur_state and next_state;
有三个always block,一个时序逻辑采用同步时序的方式描述状态转移,一个采用组合逻辑的方式判断状态转移条件、描述状态转移规律,第三个模块使用同步时序的方式描述每个状态的输出。时序逻辑的输出解决了两段式组合逻辑的毛刺问题。

简要概括就是:
**Moore状态机:**最后一种状态是已完成状态。
**Mealy状态机:**比Moore状态机少一种状态,最后一个状态同时判断输出。

三段式状态机:
第一段时序逻辑写出cur_state与next_state之间的传递关系
第二段写case,每种状态向下一状态转变的条件
第三段写输出
二段式状态机:
第一段同上
第二段同时写状态传递和输出。

两段式状态机

与三段式有所不同的是,out需为reg型

`timescale 1ns / 1ps



module seq_dec(
	input seq_in,
	input clk,
	input rst_n,
	//procedural assignment to a non-register test_out is not permitted
	output reg test_out);

//定义六种状态
	parameter 	IDLE        = 6'b000001,
				fir_1       = 6'b000010,
				sec_10      = 6'b000100,
				thir_100    = 6'b001000,
				fou_1001    = 6'b010000,
				out_10010   = 6'b100000;
						

	reg [5:0] cur_state;
	reg [5:0] next_state;
	
	always@(posedge clk or negedge rst_n)
		if (!rst_n) begin
			//test_out <= 1'b0;重复定义
			cur_state  <= IDLE;
			//next_state <= IDLE;//多次定义
			end
		else begin
			cur_state <= next_state;
			end

	always@(*)
		begin
		case(cur_state)
            IDLE        :begin
                next_state = (seq_in)? fir_1    : IDLE;
                test_out=1'b0;
            end
            fir_1       :begin
                next_state = (seq_in)? fir_1    : sec_10;
                test_out=1'b0;
            end
            sec_10      :begin
                next_state = (seq_in)? fir_1    : thir_100;
                test_out=1'b0;
            end
            thir_100    :begin
                next_state = (seq_in)? fou_1001 : IDLE;
                test_out=1'b0;
            end
            fou_1001    :begin
                next_state = (seq_in)? fir_1    : out_10010;
                test_out=1'b0;
            end
            out_10010   :begin
                next_state = (seq_in)? fir_1    : IDLE ;
                test_out=1'b1;
            end
            default     :begin
                next_state = IDLE ;
                test_out=1'b0;
            end
        endcase
        end


endmodule

三段式状态机

module seq_dec(
	input seq_in,
	input clk,
	input rst_n,
	output test_out);

//定义六种状态
	parameter 	IDLE        = 6'b000001,
				fir_1       = 6'b000010,
				sec_10      = 6'b000100,
				thir_100    = 6'b001000,
				fou_1001    = 6'b010000,
				out_10010   = 6'b100000;
						

	reg [5:0] cur_state;
	reg [5:0] next_state;
	
	always@(posedge clk or negedge rst_n)
		if (!rst_n) begin
			//test_out <= 1'b0;重复定义
			cur_state  <= IDLE;
			//next_state <= IDLE;//多次定义
			end
		else begin
			cur_state <= next_state;
			end

	always@(*)
		begin
		case(cur_state)
            IDLE        :next_state = (seq_in)? fir_1    : IDLE;
            fir_1       :next_state = (seq_in)? fir_1    : sec_10;
            sec_10      :next_state = (seq_in)? fir_1    : thir_100;
            thir_100    :next_state = (seq_in)? fou_1001 : IDLE;
            fou_1001    :next_state = (seq_in)? fir_1    : out_10010;
            out_10010   :next_state = (seq_in)? fir_1    : IDLE ;
            default     :next_state = IDLE ;
        endcase
        end

    assign test_out=(cur_state == out_10010)?1'b1:1'b0;

endmodule

在这里插入图片描述
Testbench

`timescale 1ns / 1ps

module seq_dec_tb( );
    
    reg seq_in;
    reg clk;
    reg rst_n;
    
    wire test_out;
    
    initial
    begin
    clk=0;
    seq_in=0;
    rst_n=0;
    #5
    seq_in=1;
    rst_n=1;//复位信号别忘了拉起来
    #10
    seq_in=0;
    #10
    seq_in=0;
    #10
    seq_in=0;
    #10
    seq_in=1;
    #10
    seq_in=0;
    #10
    seq_in=0;
     #10
    seq_in=1;  
    #10
    seq_in=0;
    #10
    seq_in=0;
     #10
    seq_in=1; 
    end
    
    always #5 clk=~clk;

    seq_dec u1(.seq_in(seq_in),.clk(clk),.rst_n(rst_n),.test_out(test_out));
    
    reg [127:0] next_state_name;
    
   always@(*) begin
        case(u1.cur_state)//直接用模块例化的名字
	        6'b000001:   next_state_name= "IDLE"   ;
			6'b000010:   next_state_name= "fir_1";
			6'b000100:   next_state_name= "sec_10";
			6'b001000:   next_state_name= "thir_100";
			6'b010000:   next_state_name= "four_1001";
			6'b100000:   next_state_name= "out_10010";
			default  :   next_state_name= "IDLE"  ;
			endcase
		end   
endmodule

移位寄存器法

`timescale 1ns / 1ps

//用寄存器保存每次进入序列的值,当总长度为10010,输出为1
//同时要在序列为10010清空reg的值,避免重叠输出

module seq_dec(
	input seq_in,
	input clk,
	input rst_n,
	output test_out);

    reg [4:0] ud_test;

    always@(posedge clk or negedge rst_n)
        begin
        if(!rst_n) 
            ud_test<=5'b0;
        else 
            ud_test<=(ud_test==5'b10010)?{4'b0,seq_in}:{ud_test[3:0],seq_in};
        end

    assign test_out=(ud_test==5'b10010)?1'b1:1'b0;
    
endmodule

4×4查表乘法器

查表法的核心就是将乘法的所有可能结果存储起来,然后将两个相乘的数据组合起来作为地址,直接找到相应的结果。
4位乘法器的实现:调用2位乘法器,即将高位宽数据分解成低位宽的数据再调用查表乘法器。

对于 2N 位数据A,可分解为 A=A1×2^N+A0 , A1为高N位,A0为低N位
对于2位乘法器,A = 2×A1 + A0,B = 2×B1 + B0;
乘法展开:A×B = 4×A1×B1 + 2×A1×B0 + 2×A0×B1 + A0×B0;
对于4位乘法器,A = 4×A1+ A0 ,B = 4×B1 + B0;
乘法展开:A×B = 16×A1×B1 + 4×A1×B0 + 4×A0×B1 + A0×B0;

在这里插入图片描述
在这里插入图片描述

`timescale 1ns / 1ns


module inst(
    input rst_n,
    input [3:0] a,
    input [3:0] b,
    output  wire [7:0] mul4
    );
    //A×B = 16×A1×B1 + 4×A1×B0 + 4×A0×B1 + A0×B0
    //reg [3:0] a1b1;concurrent assignment to a non-net
    wire [3:0] a1b1;
    wire [3:0] a1b0;
    wire [3:0] a0b1;
    wire [3:0] a0b0;
    
    //实列化
    MULT2x2 u1(.rst_n(rst_n),.a(a[3:2]),.b(b[3:2]),.mul(a1b1));
    MULT2x2 u2(.rst_n(rst_n),.a(a[3:2]),.b(b[1:0]),.mul(a1b0));
    MULT2x2 u3(.rst_n(rst_n),.a(a[1:0]),.b(b[3:2]),.mul(a0b1));
    MULT2x2 u4(.rst_n(rst_n),.a(a[1:0]),.b(b[1:0]),.mul(a0b0));
    
    //assign mul4 = (!rst_n)? 8'b0:{a1b1,4'b0}+{a1b0,3'b0}+{a0b1,3'b0}+a0b0;
    assign mul4 = (!rst_n)? 8'b0:{a1b1,4'b0}+{a1b0,2'b0}+{a0b1,2'b0}+a0b0;

endmodule

module MULT2x2(
    input rst_n,
    input [1:0] a,//A = 2×A1 + A0
    input [1:0] b,//B = 2×B1 + B0
    output reg[3:0] mul
    );

    always @(*) begin
        if (!rst_n)
            mul=4'd0;
        else begin
        case({a,b})//case后不跟begin
                4'b0000:mul=4'd0;
                4'b0001:mul=4'd0;
                4'b0010:mul=4'd0;
                4'b0011:mul=4'd0;
                4'b0100:mul=4'd0;
                4'b0101:mul=4'd1;
                4'b0110:mul=4'd2;
                4'b0111:mul=4'd3;
                4'b1000:mul=4'd0;
                4'b1001:mul=4'd2;
                4'b1010:mul=4'd4;
                4'b1011:mul=4'd6;
                4'b1100:mul=4'd0;
                4'b1101:mul=4'd3;
                4'b1110:mul=4'd6;
                4'b1111:mul=4'd9;
        endcase  
        end
    end

endmodule

testbench

`timescale 1ns / 1ns

module inst_tb();

    reg [3:0]a;
    reg [3:0]b;
    reg rst_n;
    wire [7:0] mul;
    
    initial 
    begin
    #5
    rst_n=0;
    #5
    rst_n=1;
    a=4'd2;
    b=4'd3;
    #5
    a=4'd10;
    b=4'd3;    
    #5
    a=4'd9;
    b=4'd3;
    #5
    a=4'd2;
    b=4'd6; 
    #5
    a=4'd2;
    b=4'd4; 
    #5
    a=4'd2;
    b=4'd7; 
    #5
    a=4'd2;
    b=4'd5; 
    #5
    a=4'd5;
    b=4'd3;  
    end
    
    inst zpz1 (.rst_n(rst_n),.a(a),.b(b),.mul4(mul));
      
endmodule

在这里插入图片描述

64bits检测为1的最低位

module low_bit_dec(
    input [63:0] data,
    output reg [5:0] out_nm
);
    integer i;
    always @(*) 
        begin
        for (i=0; i < 63 ; i=i+1)
            if (data[i] == 1'b1)
                out_nm = i;
            else
               //wrong: out_nm= 6'bx;
                out_nm= out_nm;
    end
    
endmodule

两个8位数相乘

采用循环,一个数的每位与对应数相与后相移相加。

`timescale 1ns / 1ns
//
// Company: 
// Engineer: 
// 
// Create Date: 2022/02/24 18:47:02
// Design Name: 
// Module Name: inst
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module inst(
    input [7:0] data1,
    input [7:0] data2,
    input       rst_n,
    output [15:0] mul_ans
);
    
    reg [15:0] mid_ans;
    reg [15:0]
    reg [7:0] low_ans;
    

    integer i;
    always @(*) begin
        if(!rst_n) begin
            mid_ans=16'b0;
            low_ans=8'b0;
            end
        else begin
            for (i=1; i < 8 ; i=i+1) 
                mid_ans=(data1[i])? (mid_ans + (data2 << i)):mid_ans;
             low_ans={8{data1[0]}}&data2;
        end
    end
    
    assign mul_ans=mid_ans+low_ans;

endmodule

结果有误错误原因:每次的值未清零,包含上一次的值即182+32*13=598

改:

`timescale 1ns / 1ns

module  8x8mul(
    input [7:0] data1,
    input [7:0] data2,
    input       rst_n,
    output [15:0] mul_ans
);
    
    reg [15:0] mid_ans;
    wire [15:0] ap_data2;
    reg [7:0] low_ans;
    
    assign ap_data2=data2;
    
    integer i;
    always @(*) begin
        mid_ans=16'b0;//在同一个块中多次赋值不出错
        if(!rst_n) begin
			 mid_ans=16'b0;
            low_ans=8'b0;
            end
        else begin
            for (i=1; i < 8 ; i=i+1) 
                mid_ans=(data1[i])? (mid_ans + (ap_data2 << i)):mid_ans;
            low_ans={8{data1[0]}}&data2;
        end
    end
    
    assign mul_ans=mid_ans+low_ans;

endmodule

十进制计数器

module 10counter(
    input clk,
    input rst_n,
    output reg [3:0] ctr
);

    always @(posedge clk) begin
        if (!rst_n)
            ctr=4'b0;
        else
            ctr=((ctr+1'b1)==4'b1010)?4'b0:ctr+1'b1;
    end
endmodule

DFF描述

module DFF(
    input clk,
    input rst_n,
    input d,
    
    output reg q
);
    always @(posedge clk) begin
        if (!rst_n)
            q<=1'b0;
        else
            q<=d;
    end
    
endmodule

比较电路——保留最大值

module comparator(
    input clk,
    input rst_n,
    input [7:0] a,
    input [7:0] b,
    
    output reg [1:0] q
);
    always @(*) begin
        if (a > b)
            q <= 2'b01;
        else if (a == b)
            q <= 2'b00;
         else
            q <= 2'b10;
    end
    
endmodule

独热码检测

给定一个4bit的信号A,设计逻辑来判断A是不是独热码,设输出为Y,如果A是独热码,则Y输出1,如果不是,则输出0.
思路是将各个位数取和

module one_hot(
    input clk,
    input rst_n,
    input [3:0] a,
    
    output reg  q
);
    always @(*) beginone_hot
        if ((a[0]+a[1]+a[2]+a[3]) == 1)
            q <= 1'b1;//one hot bit
         else
            q <= 1'b0;
    end
    
endmodule

可置位七进制循环计数器

module load_cnt(


    input clk,
    input rst_n,
    input load,
    output reg [2:0] out
);
    parameter load_N = 2;
    
    always @(posedge clk) begin
        if (!rst_n)
            out='b0;
        else if(load)
            out = load_N;
        else 
            out = ((out+1'b1)==7)?'b0:out+'b1;
    end

endmodule

奇偶校验位

在这里插入图片描述

奇偶校验位是一个表示给定位数的二进制数中1的个数是奇数或者偶数的二进制数,奇偶校验位是最简单的错误检测码。
按位异或:偶校验
按位异或取反:奇校验

消除毛刺

出现毛刺的原因
当信号在FPGA器件内部通过连线和逻辑门时,一般都有一定的延时。延时的大小与连线的长短和门单元的数目有关,同时还受器件的制造工艺、工作电压、温度等条件的影响。此外,信号的高/低电平转换也需要一定的过渡时间。由于存在这些因素的影响,多路信号的电平值发生变化时,在信号变化的瞬间,组合逻辑的输出都有先后顺序,而并不是同时变化,这往往就会出现一些不正确的”毛刺”。
“毛刺”信号的电路有两种类型。第一种是输入信号经过FPGA内部布线以后产生不同延时的异步电路;第二种是由于在 编程时内部信号变化落后。在多个信号关联时,第一个信号变化如果发生落后情况,后面的信号将产生不确定状况,从而出现”毛刺”。 “毛刺”的存在说明该电路存在不稳定状况,因此,这很可能导致整个系统的误动作。

解决办法:
组合逻辑电路:在电路中加入同步时钟,即输入输出加上触发器,使输出信号跟随时钟同步变化。
程序中的内部信号的判别赋值并不是立即发生变化,而是存在一个延迟。这也是最容易产生”毛刺”的一种情况:采用加触发器的方法来消除”毛刺”

串并转换

module inst(


    input clk,
    input rst_n,
    input a,
    output reg [7:0] b
);
    
    always @(posedge clk) begin
        if (!rst_n)
            b='b0;
        else 
            b = {b[6:0],a};
    end

endmodule

按键消抖15ms

机械按键在按下和弹起时会出现短时间抖动,抖动时间一般持续为15ms。
在按键较少的设计中,往往会进行按键的复用。常用的方法是根据键按下时间的长与短进行判断。因此按键复用经常是利用按键消抖后的下降沿(如果键按下是高电位的话)。

假设按键为key_in,设置一个采样寄存器sw_rst记录上一个时钟的值
判断key_in与上一个寄存器的值是否一致,如果不一致则表明可能产生按键的动作(上升沿下降沿检测),将按键动作 寄存到edge_en,edge_n = 1表明产生按键的动作
后启动15ms计数器计数,15ms延时后,将key_in值赋给out

module key (
    input clk,
    input rst_n,
    input key_in,
    output reg sw_out
);
    reg sw_rst;//按键本身为1,按下后为0

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            sw_rst <= 1'b1;
        end
        else begin
            sw_rst <= key_in;
        end
    end

    wire pos_ed,neg_ed;
    assign pos_ed = !sw_rst & key_in;//上升沿
    assign neg_ed = sw_rst & !key_in; //下降沿

    wire edge_en;
    assign edge_en = pos_ed | neg_ed; //检测键值是否发生变化(上升沿和下降沿)

    reg [19:0] cout ;
    always @ (posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			count <= 0;
		end
		else if(edge_en) begin
			count <= 0;
		end
		else begin
			count <= count + 1;
		end
	
	end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            sw_out <= 1'b1;
        else
            sw_out <= key_in;
    end


endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值