top of page

Non pipelined processor

Have you ever wondered of building your own processor? 

Building a processor is not an easy task for sure. But we can get there with practice and efforts. 

Today, as a first step towards building our own processor, let's build a minimal version of a processor. 

​

So how do we start?

First let's understand what a non-pipelined processor is. 

Imagine you have a toy factory where you make different kinds of toys. Building a non-pipelined processor is like having a special machine that can make toys one by one.

Here's how it works:

  1. Input: You give the machine some instructions, just like telling it what kind of toy you want to make. For example, you might say, "Make a car with red color." These instructions are like a special code that the machine understands.

  2. Fetch: The machine looks at the first instruction and gets ready to do what it says. It knows that it needs to make a car with a red color.

  3. Do the Task: The machine starts making the car step by step. It takes the materials it needs, like the car body and the wheels. It puts them together and paints the car red.

  4. Finish: Once the machine is done making the car, it gives you the finished toy. You can see the red car that it made.

  5. Repeat: Now, if you want to make another toy, you give the machine a new instruction, like "Make a doll with a blue dress." The machine will follow the same steps, but this time it will make a doll instead of a car and paint it blue.

That's how a non-pipelined processor works! It takes instructions one at a time and follows them to do different tasks, just like our toy-making machine in the factory.

​

Additionally, there are two important concepts: registers and instruction set.

  1. Registers: Imagine you have a special shelf with labeled boxes in your toy factory. These boxes are called registers. Each register can store a small piece of information or data. In a non-pipelined processor, there are different types of registers that help in the toy-making process. They help in keeping track of data and instructions during the toy-making process.

  2. Instruction Set: An instruction set is like a special language that the processor understands. It tells the processor what tasks to perform and how to perform them. Just like you give instructions to the toy-making machine, you give instructions to the processor using the instruction set.

​

​

Hence, to build our own processor let's define what all we need and what tasks we need to follow.

Let's follow these steps to 

  1. Define the processor's instruction set architecture (ISA): Determine the instructions your processor will support and their encoding format.

  2. Design the processor's datapath: Identify the necessary components such as registers, ALU (Arithmetic Logic Unit), control unit, and memory interfaces. Define the input and output signals for each component.

  3. Implement the control unit: Write the control logic for decoding instructions and generating control signals for the datapath components. Use case statements or if-else statements to handle different instructions.

  4. Create the datapath modules: Implement the register file, ALU, and any other required components. Define the input and output ports for each module.

  5. Connect the datapath modules: Instantiate the modules created in the previous step and connect their input and output signals based on the processor's architecture and control signals generated by the control unit.

  6. Write the top-level module: Create a top-level module that includes the processor's instruction and data memories, along with the control unit and datapath modules. Define the input and output ports of the top-level module.

  7. Simulate and test the processor: Write testbench code to apply input stimuli and verify the correctness of the processor's behavior.

​

​

System Verilog Code for non-pipelined processor: 

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
      endcase
    end
  end

  always @(posedge clk) begin
    if (write_enable) begin
      memory[memory_addr] <= result;
    end
  end

  assign data_out = result;

endmodule

 

 

Testbench to test the non-pipelined processor code: 

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
      endcase
    end
  end

  always @(posedge clk) begin
    if (write_enable) begin
      memory[memory_addr] <= result;
    end
  end

  assign data_out = result;

endmodule
 

bottom of page