Simple 8-bit Processor Design and Verilog implementation (Part 2)
Hi, In this article we will continue from where we left off in part1. We have already designed the ALU model and Register model which…
Simple 8-bit Processor Design and Verilog implementation (Part 2)

Hi, In this article we will continue from where we left off in part1. We have already designed the ALU model and Register model which supports add, sub, and, or, mov and loadi instructions. Now what we should do is compose a working CPU using the above models. And in this article, I will show you how to implement the instruction fetching mechanism and a program counter register that points to the next instruction. We will implement all control logic in our CPU top-level module itself.
module cpu(PC,INSTRUCTION,CLK,RESET)
As we are not implementing a memory module we will keep the instruction as an array of hardcoded instruction words (1024 bytes / 256 instructions) in the testbench file which we use to test your CPU. We will read the instruction asynchronously, based on the address provided by the PC.
To implement this we need a combination logic to decode a fetch instruction, extract the op-code, source or destination registers immediate values. We should generate all the control signals and send them to the register file, alu, and other components appropriately. We should do this according to the op-code. Bits 25:0 need to be simply sent to appropriate places where they may be used.
We have to pay close attention to how to coordinate the timing in instruction fetching, execution, and register file reading/writing. To realistically simulate the lateness of instruction fetching and decoding. To ensure that we will add these artificial timing delays in our design.
- Instruction Decode = One time unit (#1)
- PC Update = One time unit (#1)
- Instruction Memory Read = Two time units (#2)
We will start with something simple. We know that to fetch the next instruction we have to increment the PC value by 4 . To that we will implement a dedicated adder. What this will do is simply increment the PC value
//this adder is to increment pc value with 4
module adder(PCINPUT,RESULT);
input [31:0] PCINPUT;
output [31:0] RESULT;
reg RESULT;
always@(PCINPUT)
begin
RESULT = PCINPUT+ 4;
end
endmodule
I will show you a full overview of the CPU. In this diagram, we can get a full understand of the CPU.

Overview of the CPU
Next, we will talk about the timing of the CPU. we will make one clock span for a ten-time duration, rising edge to rising edge. In this case, every instruction should complete within one clock cycle. The next important part is every value to be written into registers should be ready by the rising edge. We should synchronous writing to registers and risings edge of the clock. To make our task simple we should add artificial delays to the corresponding operations for stimulation purposes. We will assume that our multiplexers and 2’s complement unit have negligible wires.

Timing Details
When we implementing the reset module if the reset signal is high when writing to PC at a positive clock edge, write -4 to PC instead of the next PC value in order to restart the program.
This is the code for the CPU module
module cpu(PC,INSTRUCTION,CLK,RESET);
input [31:0] INSTRUCTION;
input CLK;
input RESET;
output [31:0] PC;
reg [31:0] PC;
wire [31:0] PCRESULT;
reg writeEnable;
reg isAdd;
reg isImediate;
reg [2:0] aluOp;
reg [2:0] regRead1Addres;
reg [2:0] regRead2Addres;
reg [2:0] writeRegAddres;
reg [7:0] immediateVal;
wire [7:0] mux1out;
wire [7:0] mux2out;
wire [7:0] ALURESULT;
wire [7:0] minusVal;
reg [7:0] IN;
wire [7:0] OUT1;
wire [7:0] OUT2;
reg [7:0] OPCODE;
reg [2:0] DESTINATION;
reg [2:0] SOURCE1;
reg [2:0] SOURCE2;
always@(RESET)
//reseting the pc if reset is on
begin
if(RESET ==1) PC =-4;
end
//adder to update pc from 4
adder myadder(PC,PCRESULT);
always@(posedge CLK)
begin
#1
PC = PCRESULT;
end
always @(INSTRUCTION)
begin
// taking the opcode from the instruction
OPCODE = INSTRUCTION[31:24];
#1
//decodeing the opcode
case(OPCODE)
8'b00000000:
begin
writeEnable = 1'b1;
aluOp = 3'b000;
isAdd = 1'b1;
isImediate = 1'b1;
end
8'b00000001:
begin
writeEnable = 1'b1;
aluOp = 3'b000;
isAdd = 1'b1;
isImediate = 1'b0;
end
8'b00000010:
begin
writeEnable = 1'b1;
aluOp = 3'b001;
isAdd = 1'b1;
isImediate = 1'b0;
end
8'b00000011:
begin
writeEnable = 1'b1;
aluOp = 3'b001;
isAdd = 1'b0;
isImediate = 1'b0;
end
8'b00000100:
begin
writeEnable = 1'b1;
aluOp = 3'b010;
isAdd = 1'b1;
isImediate = 1'b0;
end
8'b00000101:
begin
writeEnable = 1'b1;
aluOp = 3'b011;
isAdd =1'b1;
isImediate = 1'b0;
end
endcase
end
//including the register file
reg_file myReg(IN,OUT1,OUT2,DESTINATION,SOURCE1,SOURCE2,writeEnable,CLK,RESET);
always@(INSTRUCTION)
begin
DESTINATION = INSTRUCTION[18:16];
SOURCE1 = INSTRUCTION[10:8];
SOURCE2 = INSTRUCTION[2:0];
immediateVal =INSTRUCTION[7:0];
end
//this is two's complemebt unit to substraction
twosCompliment mytwo(OUT2,minusVal);
//multiplexer to choose between minus value and plus value
mux2_1 mymux1(OUT2,minusVal,isAdd,mux1out);
//multiplexer to chose between immediate value and mux1 output
mux2_1 mymux2(immediateVal,mux1out,isImediate,mux2out);
//allu module
alu myalu(OUT1,mux2out,ALURESULT,aluOp);
always@(ALURESULT)
begin
IN =ALURESULT; //setting the reg input with the alu result
end
endmodule
This is the implementation of the multiplexer and the two’s complement modules.
//this is a implementation of a 2 to 1 multiplexer
module mux2_1(in0,in1,se1,out);
input se1; //immediae value
input [7:0] in0;
input [7:0] in1; // register output
output [7:0] out;
reg out;
always @(in0,in1,se1)
begin
if(se1==1'b1) begin
out =in0;
end
else
begin
out =in1;
end
end
endmodule
//this is used to convert numbers into minus in two's complement
module twosCompliment(in,result);
input [7:0] in;
output [7:0] result;
reg result;
always@(*)
begin
result = ~in+1;
end
endmodule
Now we have to create an appropriate test bench to test our code. As we don’t have a memory module we have to hard code the instructions in our test bench.
This is the sample test bench. I have written some machine code and hard code them into this test bench.


Congratulations! we have implemented our simple 8-bit processor. We should thoroughly test our CPU with different combinations of instructions. To make instructions we have to have good knowledge about our instruction set architecture. We have to convert assembly code into machine code.
sub 3 4 3
when we convert the above assembly code into machine code it is 32'b00000111111110110000001000000001. Likewise, we have to generate machine codes and add them to the test bench. To run this code we can use Icarus Verilog as the tool. You can find tutorials on how to download and run the tool. This machine code is generated according to the bits we allocated at the beginning of part1. If you haven’t read it, please read it first. And if you have any questions please leave a comment.
Thank You all.
Feel like you’re about to jump into a rabbit-hole of reading these incredible articles? Don’t worry, we feel the same way. Not only can you jump into the rabbit hole with us, but we’ve got more than enough articles that’ll help you jump out ;) For some of the best ideas on Medium from the youngest minds of the generation, visit students x students.
메타데이터
- post_id
- f1465be2cbe2
- slug
- simple-8-bit-processor-design-and-verilog-implementation-part-2-f1465be2cbe2
- url
- https://medium.com/@sathira97/simple-8-bit-processor-design-and-verilog-implementation-part-2-f1465be2cbe2
- canonical_url
- https://medium.com/@sathira97/simple-8-bit-processor-design-and-verilog-implementation-part-2-f1465be2cbe2
- author_url
- https://medium.com/@sathira97
- status
- ok
- fetched_at
- 2026-06-26 12:24:55