本实验要求实现一个两位十进制数加减乘除运算的计算器,运算数和运算符(加、减、乘、除)由按键来控制,4×4键盘按键分配如图11-1所示。 图11-1  4×4键盘按键分配图

图11-1 4×4键盘按键分配图

运算数和计算结果通过8个八段数码管显示。每个运算数使用两个数码管显示,左侧显示十位数,右侧显示个位数。输入两位十进制数时,最高位先在右侧显示,然后其跳变到左侧的数码管上,低位在刚才高位占据的数码管上显示。各数码管所显示的内容对应如表 11-1所示。

表 11-1 数码管显示内容

运算数一L7,L8
运算数二L9,L10
运算结果L11,L12,L13,L14



本实验以前面的键盘扫描和数码管显示为基础。利用按键输入两个十进制运算数和运算符(加、减、乘、除),然后进行相应的运算。其中对除法运算只能得到整数部分。由于输入的是十进制数,因此在运算时要先将十进制数转换为二进制数。在得到结果后,再将结果转换为十进制数,显示在八位数码管上。本程序的关键在于状态机的设计,计算器要求根据不同的按键输入来决定状态迁移。



3.1 总体框架

本设计的顶层模块是calculator,包括以下子模块(图11 1):
1. Scanclk模块产生键盘扫描和数码管显示所需要的扫描时钟。
2. Scan模块完成按键的扫描,获取用户按键。
3. Datain模块是程序的核心模板,完成按键和操作符及运算数的对应,完成用户要求的运算。
4. Data
2disp和dispscan模块负责运算数和运算结果的译码及显示,这个与前面键盘扫描实验一样。 图11-2 计算器程序模块图

图11-2 计算器程序模块图

下面主要介绍Datain模块,其它模块在前面程序中已经介绍过。

3.2 datain模块(datain.v)

datain对键盘扫描到的按键进行处理,获得运算数和运算符,并得出结果。输入输出接口如下:

 
input 	clk, reset;				//时钟和复位信号。
input   keyout_en;               //按键输入使能。			
input	[3:0]add_r,             //按键行线输入
input   [3:0]add_c;		        //按键列线输入
output	[7:0]dec_a;				//运算数一输出
output	[7:0]dec_b;				//运算数二输出
output	[15:0]dec_o;             //运算结果输出

datain模块的主状态机为State,State的状态图如图11 3所示。State有四个状态:操作数1(operand1)、操作数2(operand2)、运算符(operatech)、结果(equal):
1. operand1是系统的初始状态。在Operand1状态,系统等待用户输入。当有数字输入时,状态继续在operand1状态,等待下一个输入输入;如果输入为“加”、“减”、“乘”、“除”中的任意一个运算符,则进入operate
ch状态。
2. 在operatech状态,如果输入仍为运算符,则继续停留在operatech状态;如果输入为数字,则进入operand2状态,等待操作数2更多的输入;如果此时输入为“清零”,则回到operand1状态,恢复到初始状态。
3. 在operand2状态,如果输入为数字,则继续停留在operand2状态,并记录输入的数字;如果输入为“=”,则计算结果并进入equal状态;如果输入为“清零”,则回到operand1状态,恢复到初始状态。
4. 在equal状态,如果输入为“清零”,则回到operand1状态,恢复到初始状态;否则维持在原状态。

图11-3 State状态机状态转移图

图11-3 State状态机状态转移图

