fpga4student.com FPGA digital design projects using Verilog/ VHDL: Verilog code for 16-bit single cycle MIPS processor

Wednesday, January 25, 2017

Verilog code for 16-bit single cycle MIPS processor

   In this project, a 16-bit single-cycle MIPS processor is implemented in Verilog HDL. MIPS is an RISC processor, which is widely used by many universities in academic courses related to computer organization and architecture. 

The Instruction Format and Instruction Set Architecture for the 16-bit single-cycle MIPS are as follows:
Verilog code for 16 bit MIPS CPU
Instruction set for the MIPS processor
Verilog code for MIPS processor
Instruction Set Architecture for the MIPS processor
     Below is the description for instructions being implemented in Verilog:

  1. Add : R[rd] = R[rs] + R[rt]
  2. Subtract : R[rd] = R[rs] - R[rt]
  3. And: R[rd] = R[rs] & R[rt]
  4. Or : R[rd] = R[rs] | R[rt]
  5. SLT: R[rd] = 1 if R[rs] <  R[rt] else 0
  6. Jr: PC=R[rs]
  7. Lw: R[rt] = M[R[rs]+SignExtImm]
  8. Sw : M[R[rs]+SignExtImm] = R[rt]
  9. Beq : if(R[rs]==R[rt]) PC=PC+1+BranchAddr
  10. Addi: R[rt] = R[rs] + SignExtImm
  11. J : PC=JumpAddr
  12. Jal : R[7]=PC+2;PC=JumpAddr
  13. SLTI: R[rt] = 1 if R[rs] < imm else 0
