Deep Dive into the Code: Analyzing a 4-bit Microprocessor
In our previous post, we gave an overview of a 4-bit number crunching machine implemented in Verilog HDL. Now, let’s take a closer look at each module, analyzing the code and explaining its functionality in detail.
D2_register.v
The D2_register
module is a dual 4-bit register unit. It instantiates two D_register
modules, effectively creating two independent 4-bit registers.
module D2_register(clk,reseta,resetb,Ra,Rb,ena,enb,A,B);
input clk,reseta,resetb;
input [3:0] Ra,Rb;
input ena,enb;
output [3:0] A,B;
D_register RA (clk,reseta,ena,Ra,A);
D_register RB (clk,resetb,enb,Rb,B);
endmodule
Inputs:
clk
: Clock signalreseta
,resetb
: Reset signals for each registerRa
,Rb
: 4-bit input data for each registerena
,enb
: Enable signals for each register
Outputs:
A
,B
: 4-bit output data from each register
This module is useful when you need two separate but identical registers in your design, such as for storing two operands for an ALU.
D_register.v
The D_register
module implements a basic 4-bit register with synchronous reset and enable functionality.
module D_register(clk,reset,en,din,dout);
input clk, en, reset;
input[3:0] din;
output reg[3:0] dout;
always @(posedge clk,posedge reset)
begin
if (reset == 1)
dout <= 4'b0000;
else if (en)
dout <= din;
end
endmodule
Inputs:
clk
: Clock signalreset
: Asynchronous reset signalen
: Enable signaldin
: 4-bit input data
Output:
dout
: 4-bit registered output
The register updates its output on the positive edge of the clock. If reset
is high, dout
is set to 0. If en
is high and reset
is low, dout
takes the value of din
.
alu.v
The alu
(Arithmetic Logic Unit) module performs addition or subtraction on two 4-bit inputs based on a select signal.
module alu(A,B,s,Out,C);
input [3:0]A,B;
input s;
output reg C;
output reg [3:0]Out;
always@(*)
begin
if (s==1)
{C,Out}=A+(~B)+1;
if(s==0)
{C,Out}=A+B;
end
endmodule
Inputs:
A
,B
: 4-bit input operandss
: Operation select (0 for addition, 1 for subtraction)
Outputs:
Out
: 4-bit resultC
: Carry/borrow output
When s
is 0, it performs A + B. When s
is 1, it performs A - B using two's complement (A + (~B) + 1).
counter.v
The counter
module implements a 4-bit counter with reset, load, and increment functionality.
module counter(reset,clk,J,C,Cout,custom_input,count);
input reset,clk,J,C,Cout;
input [2:0] custom_input;
output reg [3:0] count;
wire [3:0] w;
wire load;
assign w[2:0] = custom_input;
assign w[3] = 0;
assign load = J | (C & Cout);
always @(posedge clk, posedge reset)
begin
if(reset == 1)
count <= 4'b0000;
else if(load)
count <= w;
else
count <= count + 4'b0001;
end
endmodule
Inputs:
reset
: Asynchronous reset signalclk
: Clock signalJ
,C
,Cout
: Control signalscustom_input
: 3-bit input for loading custom values
Output:
count
: 4-bit counter value
The counter increments on each clock cycle unless reset or load conditions are met. The load
signal is active when either J
is high or both C
and Cout
are high.
decoder.v
The decoder
module is a 2-to-4 decoder, converting a 2-bit input into a one-hot 4-bit output.
module decoder(a,b,O);
input a,b;
output reg [3:0] O;
always @(*)
begin
case({a,b})
2'b00 : O = 4'b0001;
2'b01 : O = 4'b0010;
2'b10 : O = 4'b0100;
2'b11: O = 4'b1000;
default : O = 4'b0000;
endcase
end
endmodule
Inputs:
a
,b
: 2-bit input
Output:
O
: 4-bit one-hot encoded output
It uses a case statement to determine the output based on the input combination.
display.v
The display
module is a complex circuit for driving a two-digit seven-segment display.
module display(a,b,c);
input [3:0] a;
output [6:0] b,c;
wire [3:0] w1,w2,w3;
compare c1(a,w1);
cct ct(a,w2);
mux m(a,w2,w1[0],w3);
seg s0(w3,c);
seg s1(w1,b);
endmodule
module seg(x,y);
input [3:0] x;
output reg [6:0] y;
always@(*)
case(x)
4'b0000: y = 7'b1000000;
4'b0001: y = 7'b1111001;
4'b0010: y = 7'b0100100;
4'b0011: y = 7'b0110000;
4'b0100: y = 7'b0011001;
4'b0101: y = 7'b0010010;
4'b0110: y = 7'b0000010;
4'b0111: y = 7'b1111000;
4'b1000: y = 7'b0000000;
4'b1001: y = 7'b0010000;
endcase
endmodule
module compare(a,b);
input [3:0] a;
output reg [3:0] b;
always@(*)
begin
if(a > 4'b1001)
b = 4'b0001;
else
b = 4'b0000;
end
endmodule
module mux(x,y,s,o);
input [3:0] x,y;
input s;
output reg [3:0] o;
always@(*)
begin
if(s==1)
o = y;
else
o = x;
end
endmodule
module cct(a,b);
input [3:0] a;
output reg [3:0] b;
always@(*)
b = a - 4'b1010;
endmodule
Inputs:
a
: 4-bit input value
Outputs:
b
,c
: Two 7-bit outputs for seven-segment displays
This module includes several submodules:
compare
: Checks if the input is greater than 9cct
: Subtracts 10 from the inputmux
: Selects between original and adjusted inputseg
: Converts 4-bit values to seven-segment display patterns
The circuit handles values from 0 to 15, displaying them as two digits (0–9 and 0–5) on seven-segment displays.
register_out.v
The register_out
module is a wrapper for a single 4-bit D_register, likely used for output buffering.
module register_out(clk,Ro,reseto,eno,O);
input clk,eno,reseto;
input [3:0] Ro;
output [3:0] O;
D_register RO (clk,reseto,eno,Ro,O);
endmodule
Inputs:
clk
: Clock signalRo
: 4-bit input datareseto
: Reset signaleno
: Enable signal
Output:
O
: 4-bit registered output
This module instantiates a single D_register
to create a 4-bit output register.
fourbit.v
The fourbit
module implements a 4-bit ripple carry adder using four full adder stages.
module fourbit(X,Y,Cin,Cout,Sum);
input [3:0] X,Y;
input Cin;
output [3:0] Sum;
output Cout;
wire [3:1] C;
fulladder stage0 (X[0],Y[0],Cin,C[1],Sum[0]);
fulladder stage1 (X[1],Y[1],C[1],C[2],Sum[1]);
fulladder stage2 (X[2],Y[2],C[2],C[3],Sum[2]);
fulladder stage3 (X[3],Y[3],C[3],Cout,Sum[3]);
endmodule
Inputs:
X
,Y
: 4-bit input operandsCin
: Carry input
Outputs:
Sum
: 4-bit sum outputCout
: Carry output
This module chains together four fulladder
instances, passing the carry output of each stage to the carry input of the next stage. This creates a simple but functional 4-bit adder.
fulladder.v
The fulladder
module implements a single-bit full adder.
module fulladder(x,y,cin,cout,sum);
input x,y,cin;
output sum,cout;
assign sum = x^y^cin;
assign cout = (x&y)|(x&cin)|(y&cin);
endmodule
Inputs:
x
,y
: 1-bit inputs to be addedcin
: Carry input
Outputs:
sum
: Sum outputcout
: Carry output
The module uses combinational logic to calculate the sum and carry. The sum is the XOR of all inputs, while the carry is generated if any two or more inputs are high.
initialmux.v
The initialmux
module is a 4-bit multiplexer that selects between ALU output and a custom input.
module initialmux(alu_out,custom_input,s,m_out);
input [3:0] alu_out;
input [2:0] custom_input;
input s;
output reg [3:0] m_out;
wire [3:0] w;
assign w[2:0] = custom_input;
assign w[3] = 0;
always @(*)
begin
if(s==0)
m_out = alu_out;
else
m_out = w;
end
endmodule
Inputs:
alu_out
: 4-bit input from ALUcustom_input
: 3-bit custom inputs
: Select signal
Output:
m_out
: 4-bit multiplexer output
When s
is 0, it outputs alu_out
. When s
is 1, it outputs custom_input
padded with a leading 0.
operation_alu.v
The operation_alu
module is a simple 4-bit flip-flop that captures the carry output from an ALU operation.
module operation_alu(clk,in,C); //side ff for cout
input clk;
input [3:0] in;
output reg [3:0] C;
always @(posedge clk)
begin
C <= in;
end
endmodule
Input:
clk
: Clock signalin
: 4-bit input (likely from ALU carry)
Output:
C
: 4-bit registered output
This module captures the input on the positive edge of the clock, likely used to store the carry/status from ALU operations.
eeprom.v
The eeprom
module simulates a read-only memory (ROM) for storing instructions or data.
// Quartus Prime Verilog Template
// Single Port ROM
module eeprom
#(parameter DATA_WIDTH=8, parameter ADDR_WIDTH=8)
(
input [(ADDR_WIDTH-1):0] addr,
input clk,
output reg [(DATA_WIDTH-1):0] q
);
// Declare the ROM variable
reg [DATA_WIDTH-1:0] rom[2**ADDR_WIDTH-1:0];
// Initialize the ROM with $readmemb. Put the memory contents
// in the file single_port_rom_init.txt. Without this file,
// this design will not compile.
// See Verilog LRM 1364-2001 Section 17.2.8 for details on the
// format of this file, or see the "Using $readmemb and $readmemh"
// template later in this section.
initial
begin
$readmemb("instructions.txt", rom);
end
always @ (negedge clk)
begin
q <= rom[addr];
end
endmodule
Inputs:
addr
: Address input (width parameterizable)clk
: Clock signal
Output:
q
: Data output (width parameterizable)
This module reads its contents from a file named “instructions.txt” at initialization. It then outputs the data at the specified address on each negative edge of the clock.
main.v
The main
module appears to be the top-level module of a digital system, possibly a simple processor or controller.
//module main(KEY,SW,O);
module main(KEY,SW,HEX1,HEX0);
input [1:0] KEY;
input [1:0] SW;
wire [7:0] q; //eeprom outputs
wire [3:0] w1,w2,w3,w4,w5,w6,w7,w8; //4 bit wires
wire R;
wire w9,w10; //ff
//output [3:0] O;
output [6:0] HEX1,HEX0;
assign R = SW[0];
eeprom e1(w7,KEY[0],q);
initialmux m1(w4,q[2:0],q[3],w1);
decoder d1(q[5],q[4],w6);
D_register RA (KEY[0],R,w6[0],w1,w2);
D_register RB (KEY[0],R,w6[1],w1,w3);
D_register RO (KEY[0],R,w6[2],w2,w8);
display(w8,HEX1,HEX0);
alu a1(w2,w3,q[2],w4,w9);
operation_alu f1(KEY[0],w9,w10);
counter c1(R,KEY[0],q[7],q[6],w10,q[2:0],w7);
//assign O = w8;
endmodule
Inputs:
KEY
: 2-bit input (likely from push buttons)SW
: 2-bit input (likely from switches)
Outputs:
HEX1
,HEX0
: 7-bit outputs for seven-segment displays
This module integrates various submodules including an EEPROM, ALU, registers, counter, and display driver. It implements a simple instruction execution cycle, fetching instructions from EEPROM and manipulating data through registers and the ALU.