下面为详细代码分析。要控制状态机首先要识别输入的按键:

 
always @(posedge clk or negedge reset) begin
	if(!reset) begin
		reg_in <= 4'b0000;	
		mode <= 4'b0000;
	end
	else begin
	    keyout_en_delay <= keyout_en;  //状态机在keyout_en_delay控制下工作,keyout_en_delay将按键输入使能延迟一个时钟周期,以便在按键识别后控制状态机,
		if((add_r != 4'b1111)&&(add_c != 4'b1111))
       //根据输入的键盘行值和列值判断按键。
		case(add_r) 
		4'b0100:	
			case(add_c)//7,8,9,/
				4'b0100: reg_in <= 4'b0111;  //reg_in记录了输入的数字,输入为7。
				4'b0011: reg_in <= 4'b1000;  //输入为8。
				4'b0010: reg_in <= 4'b1001; // 输入为9。
				4'b0001: begin	reg_in <= 4'b0000;	mode <= 4'b1000; end					//输入为除法。mode记录了运算符,4’b1000表示除法。
				default: begin	reg_in <= 4'b0000;	mode <= 4'b0000; end
			endcase
		4'b0011:
			case(add_c)//4,5,6,*
			4'b0100: reg_in <= 4'b0100;  //输入为4。
			4'b0011: reg_in <= 4'b0101;  //输入为5。
			4'b0010: reg_in <= 4'b0110;  //输入为6。
			4'b0001: begin	reg_in <= 4'b0000;	mode <= 4'b0100; end
			//输入为乘法。
			default: begin	reg_in <= 4'b0000;	mode <= 4'b0000;	end
		endcase
		4'b0010:
			case(add_c)//1,2,3,-
			4'b0100: reg_in <= 4'b0001;  //输入为1。
			4'b0011: reg_in <= 4'b0010;  //输入为2。
			4'b0010: reg_in <= 4'b0011;  //输入为3。
			4'b0001: begin	reg_in <= 4'b0000;	mode <= 4'b0010;	end	
			//输入为减法。
			default: begin	reg_in <= 4'b0000;	mode <= 4'b0000;	end
			endcase
		4'b0001://0、清零、=、+
			case(add_c)
			4'b0100: reg_in <= 4'b0000;    //输入为0;
			4'b0011: begin reg_in <= 4'b0000; clear <= 1; end//输入为清零。
			4'b0010: begin reg_in <= 4'b0000; calc <= 1;  end //输入为等于。
			//clear为高表示清零,calc为高表示要求输出计算结果。
			4'b0001: begin	reg_in <= 4'b0000;	mode <= 4'b0001;end	//输入为加法。
			default: begin	reg_in <= 4'b0000;	mode <= 4'b0000;	end
		endcase	 	 						
	end
end
状态机跳转分析:
always @ (posedge clk or negedge reset)
begin 
if( !reset ) begin
		dec_a <= 8'b0000_0000;
		dec_b <= 8'b0000_0000;
		dec_o <= 16'b0;
		state <= operand1;
	end
	else begin		
		if ( clear ) begin	//如果输入的按键是清零,则回到operand1状态。
			dec_a <= 8'b0000_0000;
			dec_b <= 8'b0000_0000;
			dec_o <= 16'b0;
			state <= operand1;
		end	
		else
if(keyout_en_delay)             //如果有按键输入。
case(state)  //进入状态机控制部分。
				operand1: begin					//输入第一个运算数
