# A simple calculator based on Verilog

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

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

.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
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