all about vlsi DV
Non pipelined processor
In the last tutorial, we created a non-pipelined processor with basic operations. Let's enhance it today to add more operations to it.
We will be adding following operations:
-
AND: The AND operation takes two inputs, regA and regB, performs a bitwise AND operation, and stores the result in the result register. It can either take data_in as regB or fetch the value from memory based on the instruction.
-
OR: The OR operation takes two inputs, regA and regB, performs a bitwise OR operation, and stores the result in the result register. It can either take data_in as regB or fetch the value from memory based on the instruction.
-
XOR: The XOR operation takes two inputs, regA and regB, performs a bitwise XOR operation, and stores the result in the result register. It can either take data_in as regB or fetch the value from memory based on the instruction.
-
SHIFT LEFT and SHIFT RIGHT: These operations shift the bits in the regA register either to the left or to the right by one position. The shifted result is stored in the result register.
​
Let's get straight to coding it:
SV code for design:
module NonPipelinedProcessor (
input wire clk,
input wire reset,
input wire [7:0] instruction,
input wire [7:0] data_in,
output reg [7:0] data_out
);
// Internal registers
reg [7:0] regA;
reg [7:0] regB;
reg [7:0] result;
reg [7:0] memory [0:255];
// Control signals
reg write_enable;
reg [7:0] memory_addr;
always @(posedge clk or posedge reset) begin
if (reset) begin
// Reset processor state
regA <= 0;
regB <= 0;
result <= 0;
write_enable <= 0;
memory_addr <= 0;
end
else begin
// Fetch instruction
regA <= instruction;
// Decode and execute instructions
case (instruction[7:6])
2'b00: begin // ADD
regB <= instruction[5:4] ? data_in : memory[instruction[3:0]];
result <= regA + regB;
end
2'b01: begin // SUB
regB <= instruction[5:4] ? data_in : memory[instruction[3:0]];
result <= regA - regB;
end
2'b10: begin // STORE
memory_addr <= instruction[3:0];
write_enable <= 1;
result <= data_in;
end
2'b11: begin // LOAD
regB <= memory[instruction[3:0]];
result <= regB;
end
2'b10: begin // AND
regB <= instruction[5:4] ? data_in : memory[instruction[3:0]];
result <= regA & regB;
end
2'b11: begin // OR
regB <= instruction[5:4] ? data_in : memory[instruction[3:0]];
result <= regA | regB;
end
2'b10: begin // XOR
regB <= instruction[5:4] ? data_in : memory[instruction[3:0]];
result <= regA ^ regB;
end
2'b11: begin // SHIFT LEFT
result <= regA << 1;
end
2'b10: begin // SHIFT RIGHT
result <= regA >> 1;
end
// Add more operations here as needed
endcase
end
end
always @(posedge clk) begin
if (write_enable) begin
memory[memory_addr] <= result;
end
end
assign data_out = result;
endmodule
​
​
Now let's write a testbench for the processor:
module NonPipelinedProcessor_Testbench;
reg clk;
reg reset;
reg [7:0] instruction;
reg [7:0] data_in;
wire [7:0] data_out;
NonPipelinedProcessor dut (
.clk(clk),
.reset(reset),
.instruction(instruction),
.data_in(data_in),
.data_out(data_out)
);
initial begin
clk = 0;
reset = 1;
instruction = 8'h00; // Set initial instruction
#10 reset = 0; // De-assert reset
#5 instruction = 8'h24; // ADD regA, regB, regC (regA = regB + regC)
#5 data_in = 8'h0A; // regB = 10
#5 data_in = 8'h05; // regC = 5
#5 data_in = 8'h00; // regC not used in this instruction
#10 instruction = 8'h36; // STORE regA, [address]
#5 data_in = 8'h07; // Store regA value to memory address 7
#10 instruction = 8'h1B; // SUB regA, regB, regC (regA = regB - regC)
#5 data_in = 8'h0A; // regB = 10
#5 data_in = 8'h02; // regC = 2
#5 data_in = 8'h00; // regC not used in this instruction
#10 instruction = 8'h3D; // LOAD regA, [address]
#5 data_in = 8'h07; // Load memory value at address 7 to regA
// Expected results:
// regA = 10 + 5 = 15 (after ADD instruction)
// memory[7] = 15 (after STORE instruction)
// regA = 10 - 2 = 8 (after SUB instruction)
// regA = 15 (after LOAD instruction)
// Verify results
#5 if (data_out === 8'h0F) $display("ADD instruction: Passed");
else $display("ADD instruction: Failed");
#5 if (memory[7] === 8'h0F) $display("STORE instruction: Passed");
else $display("STORE instruction: Failed");
#5 if (data_out === 8'h08) $display("SUB instruction: Passed");
else $display("SUB instruction: Failed");
#5 if (data_out === 8'h0F) $display("LOAD instruction: Passed");
else $display("LOAD instruction: Failed");
// Additional operations
#10 instruction = 8'h52; // AND regA, regB, regC (regA = regB & regC)
#5 data_in = 8'h0F; // regB = 15
#5 data_in = 8'h05; // regC = 5
#5 data_in = 8'h00; // regC not used in this instruction
#10 instruction = 8'h73; // OR regA, regB, regC (regA = regB | regC)
#5 data_in = 8'h0A; // regB = 10
#5 data_in = 8'h03; // regC = 3
#5 data_in = 8'h00; // regC not used in this instruction
#10 instruction = 8'h9E; // XOR regA, regB, regC (regA = regB ^ regC)
#5 data_in = 8'hF0; // regB = 240
#5 data_in = 8'h0F; // regC = 15
#5 data_in = 8'h00; // regC not used in this instruction
#10 instruction = 8'hBF; // SHIFT LEFT regA
#10 instruction = 8'hA0; // SHIFT RIGHT regA
// Expected results:
// regA = 15 & 5 = 5 (after AND instruction)
// regA = 10 | 3 = 11 (after OR instruction)
// regA = 240 ^ 15 = 255 (after XOR instruction)
// regA = 510 (after SHIFT LEFT and SHIFT RIGHT instructions)
// Verify results
#5 if (data_out === 8'h05) $display("AND instruction: Passed");
else $display("AND instruction: Failed");
#5 if (data_out === 8'h0B) $display("OR instruction: Passed");
else $display("OR instruction: Failed");
#5 if (data_out === 8'hFF) $display("XOR instruction: Passed");
else $display("XOR instruction: Failed");
#5 if (data_out === 8'hFE) $display("SHIFT LEFT and SHIFT RIGHT instructions: Passed");
else $display("SHIFT LEFT and SHIFT RIGHT instructions: Failed");
$finish;
end
always #5 clk = ~clk;
endmodule
This was a very simple exercise and now we can be proud that we have created our own first processor.
In the next tutorial, we will try to add driver, agent and other components in the same code.
Till then, Happy Coding!!