时序电路状态机-连续加法计算器

关于组合逻辑时序逻辑,<=,=,电平触发和边缘触发

时序逻辑用<=(非阻塞赋值)
组合逻辑用=(阻塞赋值)
组合逻辑一般采用电平触发:always @ *
时序逻辑一般采用时钟边沿触发:always @( posedge clk)

暂存问题:reg型变量和wire型变量与上面的关系

1.非阻塞赋值和阻塞赋值与是否是reg型变量和wire型变量无关?

关于利用verilog将二进制码转换为十进制BCD码

利用verilog将二进制码转换为十进制BCD码

思路

题目的关键在于状态机的设计,由于以输入数据为状态,将会导致状态太多,因此用等待不同的按键输入为状态,总共有下面四种状态:
1.清零状态:按下reset键会进入这种状态
2.等待加号或等号状态:按下confirm键会进入这种状态
3.等待加号或确认状态:按下equal键会进入这种状态
4.等待确认状态:按下add键会进入这种状态

加法的思路:
设计一个长度为8的向量作为最终的结果存放,设计两个长度为4的数组,分别存放的数的十位和个位数

显示的思路:
由于显示得数的两个数码管共用同一管脚,但是两个分别显示十位和个位,所以不能同时显示,只能沿用实验三的思路,同一时刻只显示一个数码管,但是变换频率很高,利用人眼的暂留效应,看起来是两个数码管同时显示。

输出的思路:
调用实验一中的数码管数字显示模块。

第二版成功程序


