数字IC手撕代码50题(1-10)

一、序列检测器

        可以说是最经典的题目了,数电课本上的题目,涉及到状态机的设计,出现频率非常高,这里分别采用两种方式实现:

        1.状态机

        2.移位寄存器

1.1 题目描述与分析

        使用verilog代码,设计电路,检测序列为1001,检测到的时候输出1,没检测到的时候输出0

        分析:检测四位,那么就会有五个状态。分别为 0   1    10   100  1001。显然是一个摩尔型状态机。当到达第五个状态时,输出1.对五个状态进行状态编码,IDLE = 3'b000 , s1 = 3'b001 , s2 = 3'b011 ,s3 = 3'b010, s4 = 3'b110;可以使用格雷码编码,并画出状态转移图。

       

         至此,我们可以根据这个状态转移图去写两段式状态机了。

        两段式要点:parameter 状态声明 ; 第一段 当前状态转移成下一状态 ;第二段 组合逻辑 根据输入,对当前状态进行case变换;第三段 组合逻辑 检测是否到最后一个输出状态。?:

1.2 状态机实现序列检测

module moduleName1 (
    input clk,
    input rst_n,
    input seq,
    output wire detect
);
    parameter IDLE = 3'b000 , s1 = 3'b001 , s2 = 3'b011 ,s3 = 3'b010, s4 = 3'b110;
    reg [2:0] cstate,nstate;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            cstate <= IDLE;
        else
            cstate <= nstate;
    end

    always @(*) begin
        case (cstate)
            IDLE :  nstate = seq ? s1 : IDLE;
            s1   :  nstate = seq ? s1 : s2  ;
            s2   :  nstate = seq ? s1 : s3  ;
            s3   :  nstate = seq ? s4 : IDLE;
            s4   :  nstate = seq ? s1 : IDLE;
            default: nstate = IDLE;
        endcase
    end

    assign detect = nstate == s4 ? 1 : 0 ;
endmodule

 1.3 移位寄存器实现序列检测

        用移位寄存器的方法比状态机的方法代码要轻简很多,先说一下原理:

        ①先定义一个4位的变量用来移位——shift = 4’b0000;

        ②然后每输入一位x,移位放至shift中——shift <= {shift[2:0], x};

        ③检测shift是否等于1001.

        至于为什么移位是这样的表达,大家可以看下面的这个图。

        

         这样代码就简单了不少,如下所示:

module moduleName2 (
    input clk,
    input rst_n,
    input seq,
    output wire detect
);
    reg [3:0] shift;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            shift <= 4'b0000;
        else
            shift <= {shift[2:0],seq};
    end
    assign detect = shift == 4'b1001 ? 1 : 0;
endmodule

二、 序列发生器

2.1 题目描述与分析  

         编写一个模块,实现循环输出序列001011。

        分析:这里需要循环输出序列001011,显然定义一个reg[5:0] 去存储001011,然后按照时钟一拍一拍的把他的最高位读出即可。由于这里需要循环输出,所以当读到100000的时候,需要重新写入reg的值。

2.1 移位实现序列发生器