// 当扫描到第四列的键盘时,说明有运算符输入,则下个周期进入operate_ch状态
					if(add_c == 4'b0001)
state <= operate_ch;      
				else
					begin
						state <= operand1;  
				//将输入数字赋值给dec_a的低四位。同时原来的低四位进位到高四位。						dec_a <= {dec_a[3:0],reg_in}; 
					end
				end
			operate_ch:	begin		//输入运算符
				if(add_c != 4'b0001) begin
state <= operand2;    //当扫描到键盘不在第四列时,则下个周期进入operand2状态,输入第二个运算数。
dec_b <= {dec_b[3:0],reg_in}//记录输入的运算数。
end
		end
	operand2: begin	
		if( calc ) begin  //如果输入的是等号,则输出计算结果。
			state <= equal;	
			dec_o <= result;  //result为计算结果。
		end
		else begin        //否则记录新输入的数字。
			state <= operand2;
			dec_b <= {dec_b[3:0],reg_in};
		end			
	end
	equal:  //在equal状态维持不变。
		begin
		end
	default: 
		begin	state <= operand1;	end              //默认状态为operand1
endcase

3.3 calculate模块(calculate.v)

Calculate模块完成两位十进制整数的四则运算,此模块为datain模块的子模块,输入输出接口如下:

input 	clk,reset;						//时钟和复位。
input	[3:0]mode;                      //四则运算符输入。
input 	[IN_WIDTH-1:0]dec_a;            //运算数一输入。
 input 	[IN_WIDTH-1:0]dec_b;            //运算数二输入。
output [O_WIDTH-1:0]result;            //运算结果 输出。
 
assign hex_a = dec_a[7:4]*4'b1010 + dec_a[3:0];	//dec_a的前四位是十进制数的十位数,后四位是十进制数的个位数。将dec_a转换为二进制数 hex_a。
assign	hex_b = dec_b[7:4]*4'b1010 + dec_b[3:0];
 
always @ (posedge clk or negedge reset) begin
if(!reset)
		temp <= 16'b0;
	else begin
		case(mode)
		4'b0001: temp <= hex_a + hex_b;		//加法操作
		4'b0010: begin 							//减法操作
			if(hex_a>=hex_b)
				temp <= hex_a - hex_b;	
			else
				temp <= hex_b - hex_a;
		end	
		4'b0100: temp <= hex_a * hex_b;		//乘法操作
		4'b1000: temp <= hex_a / hex_b;		//除法操作
		default: temp <= 16'b0;
		endcase	
	end
end
//将二进制数转换为十进制数
assign	result[15:12] = temp/1000;		//千位数。
assign	result[11:8] = (temp%1000)/100; //百位数。
assign	result[7:4] = (temp%100)/10;    //十位数。
assign	result[3:0] = temp%10;          //各位数。



在此程序中的核心模块是datain,其它模块都是复用了前面几个程序中的模块,所以仿真时只对datain模块进行仿真,以提高仿真效率。仿真的激励文件为testdin.v,在激励文件中产生datain模块需要的addrr、addrc、keyouten等信号,分别对“加”、“减”、“乘”、“除”进行仿真,从图11 4到图11 7分别给出了“加”、“减”、“乘”、“除”的仿真结果,下面对加法运算做详细解释。

由左至右观测图11-4中的光标,在最左边光标处,模拟用户按下了按键“4”;在第2个光标处,模拟用户按下了按键“3”,那么用户输入的加数为“43”,在deca中十位和各位分别用高四位和低四位保存,因此,deca为67。在第3个光标处,用户输入操作符“+”。在第4个光标处,用户按下了按键“9”;在第5个光标处,用户按下了按键“8”,因此用户输入的被加数是“98”,和加数一样,被加数由decb的高四位和低四位保存,因此decb为152。在第6个光标处,Calculate运算出结果为141。在第7个光标处用户按键“=”键,则输出结果给deco;同样141在deco中为16进制,转换成10进制则为321,结果正确。

其它的运算的仿真与加法类似:图11 5的乘法为:8×20=160;图11 6的减法为76-15=61;图11 7的除法为84÷41=4。 图11-4 加法运算仿真波形

图11-4 加法运算仿真波形

图11-5 乘法运算仿真波形

图11-5 乘法运算仿真波形

图11-6 减法仿真波形

图11-6 减法仿真波形

图11-7 除法仿真波形

图11-7 除法仿真波形



文件名功能类型、对应模块
calculator.v主程序顶层文件
datain.v从键盘读取数据,进行计算,得到结果
calculate.v进行四则运算
scanclk.v|键盘扫描时钟|…| |scan.vv|扫描键盘|…| |data2disp.v|数码管译码|…| |dispscan.v数码管扫描
test_din.v测试程序用于modelsim仿真,对datain.v进行仿真



演示设备要求:核心板,扩展板

演示步骤:把程序下载到开发系统上后,参照键盘分配图按下4*4键盘区的相应键,即可做整数四则运算,以及清零。按下的键值和预期的运算结果可在八个数码管上显示出来。核心板Reset按键为复位键。