module shiyan4c(
input clk,  
	    //input rst_n,  
	    //四个按键  
	    input clear_button,  
	    input confirm_button,  
	    input plus_button,  
	    input equal_button,  
	    //输入加数  
	    input [3:0] number,  
	    //输出结果 88421数
	    output [7:0] out,
        output reg [1:0] wei
	);  
    reg [7:0] result;
	//暂存结果  
	reg [7:0] result_temp;  
	reg [3:0] input_temp;  
	reg [7:0] plus_result_temp;  
	//此变量仅用于在摁下等号之后,再摁确认键将暂存结果清零,进行新一轮加法  
	reg if_clear_result;   
	reg [3:0] ishi;reg [3:0] ige;//十位数和个位数
	//用于频闪
	localparam N = 18; 
    reg [N-1:0] regN;
    reg [3:0] i;
	initial begin  
	    result_temp = 8'b0;  
	    plus_result_temp = 8'b0;  
	    input_temp = 4'b0;  
	end  
	  
	//定义状态  
	parameter w_f_p_e = 2'b00; //处于等待加号或等号状态 wait for plus or equal  
	parameter w_f_c = 2'b01;  //处于等待确认键状态 wait for confirm  
	parameter w_f_p_c = 2'b10;    //处于等待加号或确认键状态 wait for plus or confirm  
	parameter clear = 2'b11;    //清零状态  clear  
	  
	reg [1:0] current_state;  
	reg [1:0] next_state;  
	  
	//状态寄存器  
	always @(posedge clk) begin  
	    if (clear_button)  
	        current_state <= clear;  
	    else   
	        current_state <= next_state;  
	end  
	  
	  
	//次态的组合逻辑  
	always @* begin  
	    case (current_state)  
	        clear: begin  
	            if (confirm_button) next_state = w_f_p_e;  
	            else next_state = clear;  
	            end  
	        w_f_p_e: begin  
	            if (plus_button) next_state = w_f_c;  
	            else if (equal_button) next_state = w_f_p_c;  
	            else next_state = w_f_p_e;  
	            end  
	        w_f_c: begin  
	            if (confirm_button) next_state = w_f_p_e;  
	            else next_state = w_f_c;  
	            end  
	        w_f_p_c:begin  
	            if (plus_button) next_state = w_f_c;  
	            else if (confirm_button) next_state = w_f_p_e;  
	            else next_state = w_f_p_c;  
	            end  
	        default: next_state = 2'bxx;   
	    endcase  
	end  
	  
	//input_temp  
	always @(posedge clk) begin  
	    if (current_state == clear)  
	        input_temp <= 4'b0;  
	    else if (current_state == w_f_p_e && confirm_button)  //只有在wfpe状态时更新的input_temp才有效  
	        input_temp <= number;  
	    else if (current_state == w_f_p_c && equal_button)  //按下等号后input_temp要清零  
	        input_temp <= 4'b0;  
	end  
	  
	//if_clear_result  
	always @ * begin  
	    if (current_state == w_f_p_c && confirm_button)  //在等号之后摁下确认键,且状态切换前  
	        if_clear_result = 1'b1;  
	    else  
	        if_clear_result = 1'b0;  
	end  
	  
	/* 
	关于plus_result_temp和result_temp: 
	    加法的基本公式是:当前结果=输入+上次运算的结果 
	    在本题中不能用result_temp <= result_temp + ...的形式,因为result_temp是时序逻辑,写成自加会每一个上升沿都进行计算。 
	    试着用另一个变量去储存"上次运算的结果",因为输入是比较简单的。plus_result_temp就是这个功能。 
	    因为按下确认键之后,进入wfpe状态,即下一个有效的键一定是加号或者等号,所以下一步一定是计算操作(除去清零,清零的优先级最高), 
	因此在wfpe状态就将下一步的结果计算出来,在wfpe的第一个周期input_temp还没变,但input_temp早晚会成为正确值,因此plus_result_temp 
	也是早晚会成为有效值。而plus_result_temp = result_temp +input_temp,即上次的结果和当前输入之和。 
	    在摁下加号或者等号之后,进入的wfc或wfpc状态后把plus_result_temp赋给result_temp,这时的result_temp就是我们希望的值,并且在 
	此后,result_temp又成为了"上次运算的结果",供下次plus计算所用。当然,plus在wfc和wfpc的状态中是不能变更的。 
	*/  
	//plus_result_temp  
	always @(posedge clk) begin  
	    if (current_state == clear)  
	        plus_result_temp <= 8'b0;  
	    else if (if_clear_result == 1'b1)  
	        plus_result_temp <= 8'b0;  
	    else if (current_state == w_f_p_e) begin  
	        if (({4'b0000,input_temp} + {4'b0000,result_temp[3:0]} > 8'd9) && (result_temp[7:4] + 1'b1 > 4'b1001))  //这是加数达到三位数的情况,例如99+9  
	            plus_result_temp <= result_temp + {4'b0000,input_temp} + 8'b0110_0110;  
	        else if (({4'b0000,input_temp} + {4'b0000,result_temp[3:0]} > 8'd9) && (result_temp[7:4] + 1'b1 <= 4'b1001))   //这是需要进位但加数不到三位数的情况  
	            plus_result_temp <= result_temp + {4'b0000,input_temp} + 8'b0000_0110;  
	        else   
	            plus_result_temp <= result_temp + {4'b0000,input_temp};  
	    end  
	  
	end  
	  
	//result_temp  
	always @(posedge clk) begin  
	    if (current_state == clear)  
	        result_temp <= 8'b0;  
	    else if (if_clear_result == 1'b1)  
	        result_temp <= 8'b0;  
	    //在输入加号或者等号的时候才进行计算,但加号只在wfc状态有效,等号只在wfpc状态有效  
	    else if (current_state == w_f_c && plus_button)   
	        result_temp <= plus_result_temp;  
	    else if (current_state == w_f_p_c && equal_button)  
	        result_temp <= plus_result_temp;  
	end  
	  
	//输出result  
	always @ * begin  
	    if (current_state == clear)  
	        result = 8'b0;  
	    else if (current_state == w_f_p_e)  
	        result = {4'b0000,input_temp};  
	    else if (current_state == w_f_c || current_state == w_f_p_c)  
	        result = result_temp;  
	    else  
	        result = 8'b0;
	    //赋值给十位和个位
	    ishi=result[7:4];
	    ige=result[3:0];  
	end  
//频闪
always @(posedge clk) 
    regN<= regN + 1;
    always@ *
    case (regN[N-1])
    1'b0:begin
        wei=2'b01;
        i=ige;
    end
    1'b1:begin
        wei=2'b10;
        i=ishi;
    end
    endcase
//实例化
qiduanyimaqi A1(i,out);
endmodule // calculate   
	
module qiduanyimaqi(
    input [3:0] in,
    output reg [7:0] out    //in和out是子模块里面的输出,不对应管脚
    );
    always@*
    begin
    case(in)
    4'b0000:out=8'b11111100;
    4'b0001:out=8'b01100000;
    4'b0010:out=8'b11011010;
    4'b0011:out=8'b11110010;
    4'b0100:out=8'b01100110;
    4'b0101:out=8'b10110110;
    4'b0110:out=8'b10111110;
    4'b0111:out=8'b11100000;
    4'b1000:out=8'b11111110;
    4'b1001:out=8'b11100110;
    4'b1010:out=8'b00011010;
    4'b1011:out=8'b00110010;
    4'b1100:out=8'b01000110;
    4'b1101:out=8'b10010110;
    4'b1110:out=8'b00011110;
    4'b1111:out=8'b00000000;
    endcase
   end
endmodule

管脚设置

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

第一版失败程序


module shiyan4(
input clk,reset,
input [0:3] in,//每次的输入数据    !!!注意:这才是向量的命名,把[3:0]放后面是数组!!!
input add,equal,confirm,
output [7:0]ge,[7:0]shi,//个位和十位数码管的八个灯管的输出
output reg g=1,//个位所在的灯的公共端
output reg s=1//十位所在的灯的公共端
    );
//以下为中间变量设置
localparam[1:0]
in1=2'b00,
in2=2'b01,
in3=2'b10;//三种状态
reg [1:0] inn,inst;//inn为当前状态,inst为下一个状态
reg [0:3] ige; reg [0:3] ishi;//个位数和十位数
reg [0:6] i=7'b0000000;//总的计算结果,由于题目要求不超过100,因此7位够用
    always @(posedge reset)
        if(reset) begin
            inn<=in1;
            i<=7'b0000000;
        end
        else 
            inn<=inst;
    always @(posedge clk)
        case(inn)
            in1:
                if(confirm)
                    inst<=in1;
                else if(add) 
                    inst<=in2;
                else if(equal)
                    inst<=in3;
                        
            in2:
                if(confirm) 
                    inst<=in2;
                else if(add) 
                    inst<=in1;
                else if(equal) 
                    inst<=in3;
            in3:
                if(add)
                    inst<=in1;
       endcase
always @(posedge clk)
        case(inn)
            in1:
                if(confirm) 
                    i<=in;//能这么写吗?
                else if(add)
                    i<=in;
                else if(equal)
                    i<=i+in;         
            in2:
                if(confirm) 
                    i<=in;//是不是此时已经更新到in2?
                else if(add)
                    i<=i+in;
                else if(equal)
                    i<=i+in;
            in3:
                if(add) 
                    i<=i+in;
       endcase
//假设此已经计算出最终的结果存在i里面
//Verilog里面只能对2的指数次幂做取模运算,
//因为对于2的指数次幂来说,只涉及到对寄存器中数据的左移右移操作,适合数字处理。
//如果在FPGA里要进行对任何整数的取模运算,对于确定的数,
//可以用while判断循环,转换成乘法和减法运算;
//对于不确定的数,由于while条件语句里不能有不确定项,所以只能转换成以最大循环次数为固定次数的for循环。
always @(*)
        while (i>=10)begin
            i=i-7'b0001010;
            ishi=ishi+4'b0001;
            ige = i[3:6];//把剩下的i(小于10)赋值给个位
        end
        qiduanyimaqi A1(ige,ge);
        qiduanyimaqi A2(ishi,shi);
endmodule

module qiduanyimaqi(
    input [0:3] in,
    output reg [7:0] out    //in和out是子模块里面的输出,不对应管脚
    );
    always@*
    begin
    case(in)
    4'b0000:out=8'b00111111;
    4'b0001:out=8'b00000110;
    4'b0010:out=8'b01011011;
    4'b0011:out=8'b01001111;
    4'b0100:out=8'b01100110;
    4'b0101:out=8'b01101101;
    4'b0110:out=8'b01111101;
    4'b0111:out=8'b00000111;
    4'b1000:out=8'b01111111;
    4'b1001:out=8'b01100111;
    4'b1010:out=8'b01011000;
    4'b1011:out=8'b01001100;
    4'b1100:out=8'b01100010;
    4'b1101:out=8'b01101001;
    4'b1110:out=8'b01111000;
    4'b1111:out=8'b00000000;
    endcase
   end
endmodule
//疑点1in在时时更新,这里所有的in1 in2 ....都用in?
//疑点2:状态转换图正确吗?
//还是上次的问题:【03】还是【30】参考实验2
//目前改动:把中间计算用到的都改成0开头,输出正常
//尝试把十进制转化为二进制失败程序
//  always @(*)
//        while (i>=10)begin
//            i=i-7'b0001010;
//            ishi=ishi+4'b0001;
//            ige = i[3:6];//把剩下的i(小于10)赋值给个位
//        end
//        qiduanyimaqi A1(ige,ge);
//        qiduanyimaqi A2(ishi,shi);
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值