A simple calculator based on Verilog

Posted by SeenGee on Wed, 02 Feb 2022 19:00:27 +0100

1. Tasks

A four digit simple calculator is designed. The number keys are input by the matrix keyboard and the display is output by the four digit nixie tube. The four operations of +, -, *, / can be realized correctly (regardless of the operation of decimals).

2. System block diagram

It is mainly composed of five modules, namely matrix keyboard scanning module, nixie tube display module, binary to BCD module and operation module. The block diagram is shown in the figure below:

3. Modular design

3.1 top level module preparation

As the entrance of the program, the top-level module calls each module to realize the functions of each module. The source code is as follows:

module cal_top(
	
	Clk,Rst_n,key_c,key_r,dig_sel,dig_data
	
	);

// input
	input Clk;						//50MHz
	input Rst_n;					//Reset low level active
	input [3:0]key_r;				//Matrix keyboard row
	
// output
	output wire [3:0]key_c;			//Matrix keyboard column
	output wire[7:0]dig_data;		//Nixie tube segment code
	output wire[3:0]dig_sel;		//4-digit nixie tube bit selection
	
	wire key_in;
	wire [3:0]key_out;				
	wire key_flag;					//Press the key to press the flag bit
	wire key_state;					//Key state

	reg [4:0]key_num;				//Key value pressed by the key

	reg [13:0]num_a;				//Operand a
	reg [13:0]num_b;				//Operand b
	
	reg dis_flag;					
	reg [3:0]opera_flag;
	wire [13:0]out_a;
	wire [13:0]out_b;
	wire dis_flago;
	wire [3:0]opera_flago;
	wire out_flagd;
	
	reg [13:0]num_sel;
	wire [3:0]data_q;			//Thousand bit
	wire [3:0]data_b;			//Hundredth
	wire [3:0]data_s;			//Ten
	wire [3:0]data_g;			//Bit
		
	reg en;
	wire [13:0]out_num;
	wire num_flag;
	
	key_filter my_key_filter(
			.Clk(Clk),
			.Rst_n(Rst_n),
			.key_in(key_in),
			.key_flag(key_flag),
			.key_state(key_state)
			);
	key_array my_array(
			.Clk(Clk),
			.Rst_n(Rst_n),
			.key_c(key_c),
			.key_out(key_out),
			.key_r(key_r),
			.key_in(key_in)
			);
			
	dig_dynamic my_dig_dynamic(
				.Clk(Clk),
				.Rst_n(Rst_n),
				.En(1'b1),
				.disp_data({data_q[3:0],data_b[3:0],data_s[3:0],data_g[3:0]}),
				.dig_sel(dig_sel),
				.dig_data(dig_data)
				);
				
	bcd_d my_bcd_d(
			.binary(num_sel),
			.g(data_g),
			.s(data_s),
			.b(data_b),
			.q(data_q)
		);	
		
	add  my_add(
			.num_a(num_sel),
			.en(en),
			.key_num(key_num),
			.out_a(out_num),
			.num_flag(num_flag)
		);
	
	opera my_opera(
			.num_a(num_a),
			.num_b(num_b),
			.en(en),
			.dis_flag(dis_flag),
			.opera_flag(opera_flag),
			.key_num(key_num),
			.out_a(out_a),
			.out_b(out_b),
			.dis_flago(dis_flago),
			.opera_flago(opera_flago),
			.out_flagd(out_flagd)
		);		
	 
	/********Number of choices******/	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n) begin
			num_a <= 14'd0;
			num_b <= 14'd0;
			dis_flag <= 1'b0;
	end
	else if(out_flagd == 1'b1) begin
			num_a <= out_a;
			num_b <= out_b;
			dis_flag <= dis_flago;
			opera_flag <= opera_flago; 
	end			
	else if(dis_flag == 1'b0) begin
			num_sel <= num_a;
			if(num_flag == 1'b1)
					num_a <= out_num;
			
			else
					num_a <= num_a;
	end
	else if(dis_flag == 1'b1) begin
			num_sel <= num_b;
			if(num_flag == 1'b1)				
					num_b <= out_num;
			else
					num_b <= num_b;
	end
			
	/*************Operand en************/		
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		en <= 1'b0;
	else if(key_flag && (!key_state)) begin
			key_num <= key_out;
			en <= 1'b1;
		end
		else
			en <= 1'b0;
endmodule

3.2 nixie tube dynamic display module

The nixie tube selected here is the common Yin nixie tube. Although there are 8 digits in the schematic diagram, only four digit nixie tubes are actually used. If necessary, you can change it to the common Yang nixie tube or increase the number of digits displayed by the nixie tube. Then, the nixie tube is directly connected to the IO without passing through other chips. Some other development boards may pass through the 138 decoder. If you download it, please modify it yourself. The schematic diagram is as follows:

module dig_dynamic(
	Clk,
	Rst_n,
	En,
	disp_data,
	dig_sel,
	dig_data	
	);
	
	// input
	input		Clk;//Clock, 50MHz
	input		Rst_n;//Asynchronous reset signal, active at low level
	input		En; //Enable end: the high level is effective. When it is effective, the nixie tube displays normally, and it is not
						//All nixie tubes are off when
	input	[15:0]disp_data;//16 bit wide data to be displayed

	// output
	output	[3:0]dig_sel;   //Bit selection drive port of nixie tube
	output	[7:0]dig_data;//Segment selection drive port of nixie tube
	
	reg		[7:0]dig_data;
	
	reg		[14:0]Cnt;			//Frequency division counter
	reg		Clk_1k;				//1kHz sweep signal
	reg		[3:0]dig_sel_r;	//Temporary register for bit select data
	reg		[3:0]dig_data_temp;//Temporary register of data to be displayed
	
	//Scan cycle counter, return to zero after counting to 25000, and control to generate 1KHz sweep signal
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		Cnt <= 15'h0;
	else if(En) begin
		if(Cnt == 15'd25000)
			Cnt <= 15'h0;
		else
			Cnt <= Cnt + 15'h1;
	end
	else
		Cnt <= Cnt;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		Clk_1k <= 1'h0;
	else if(Cnt == 15'd25000)
		Clk_1k <= ~Clk_1k;
	else
		Clk_1k <= Clk_1k;
		
	//Generate a constantly changing bit selection signal with an update frequency of 1KHz	
	always@(posedge Clk_1k or negedge Rst_n)
	if(!Rst_n)
		dig_sel_r <= 4'b1110;
	else if(En)
		dig_sel_r <= {dig_sel_r[2:0],dig_sel_r[3]};
	else
		dig_sel_r <= dig_sel_r;
	
	//When enabled, the bit selection data is output normally. When not enabled, all 0 is output, that is, all nixie tubes are off
	assign	dig_sel = (En)?dig_sel_r:4'b0000;
		
	always@(*)
		case(dig_sel_r)
			4'b1110:dig_data_temp <= disp_data[3:0];		//Lowest digital tube
			4'b1101:dig_data_temp <= disp_data[7:4];
			4'b1011:dig_data_temp <= disp_data[11:8];
			4'b0111:dig_data_temp <= disp_data[15:12];
			default:dig_data_temp <= 4'b0000;
		endcase
		
	// Common cathode nixie tube
	always@(*)
		case(dig_data_temp)
			0: dig_data <= 8'h3F;		// 0
			1: dig_data <= 8'h06;
			2: dig_data <= 8'h5B;
			3: dig_data <= 8'h4F;
			4: dig_data <= 8'h66;
			5: dig_data <= 8'h6D;
			6: dig_data <= 8'h7D;
			7: dig_data <= 8'h07;
			8: dig_data <= 8'h7F;
			9: dig_data <= 8'h6F;
			10: dig_data <= 8'h77;
			11: dig_data <= 8'h7C;
			12: dig_data <= 8'h39;
			13: dig_data <= 8'h5E;
			14: dig_data <= 8'h79;
			15: dig_data <= 8'h71;
			default:;
		endcase		

endmodule

3.3 matrix keyboard scanning

The matrix keyboard is a 4 * 4 determinant keyboard; The scanning principle of matrix keyboard is to let four horizontal rows or four vertical columns output high level first, and the other four are input mode. If scanning to high level, it means that the key is pressed in this row or column, then switch input and output, scan the other four to obtain other coordinates, so as to determine the position of the key. The schematic diagram is shown in the following figure:

/********************************************************************
*					Matrix keyboard scanning
*	Function: use scanning method to detect matrix keyboard
*	Author: Ray
*	Starting time: April 24, 2021
*	Completion time: May 26, 2021
********************************************************************/
module key_array(
	
	Clk,Rst_n,key_r,key_c,key_out,key_in
	
	);
	input Clk;
	input Rst_n;
	input [3:0]key_r; 			//that 's ok
	
	output reg [3:0]key_c;		//column
	output reg [3:0]key_out;
	output reg key_in;
	
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		key_c <= 4'b1110;
	else if (key_r == 4'b1111) begin
		if (key_c == 4'b0111)
			key_c <= 4'b1110;
		else begin
			key_c <= {key_c[2:0],key_c[3]};
			
		end
	end
	

	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		key_out <= 4'h6;
	else if (key_r == 4'b1111)
		key_in<=1'b1;
	else
		case(key_c)
			4'b1110 :
				case(key_r)
					4'b1110 : 	begin
									key_out <= 4'h1;
									key_in <= key_r[0];
									end
					
					4'b1101 : 	begin
									key_out <= 4'h4;
									key_in <= key_r[1];
									end
					
					4'b1011 : 	begin
									key_out <= 4'h7;
									key_in <= key_r[2];
									end
					
					4'b0111 : 	begin
									key_out <= 4'ha;
									key_in <= key_r[3];
									end
				endcase
			4'b1101 :
				case(key_r)	
					4'b1110 : 	begin
									key_out <= 4'h2;
									key_in <= key_r[0];
									end
					
					4'b1101 : 	begin
									key_out <= 4'h5;
									key_in <= key_r[1];
									end
					
					4'b1011 : 	begin
									key_out <= 4'h8;
									key_in <= key_r[2];
									end
					
					4'b0111 : 	begin
									key_out <= 4'hb;
									key_in <= key_r[3];
									end
				endcase
			4'b1011 :
				case(key_r)
					4'b1110 : 	begin
									key_out <= 4'h3;
									key_in <= key_r[0];
									end
					
					4'b1101 : 	begin
									key_out <= 4'h6;
									key_in <= key_r[1];
									end
					
					4'b1011 : 	begin
									key_out <= 4'h9;
									key_in <= key_r[2];
									end
					
					4'b0111 : 	begin
									key_out <= 4'hc;
									key_in <= key_r[3];
									end
				endcase
				
			4'b0111 :
				case(key_r)
					4'b1110 : 	begin
									key_out <= 4'hf;
									key_in <= key_r[0];
									end
					
					4'b1101 : 	begin
									key_out <= 4'h0;
									key_in <= key_r[1];
									end
					
					4'b1011 : 	begin
									key_out <= 4'he;
									key_in <= key_r[2];
									end
					
					4'b0111 : 	begin
									key_out <= 4'hd;
									key_in <= key_r[3];
									end
				endcase
		endcase
endmodule	

3.4 operation module

The module is the core part of this design, which is used to realize four operations and two decimal numbers num_a,num_b as the input value of the calculator; 1010 represents addition, 1011 represents subtraction, 1100 represents multiplication and 1101 represents division. Output 14 bit binary number out_a. Because the output is binary and the number we see in the nixie tube is decimal, we need to convert the binary number into BCD code (call the binary to BCD module), and then output the converted data to the nixie tube display. The program is as follows:

/********************************************************************
*					Operation module
*	Function: calculate the two numbers entered
*	Author: Ray
*	Starting time: April 24, 2021
*	Completion time: May 26, 2021
********************************************************************/
module opera(

	// input
	input wire [13:0]num_a,
	input wire [13:0]num_b,
	input wire dis_flag,
	input wire [3:0]opera_flag,
	input wire en,
	input wire[3:0]key_num,
	
	// output
	output reg[13:0]out_a,
	output reg[13:0]out_b,
	output reg dis_flago,
	output reg [3:0]opera_flago,
	output reg out_flagd
	);
	
	always@(*)
	if(en && (key_num > 4'b1001)) begin
			if(key_num < 4'b1110)
					if(dis_flag == 1'b0) begin
							opera_flago <= key_num;
							
							out_a <= num_a;
							out_b <= num_b;
							dis_flago <= 1'b1;
					end
					else if(dis_flag == 1'b1) begin
							// addition
							if(opera_flag == 4'b1010)begin
									out_a <= num_a + num_b;
									out_b <= 14'h0;
							end
							// subtraction
							else if(opera_flag == 4'b1011) begin
									out_a <= num_a - num_b;
									out_b <= 14'h0;
							end
							// multiplication
							else if(opera_flag == 4'b1100) begin
									out_a <= num_a * num_b;
									out_b <= 14'h0;
							end
							// division
							else if(opera_flag == 4'b1101) begin
									out_a <= num_a / num_b;
									out_b <= 14'h0;
							end	
							
									opera_flago <= key_num;	
									dis_flago <= 1'b0;
					end
			
			out_flagd = 1'b1;
	end
	else begin
			out_flagd <= 1'b0;
	end
endmodule

4. Overall function commissioning

Waveform simulation is required in actual debugging. Here, we are mainly lazy and have not written test files. You can write them yourself; The debugging process is as follows: first press "12" (Figure 4-1) with the key, and then press the operator "/". After pressing the operator, the nixie tube will display "0000", then press the number key "3" (Figure 4-2), and then press the operator "/" to display the operation result (Figure 4-3).


5. Summary

The project has been packaged into github. If necessary, double-click the link directly.
https://github.com/Jeff-Ray/Calculator/tree/master

Topics: Verilog