Build a 4-bit MicroProcessor: A Verilog HDL Project
In the realm of digital logic design, creating a functional microprocessor from scratch is a challenging yet rewarding endeavor. This post explores a project that implements a 4-bit number crunching machine (or a simple 4-bit microprocessor) using Verilog HDL. We’ll dissect the various components, understand their roles, and see how they come together to form a working system.
Project Overview
The heart of this project is a custom Instruction Set Architecture (ISA) implemented through various digital logic components. The system is designed to perform basic arithmetic and logical operations, store data, and display results. Let’s break down the main components:
- Arithmetic Logic Unit (ALU)
- Data Registers and Control Logic
- 4-bit D-type Registers
- 4-bit Binary Full Adder
- 4-bit Binary Counter
- Quad 2-Data Selectors (Multiplexers)
- 2-Line to 4-Line Decoder
- Basic Logic Gates (AND, OR, NOT, XOR)
- Parallel Address, Parallel 8-bit I/O EEPROM
Deep Dive into Components
1. Arithmetic Logic Unit (ALU)
The ALU is the computational core of our system. It’s defined in the alu
module (which isn't provided in the given files, but we can infer its existence from the main.v
file). The ALU takes two 4-bit inputs and performs operations based on control signals.
2. Data Registers
The system uses several D-type registers to store data. These are implemented in the D_register
module (also not provided, but referenced in main.v
). These registers store the ALU inputs, outputs, and intermediate results.
3. 4-bit Binary Full Adder
The full adder is a crucial component for arithmetic operations. It’s implemented in the fourbit.v
and fulladder.v
files:
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
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
This implementation cascades four 1-bit full adders to create a 4-bit adder, demonstrating how larger components can be built from smaller ones.
4. 4-bit Binary Counter
The counter is used to keep track of the current instruction or memory address. It’s implemented in the counter
module (referenced in main.v
but not provided).
5. Multiplexers (Quad 2-Data Selectors)
Multiplexers are used to select between different data sources. The initialmux
module is an example:
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
This multiplexer chooses between the ALU output and a custom input based on a select signal.
6. 2-Line to 4-Line Decoder
The decoder is used for instruction decoding or register selection. It’s implemented in the decoder
module:
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
This decoder takes a 2-bit input and activates one of four output lines, useful for selecting registers or operations.
7. EEPROM
The EEPROM serves as the instruction memory for our system. It’s implemented in the eeprom
module:
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
);
reg [DATA_WIDTH-1:0] rom[2**ADDR_WIDTH-1:0];
initial
begin
$readmemb("instructions.txt", rom);
end
always @ (negedge clk)
begin
q <= rom[addr];
end
endmodule
This module reads instructions from a file and outputs them based on the provided address.
8. Display
The system includes a display module to visualize the output:
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
This module converts the 4-bit output to 7-segment display format, likely for use with LED displays.
Putting It All Together
The main
module in main.v
ties all these components together:
module main(KEY,SW,HEX1,HEX0);
input [1:0] KEY;
input [1:0] SW;
wire [7:0] q;
wire [3:0] w1,w2,w3,w4,w5,w6,w7,w8;
wire R;
wire w9,w10;
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);
endmodule
This module orchestrates the operation of the entire system. It connects the EEPROM, ALU, registers, counter, and display components, creating a functional 4-bit number crunching machine.
Conclusion
This project demonstrates the power of Verilog HDL in creating complex digital systems from basic components. By understanding each module and how they interact, we gain insight into the workings of simple processors. This knowledge forms a solid foundation for more advanced digital design projects and helps bridge the gap between hardware description languages and actual computer architecture.
The modular nature of this design also showcases good engineering practices, allowing for easy debugging, testing, and potential future expansions. Whether you’re a student learning digital logic design or an experienced engineer looking to refresh your knowledge, projects like these provide valuable hands-on experience in the fascinating world of digital systems design.