module moduleName (
    input clk,
    input rst_n,
    output reg data
);
    reg [5:0]data_r;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            data_r <= 6'b001011;
        else if (data_r == 6'b100000)           // 加括号
            data_r <= 6'b001011;
        else    
            data_r <= (data_r << 1 );           // 加括号
    end
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            data <= 1'b0;
        else    
            data <= data_r[5];
    end   
endmodule

2.2 用状态机实现序列发生器

        用状态机的思路也比较简单,可以设计 s0 = 0 , s1 = 00 ,s2 = 001, s3= 0010,s4 =00101,s5= 001011 这六个状态,并进行状态编码,每个状态都可以对应一个输出,而且状态机的转换与输入没有关系,case当前是s1,那么无条件跳到s2,并且可以对应data输出,s0对应输出0,s1对应输出0,s2对应输出1,s3对应0,等等。

module moduleName2 (
    input wire clk;
    input wire rst_n;
    output reg data
);
    reg [2:0] cstate,nstate;
    parameter s0 = 3'b000, s1 = 3'b001 , s2 = 3'b011 ,s3 = 3'b010, s4 = 3'b110, s5= 3'b111;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            cstate <= s0;
        else
            cstate <= nstate;
    end
    always @(*) begin
        case (cstate)
            s0   :  begin nstate = s1; data <= 1'b0;end
            s1   :  begin nstate = s2; data <= 1'b0;end
            s2   :  begin nstate = s3; data <= 1'b1;end
            s3   :  begin nstate = s4; data <= 1'b0;end
            s4   :  begin nstate = s5; data <= 1'b1;end
            s5   :  begin nstate = s0; data <= 1'b1;end
            default: begin nstate = s0; data <= 1'b0;end
        endcase
    end
endmodule

三、模三检测器

3.1 题目描述与分析

        使用verilog代码,设计电路,判断输入序列能否被三整除,能的时候输出1,不能的时候输出0。

        思路:首先,一个数去除以3,其余数只有0 1 2 三种状态,我们再加一个IDLE状态,对这四个状态进行编码。IDLE = 2‘b00,s0 = 2‘b01,s1 = 2‘b11,s2= 2'b10。分别对应余0,余1,余2三种状态。

              这个题目是判断输入序列能不能被三整除,所以我们要推断出,输入与状态转换之间的关系。每输入一位,就相当于现在的序列左移一位,在加上输入的数。

                所以,当输入为0时,序列值变成前一刻的值x的二倍 2x 。当输入序列为1时,序列值变成 2x + 1。这样我们可以推断出余数的状态转移图。

                

                对于s0,可以看做是3x。

                来一个1时,变成2*(3x)+ 1,则余1,转到s1态。

                来一个0时,变成2*(3x) ,则余0,转到s0态。

                对于s1,可以看做是3x+1。

                来一个1时,变成2*(3x+1)+ 1,则余0,转到s0态。

                来一个0时,变成2*(3x+1) ,则余2,转到s2态。

                对于s2,可以看做是3x+2。

                来一个1时,变成2*(3x+2)+ 1,则余2,转到s2态。

                来一个0时,变成2*(3x+2) ,则余1,转到s1态。

                最后判断是否为s0态,输出检测信号高电平。

3.2 状态机实现模三检测器

module moduleName (
    input clk,
    input rst_n,
    input data,
    output wire mod0
);
    parameter IDLE = 2'b00,s0 = 2'b01,s1 = 2'b11,s2 = 2'b10;
    reg[1:0]  cstate,nstate;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            cstate <= IDLE;
        else
            cstate <= nstate;
    end
    always @(*) begin
        case (cstate)
            IDLE : nstate = data ? s1 : s0 ;
            s0   : nstate = data ? s1 : s0 ;
            s1   : nstate = data ? s0 : s2 ;
            s2   : nstate = data ? s2 : s1 ;
            default: nstate = IDLE;
        endcase
    end
    assign mod0 = (cstate == s0) ? 1 : 0;
endmodule

  四、自动售卖机(一)

    4.1 题目描述与分析

        使用Verilog设计电路,完成以下功能:每瓶饮料1.5元,一次只能投入一个硬币,可投入0.5与1.0两种硬币,具有找零功能。

        分析:首先就要进行 状态的划分。状态数值为售卖机里面的钱,一共有IDLE,0.5,1,1.5,2五个状态。

        具体描述一下:(只会找零0.5元)

IDLE:复位状态,表示饮料机中的余额为0元
s1:饮料机的余额为0.5元
s2:饮料机中的余额为1元
s3:饮料机中的余额为1.5元(输出饮料,不找零)
s4:饮料集中的余额为2元(输出饮料,找零0.5元)

这个设计不会找零1元

        这个题目的不同点在于,有两个输入信号,决定状态机的转换,所以第二段的组合逻辑,状态转换时需要先考虑第一个输入,再考虑第二个输入。才决定状态转换。

        那么对于这个状态机的输入和输出来说
        输入:两位input代表投入的硬币,input[1]拉高意味着投入1元,input[0]拉高意味着投入0.5 元,默认一次只能投入一枚硬币
        输出:drink代表输出饮料,coin代表输出硬币。

        先判断是否有input[1],再去判断input[2].

        画出状态转移图,红色代表input[1],蓝色代表input[0],

        

        s3,s4 收到0的时候,返回IDLE。

 4.2 状态机实现自助售卖机

module moduleName (
    input clk,
    input rst_n,
    input [1:0] money,
    output wire drink,
    output wire coin
);
    parameter IDLE = 3'b000,s1 = 3'b001,s2 =3'b010,s3 = 3'b011,s4 = 3'b100;
    reg[2:0]  cstate,nstate;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            cstate <= IDLE;
        else
            cstate <= nstate;
        
    end
    always @(*) begin
        case (cstate)
            IDLE : nstate = money[1] ? s2 : (money[0] ? s1 : IDLE) ;
            s1   : nstate = money[1] ? s3 : (money[0] ? s2 : s1  ) ;
            s2   : nstate = money[1] ? s4 : (money[0] ? s3 : s2  ) ;
            s3   : nstate = money[1] ? s2 : (money[0] ? s1 : IDLE) ;
            s4   : nstate = money[1] ? s2 : (money[0] ? s1 : IDLE) ;
            default: nstate = IDLE ;
        endcase
    end 
    assign drink = (cstate == s3) ||  (cstate == s4) ;
    assign cion  = (cstate == s4) ? 1 : 0 ;
endmodule

五、自动售卖机(二)

5.1 题目描述与分析 

        题目描述:   

设计一个自动贩售机,输入货币有两种,为0.5/1元,饮料价格是1.5/2.5元,要求进行找零,找零只会支付0.5元。

ps:

1、投入的货币会自动经过边沿检测并输出一个在时钟上升沿到1,在下降沿到0的脉冲信号

2、此题忽略出饮料后才能切换饮料的问题

注意rst为低电平复位

信号示意图:

d1 0.5   d2 1  sel  选择饮料 out1 饮料1 out2 饮料2 out3 零钱

        题目分析:

        与上题不同的是。多了一个选择信号,这个选择信号可以选择购买不同的饮料。

        首先,我们确定状态个数。 

        IDLE : 贩卖机里累计0元。

        s1     : 贩卖机里累计0.5元。

        s2     : 贩卖机里累计1元。

        s3     : 贩卖机里累计1.5元。

        s4     : 贩卖机里累计2元。

        s5     : 贩卖机里累计2.5元。

        s6     : 贩卖机里累计3元。

       第一种思路: 当贩售机内金额大于1.5时,也就是S3及以后的状态,还应根据饮料种类做判断。如果选择的是饮料1,sel=0,则返回S0状态;如果选择的是饮料2,sel=1,则根据投入的货币继续向下转移状态。即s3以后的状态,三目运算符会多一点点。

        第二种思路:分别两个状态机,由sel信号一开始就决定进入哪个状态机。

5.2 状态机实现自动贩卖机(二)

        第一种思路:

        

module moduleName (
    input wire clk  ,
    input wire rst_n  ,
    input wire d1 ,
    input wire d2 ,
    input wire sel ,
     
    output reg out1,
    output reg out2,
    output reg out3
);
    parameter IDLE = 3'b000, s0_5 = 3'b001,s1 =3'b010,s1_5 = 3'b011,s_2 = 3'b100;
    parameter s2_5 = 3'b101, s3 = 3'b110;
    reg[2:0]  cstate,nstate;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n)
            cstate <= IDLE;
        else
            cstate <= nstate;
    end
    always @(*) begin
        case (cstate)
            IDLE : nstate = d1 ? s0_5 : (d2 ? s1 : IDLE) ;
            s0_5 : nstate = d1 ? s1   : (d2 ? s1_5 : s1) ;
            s1   : nstate = d1 ? s1_5 : (d2 ? s_2 : s1)   ;
            s1_5 : nstate = !sel ? IDLE : d1 ? s_2 : (d2 ? s2_5 : s1_5);
            s_2  : nstate = !sel ? IDLE : d1 ? s2_5 : (d2 ? s3 : s_2);
            s2_5 : nstate = IDLE ;    
            s3   : nstate = IDLE ; 
            default: nstate = IDLE ;
        endcase
    end 
    always@(posedge clk or negedge rst_n)begin
        if(rst_n == 1'b0)begin
            out1 <= 1'b0;
            out2 <= 1'b0;
            out3 <= 1'b0;
        end
        else begin
            if(!sel)begin
                case (nstate)
                    s1_5:    begin out1 <= 1'b1;out2 <= 1'b0;out3 <= 1'b0;end 
                    s_2:     begin out1 <= 1'b1;out2 <= 1'b0;out3 <= 1'b1;end 
                    default:begin out1 <= 1'b0;out2 <= 1'b0;out3 <= 1'b0;end 
                endcase
            end
            else begin
                case (nstate) 
                    s1_5:   begin out1 <= 1'b0;out2 <= 1'b1;out3 <= 1'b0;end  
                    s_2:     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
        end
    end
endmodule

        第二种思路:

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

    parameter S0 = 'd0, S1 = 'd1, S2 = 'd2, S3 = 'd3 , S4 = 'd4, S5 = 'd5, S6 = 'd6;
    reg [2:0]	current_state;
    reg [2:0]	next_state;
    wire [1:0]  input_state;
	assign input_state = {d1,d2};
	
    always@(posedge clk or negedge rst)begin
        if(rst == 1'b0)begin
            current_state <= S0;
        end
        else begin
            current_state <= next_state;
        end
    end   
    
    always@(*)begin
		if (!sel) begin
			case(current_state)
			    S0:begin
					case(input_state)
						2'b10 :next_state = S1 ;
						2'b01 :next_state = S2 ;
						default:next_state = next_state;
					endcase	
			    end
			    S1:begin
			    	case(input_state)
						2'b10 :next_state = S2 ;
						2'b01 :next_state = S3 ;
						default:next_state = next_state;
					endcase	 
			    end
			    S2:begin
				    case(input_state)
						2'b10 :next_state = S3 ;
						2'b01 :next_state = S4 ;
						default:next_state = next_state;
					endcase	
			    end
			    default:  next_state = S0;  
			endcase
		end
		else begin
			case(current_state)
			    S0:begin
					case(input_state)
						2'b10 :next_state = S1 ;
						2'b01 :next_state = S2 ;
						default:next_state = next_state;
					endcase	
			    end
			    S1:begin
			    	case(input_state)
						2'b10 :next_state = S2 ;
						2'b01 :next_state = S3 ;
						default:next_state = next_state;
					endcase	 
			    end
			    S2:begin
				    case(input_state)
						2'b10 :next_state = S3 ;
						2'b01 :next_state = S4 ;
						default:next_state = next_state;
					endcase	
			    end
				S3:begin
				    case(input_state)
						2'b10 :next_state = S4 ;
						2'b01 :next_state = S5 ;
						default:next_state = next_state;
					endcase	
				end
				S4:begin
				    case(input_state)
						2'b10 :next_state = S5 ;
						2'b01 :next_state = S6 ;
						default:next_state = next_state;
					endcase	
				end
			    default:  next_state = S0;  
			endcase
			
		
		end
    end
    
    always@(posedge clk or negedge rst)begin
        if(rst == 1'b0)begin
            out1 <= 1'b0;
			out2 <= 1'b0;
			out3 <= 1'b0;
        end
        else begin
			if(!sel)begin
				case (next_state)
					S3:		begin out1 <= 1'b1;out2 <= 1'b0;out3 <= 1'b0;end 
					S4:		begin out1 <= 1'b1;out2 <= 1'b0;out3 <= 1'b1;end 
					default:begin out1 <= 1'b0;out2 <= 1'b0;out3 <= 1'b0;end 
				endcase
			end
			else begin
				case (next_state)	
					S5:		begin out1 <= 1'b0;out2 <= 1'b1;out3 <= 1'b0;end 	
					S6:		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
        end
    end
 
endmodule

猜你喜欢

转载自blog.csdn.net/lgk1996/article/details/125983739