SignExtImm = { 9{immediate[6]}, imm
JumpAddr =    { (PC+1)[15:13], address}
BranchAddr = { 7{immediate[6]}, immediate, 1’b0 }
Based on the provided instruction set, the data-path and control unit are designed and implemented.
Control unit design:

Control signals
Instruction
Reg
Dst
ALUSrc
Memto
Reg
Reg
Write
MemRead
Mem
Write
Branch
ALUOp
Jump
R-type
1
0
0
1
0
0
0
00
0
LW
0
1
1
1
1
0
0
11
0
SW
0
1
0
0
0
1
0
11
0
addi
0
1
0
1
0
0
0
11
0
beq
0
0
0
0
0
0
1
01
0
j
0
0
0
0
0
0
0
00
1
jal
2
0
2
1
0
0
0
00
1
slti
0
1
0
1
0
0
0
10
0


ALU Control
ALU op
Function
ALUcnt
ALU Operation
Instruction
11
xxxx
000
ADD
Addi,lw,sw
01
xxxx
001
SUB
BEQ
00
00
000
ADD
R-type: ADD
00
01
001
SUB
R-type: sub
00
02
010
AND
R-type: AND
00
03
011
OR
R-type: OR
00
04
100
slt
R-type: slt
10
xxxxxx
100
slt
i-type: slti

Verilog code for mips processor
Data-path and control unit of the 16-bit MIPS processor
After completing the design for the MIPS processor, it is easy to write Verilog code for the MIPS processor. The Verilog code for the whole design of the MIPS processor as follows:
Verilog code for ALU unit
Verilog code for register file
Verilog code for instruction memory
Verilog code for data memory:
 `timescale 1ns / 1ps  
 module data_memory  //fpga4student.com: FPga projects, Verilog projects, VHDL projects
 (  
      input                         clk,  
      // address input, shared by read and write port  
      input     [15:0]               mem_access_addr,  
      // write port  
      input     [15:0]               mem_write_data,  
      input                         mem_write_en,  
      input mem_read,  
      // read port  
      output     [15:0]               mem_read_data  
 );  
      integer i;  
      reg [15:0] ram [255:0];  
      wire [7 : 0] ram_addr = mem_access_addr[9 : 2];  
      initial begin  
           for(i=0;i<256;i=i+1)  
                ram[i] <= 16'd0;  
      end  
      always @(posedge clk) begin  
           if (mem_write_en)  
                ram[ram_addr] <= mem_write_data;  
      end  
      assign mem_read_data = (mem_read==1'b1) ? ram[ram_addr]: 16'd0;   
 endmodule   
Verilog code for ALU Control unit:
 `timescale 1ns / 1ps  //fpga4student.com: FPga projects, Verilog projects, VHDL projects
 module ALUControl( ALU_Control, ALUOp, Function);  
 output reg[2:0] ALU_Control;  
 input [1:0] ALUOp;  
 input [3:0] Function;  
 wire [5:0] ALUControlIn;  
 assign ALUControlIn = {ALUOp,Function};  
 always @(ALUControlIn)  
 casex (ALUControlIn)  
  6'b11xxxx: ALU_Control=3'b000;  
  6'b10xxxx: ALU_Control=3'b100;  
  6'b01xxxx: ALU_Control=3'b001;  
  6'b000000: ALU_Control=3'b000;  
  6'b000001: ALU_Control=3'b001;  
  6'b000010: ALU_Control=3'b010;  
  6'b000011: ALU_Control=3'b011;  
  6'b000100: ALU_Control=3'b100;  
  default: ALU_Control=3'b000;  
  endcase  
 endmodule  
Verilog code for control unit:
 `timescale 1ns / 1ps  //fpga4student.com: FPga projects, Verilog projects, VHDL projects
 module control( input[2:0] opcode,  
                           input reset,  
                           output reg[1:0] reg_dst,mem_to_reg,alu_op,  
                           output reg jump,branch,mem_read,mem_write,alu_src,reg_write,sign_or_zero                      
   );  
 always @(*)  
 begin  
      if(reset == 1'b1) begin  
                reg_dst = 2'b00;  
                mem_to_reg = 2'b00;  
                alu_op = 2'b00;  
                jump = 1'b0;  
                branch = 1'b0;  
                mem_read = 1'b0;  
                mem_write = 1'b0;  
                alu_src = 1'b0;  
                reg_write = 1'b0;  
                sign_or_zero = 1'b1;  
      end  
      else begin  
      case(opcode)   
      3'b000: begin // add  
                reg_dst = 2'b01;  
                mem_to_reg = 2'b00;  
                alu_op = 2'b00;  
                jump = 1'b0;  
                branch = 1'b0;  
                mem_read = 1'b0;  
                mem_write = 1'b0;  
                alu_src = 1'b0;  
                reg_write = 1'b1;  
                sign_or_zero = 1'b1;  
                end  
      3'b001: begin // sli  
                reg_dst = 2'b00;  
                mem_to_reg = 2'b00;  
                alu_op = 2'b10;  
                jump = 1'b0;  
                branch = 1'b0;  
                mem_read = 1'b0;  
                mem_write = 1'b0;  
                alu_src = 1'b1;  
                reg_write = 1'b1;  
                sign_or_zero = 1'b0;  
                end  
      3'b010: begin // j  
                reg_dst = 2'b00;  
                mem_to_reg = 2'b00;  
                alu_op = 2'b00;  
                jump = 1'b1;  
                branch = 1'b0;  
                mem_read = 1'b0;  
                mem_write = 1'b0;  
                alu_src = 1'b0;  
                reg_write = 1'b0;  
                sign_or_zero = 1'b1;  
                end  
      3'b011: begin // jal  
                reg_dst = 2'b10;  
                mem_to_reg = 2'b10;  
                alu_op = 2'b00;  
                jump = 1'b1;  
                branch = 1'b0;  
                mem_read = 1'b0;  
                mem_write = 1'b0;  
                alu_src = 1'b0;  
                reg_write = 1'b1;  
                sign_or_zero = 1'b1;  
                end  
      3'b100: begin // lw  
                reg_dst = 2'b00;  
                mem_to_reg = 2'b01;  
                alu_op = 2'b11;  
                jump = 1'b0;  
                branch = 1'b0;  
                mem_read = 1'b1;  
                mem_write = 1'b0;  
                alu_src = 1'b1;  
                reg_write = 1'b1;  
                sign_or_zero = 1'b1;  
                end  
      3'b101: begin // sw  
                reg_dst = 2'b00;  
                mem_to_reg = 2'b00;  
                alu_op = 2'b11;  
                jump = 1'b0;  
                branch = 1'b0;  
                mem_read = 1'b0;  
                mem_write = 1'b1;  
                alu_src = 1'b1;  
                reg_write = 1'b0;  
                sign_or_zero = 1'b1;  
                end  
      3'b110: begin // beq  
                reg_dst = 2'b00;  
                mem_to_reg = 2'b00;  
                alu_op = 2'b01;  
                jump = 1'b0;  
                branch = 1'b1;  
                mem_read = 1'b0;  
                mem_write = 1'b0;  
                alu_src = 1'b0;  
                reg_write = 1'b0;  
                sign_or_zero = 1'b1;  
                end  
      3'b111: begin // addi  
                reg_dst = 2'b00;  
                mem_to_reg = 2'b00;  
                alu_op = 2'b11;  
                jump = 1'b0;  
                branch = 1'b1;  
                mem_read = 1'b0;  
                mem_write = 1'b0;  
                alu_src = 1'b1;  
                reg_write = 1'b1;  
                sign_or_zero = 1'b1;  
                end  
      default: begin  
                reg_dst = 2'b01;  
                mem_to_reg = 2'b00;  
                alu_op = 2'b00;  
                jump = 1'b0;  
                branch = 1'b0;  
                mem_read = 1'b0;  
                mem_write = 1'b0;  
                alu_src = 1'b0;  
                reg_write = 1'b1;  
                sign_or_zero = 1'b1;  
                end  
      endcase  
      end  
 end  
 endmodule  
Verilog code for the single-cycle MIPS processor:
 `timescale 1ns / 1ps  
 // fpga4student.com: FPga projects, Verilog projects, VHDL projects
 // Verilog code for 16 bit single cycle MIPS CPU  
 module mips_16( input clk,reset,  
                           output[15:0] pc_out, alu_result,reg3,reg4  
   );  
 reg[15:0] pc_current;  
 wire signed[15:0] pc_next,pc2;  
 wire [15:0] instr;  
 wire[1:0] reg_dst,mem_to_reg,alu_op;  
 wire jump,branch,mem_read,mem_write,alu_src,reg_write     ;  
 wire     [2:0]     reg_write_dest;  
 wire     [15:0] reg_write_data;  
 wire     [2:0]     reg_read_addr_1;  
 wire     [15:0] reg_read_data_1;  
 wire     [2:0]     reg_read_addr_2;  
 wire     [15:0] reg_read_data_2;  
 wire [15:0] sign_ext_im,read_data2,zero_ext_im,imm_ext;  
 wire JRControl;  
 wire [2:0] ALU_Control;  
 wire [15:0] ALU_out;  
 wire zero_flag;  
 wire signed[15:0] im_shift_1, PC_j, PC_beq, PC_4beq,PC_4beqj,PC_jr;  
 wire beq_control;  
 wire [14:0] jump_shift_1;  
 wire [15:0]mem_read_data;  
 wire [15:0] no_sign_ext;  
 wire sign_or_zero;  
 // PC   
 always @(posedge clk or posedge reset)  
 begin   
      if(reset)   
           pc_current <= 16'd0;  
      else  
           pc_current <= pc_next;  
 end  
 // PC + 2   
 assign pc2 = pc_current + 16'd2;  
 // instruction memory  
 instr_mem instrucion_memory(.pc(pc_current),.instruction(instr));  
 // jump shift left 1  
 assign jump_shift_1 = {instr[13:0],1'b0};  
 // control unit  
 control control_unit(.reset(reset),.opcode(instr[15:13]),.reg_dst(reg_dst)  
                ,.mem_to_reg(mem_to_reg),.alu_op(alu_op),.jump(jump),.branch(branch),.mem_read(mem_read),  
                .mem_write(mem_write),.alu_src(alu_src),.reg_write(reg_write),.sign_or_zero(sign_or_zero));  
 // multiplexer regdest  
 assign reg_write_dest = (reg_dst==2'b10) ? 3'b111: ((reg_dst==2'b01) ? instr[6:4] :instr[9:7]);  
 // register file  
 assign reg_read_addr_1 = instr[12:10];  
 assign reg_read_addr_2 = instr[9:7];  
 register_file reg_file(.clk(clk),.rst(reset),.reg_write_en(reg_write),  
 .reg_write_dest(reg_write_dest),  
 .reg_write_data(reg_write_data),  
 .reg_read_addr_1(reg_read_addr_1),  
 .reg_read_data_1(reg_read_data_1),  
 .reg_read_addr_2(reg_read_addr_2),  
 .reg_read_data_2(reg_read_data_2),  
 .reg3(reg3),  
 .reg4(reg4));  
 // sign extend  
 assign sign_ext_im = {{9{instr[6]}},instr[6:0]};  
 assign zero_ext_im = {{9{1'b0}},instr[6:0]};  
 assign imm_ext = (sign_or_zero==1'b1) ? sign_ext_im : zero_ext_im;  
 // JR control  
 JR_Control JRControl_unit(.alu_op(alu_op),.funct(instr[3:0]),.JRControl(JRControl));       
 // ALU control unit  
 ALUControl ALU_Control_unit(.ALUOp(alu_op),.Function(instr[3:0]),.ALU_Control(ALU_Control));  
 // multiplexer alu_src  
 assign read_data2 = (alu_src==1'b1) ? imm_ext : reg_read_data_2;  
 // ALU   
 alu alu_unit(.a(reg_read_data_1),.b(read_data2),.alu_control(ALU_Control),.result(ALU_out),.zero(zero_flag));  
 // immediate shift 1  
 assign im_shift_1 = {imm_ext[14:0],1'b0};  
 //  
 assign no_sign_ext = ~(im_shift_1) + 1'b1;  
 // PC beq add  
 assign PC_beq = (im_shift_1[15] == 1'b1) ? (pc2 - no_sign_ext): (pc2 +im_shift_1);  
 // beq control  
 assign beq_control = branch & zero_flag;  
 // PC_beq  
 assign PC_4beq = (beq_control==1'b1) ? PC_beq : pc2;  
 // PC_j  
 assign PC_j = {pc2[15],jump_shift_1};  
 // PC_4beqj  
 assign PC_4beqj = (jump == 1'b1) ? PC_j : PC_4beq;  
 // PC_jr  
 assign PC_jr = reg_read_data_1;  
 // PC_next  
 assign pc_next = (JRControl==1'b1) ? PC_jr : PC_4beqj;  
 // data memory  
 data_memory datamem(.clk(clk),.mem_access_addr(ALU_out),  
 .mem_write_data(reg_read_data_2),.mem_write_en(mem_write),.mem_read(mem_read),  
 .mem_read_data(mem_read_data));  
 // write back  
 assign reg_write_data = (mem_to_reg == 2'b10) ? pc2:((mem_to_reg == 2'b01)? mem_read_data: ALU_out);  
 // output  
 assign pc_out = pc_current;  
 assign alu_result = ALU_out;  
 endmodule  
Verilog testbench code for the single-cycle MIPS processor:
 `timescale 1ns / 1ps  
 module tb_mips16;  //fpga4student.com: FPga projects, Verilog projects, VHDL projects
      // Inputs  
      reg clk;  
      reg reset;  
      // Outputs  
      wire [15:0] pc_out;  
      wire [15:0] alu_result,reg3,reg4;  
      // Instantiate the Unit Under Test (UUT)  
      mips_16 uut (  
           .clk(clk),   
           .reset(reset),   
           .pc_out(pc_out),   
           .alu_result(alu_result),  
           .reg3(reg3),  
           .reg4(reg4)  
      );  
      initial begin  
           clk = 0;  
           forever #10 clk = ~clk;  
      end  
      initial begin  
           // Initialize Inputs  
           $monitor ("register 3=%d, register 4=%d", reg3,reg4);  
           reset = 1;  
           // Wait 100 ns for global reset to finish  
           #100;  
     reset = 0;  
           // Add stimulus here  
      end  
 endmodule  
   It is quite simple to verify the Verilog code for the single-cycle MIPS CPU by doing several simulations on ModelSim or Xilinx ISIM in order to see how the MIPS processor works. To fully verify the MIPS processor, it is needed to modify the instruction memory to simulate all the instructions in the instruction set architecture, and then check simulation waveform and memory to see if the processor works correctly as designed. 
Plate License Recognition Verilog/Matlab Implementation on FPGA Xilinx Spartan-6

8 comments:

  1. May i check with you what type of stimulus do i have to include in the test bench codes. I ran the whole program with the testbench for 100ns but the register values are 0 or x

    ReplyDelete
  2. Double check your instruction memory. All instructions here are fully verified.
    You can check instruction by instruction and see register files, data memory to verify.

    ReplyDelete
  3. Ok thanks for your advice. And i have another problem, when i compile the program, i have this error:
    Error (12006): Node instance "JR_Control_unit" instantiates undefined entity "JR_Control"

    Anybody knows how to solve it?

    ReplyDelete
  4. module JR_Control( input[1:0] alu_op,
    input [3:0] funct,
    output JRControl
    );
    assign JRControl = ({alu_op,funct}==6'b001000) ? 1'b1 : 1'b0;

    endmodule
    Here it is

    ReplyDelete
    Replies
    1. Thank you so much for your help!

      Delete
  5. How do i implement a pipeline structure into a mips processor? Do i just implement a register in between individual stages? If so where do i implement them.

    ReplyDelete
  6. There will be a pipe-lined MIPS in Verilog post soon.

    ReplyDelete
  7. can anybdy please explain the working, i shall be very greatful..plz

    ReplyDelete

FPGA Courses for Beginners

Programming Category (English)300x250 Mobile Apps Category (English)300x250