DEPARTMENT OF ELECTRONICS ENGINEERING
SHRI RAMDEOBABA COLLEGE OF ENGINEERING AND MANAGEMENT,
(An Autonomous Institution permanently affiliated to Rashtrasant Tukadoji Maharaj Nagpur University,
Nagpur)
Computer Architecture and Organization Laboratory (ENP360)
Session: 2020-21(EVEN)
Project report on
“16 bit Single Cycle MIPS CPU Design and Implementation on FPGA ”
Submitted by: Juhi Panname
Class and Roll No.: VI-C-17
Objective: Design a 16 bit Single Cycle MIPS CPU with following Instruction encoding and ISA
support.
Below is the description for instructions being supported by SCCPU:
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 Synthesis
tool : Xilinx Vivado 2016.2
Verilog code for the MIPS processor. The Verilog code for the whole design of the
MIPS processor as follows:
Verilog code for data memory:
module data_memory
(
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[8 : 1]; 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 instruction memory:
// Verilog code for instruction memory module instr_mem //
a synthesisable rom implementation
(
input [15:0] pc,
output wire [15:0] instruction
);
wire [3 : 0] rom_addr = pc[4 :
1]; /* lw $3, 0($0) -- Loop: slti
$1, $3, 50 beq $1, $0, Skip add
$4, $4, $3 addi $3, $3, 1 beq
$0,
$0, Loop-- Skip
*/
reg [15:0] rom[15:0];
initial
begin rom[0] = 16'b1000000110000000; rom[1]
= 16'b0010110010110010; rom[2] =
16'b1101110001100111; rom[3] =
16'b1101110111011001; rom[4] =
16'b1111110110110001; rom[5] =
16'b1100000001111011; rom[6] =
16'b0000000000000000; rom[7] =
16'b0000000000000000; rom[8] =
16'b0000000000000000; rom[9] =
16'b0000000000000000; rom[10] =
16'b0000000000000000; rom[11]
= 16'b0000000000000000;
rom[12] =
16'b0000000000000000; rom[13]
= 16'b0000000000000000;
rom[14] =
16'b0000000000000000; rom[15]
= 16'b0000000000000000; end
assign instruction = (pc[15:0] < 32 )? rom[rom_addr[3:0]]: 16'd0;
endmodule
Verilog code for register file:
// Verilog code for register file module
register_file
(
input clk,
input rst,
// write port
input reg_write_en,
input [2:0] reg_write_dest, input
[15:0] reg_write_data,
//read port 1
input [2:0] reg_read_addr_1, output
[15:0] reg_read_data_1,
//read port 2
input [2:0] reg_read_addr_2, output
[15:0] reg_read_data_2
);
reg [15:0] reg_array [7:0];
// write port //reg
[2:0] i;
always @ (posedge clk or posedge rst)
begin if(rst) begin reg_array[0] <= 16'b0;
reg_array[1] <= 16'b0; reg_array[2] <=
16'b0; reg_array[3] <= 16'b0; reg_array[4]
<= 16'b0; reg_array[5] <= 16'b0;
reg_array[6] <= 16'b0; reg_array[7] <=
16'b0;
end else begin if(reg_write_en) begin
reg_array[reg_write_dest] <= reg_write_data; end
end end assign reg_read_data_1 =
( reg_read_addr_1
== 0)? 16'b0 :
reg_array[reg_read_addr_1]; assign reg_read_data_2 = == 0)? 16'b0 :
( reg_read_addr_2
reg_array[reg_read_addr_2];
endmodule
Control Unit in Verilog:
// Submodule: Control Unit in Verilog module control( input[2:0] reg
opcode, input reset, output reg[1:0]
reg_dst,mem_to_reg,alu_op,
jump,branch,mem_read,mem_write,alu_src,reg_write,sign_or_zero
); output
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'b0; 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 ALU:
// Verilog code for ALU module alu( input [15:0] a,
//src1 input [15:0] b,
//src2
input [2:0] alu_control, //function sel
output reg [15:0] result, //result output zero
);
always @(*) begin
case(alu_control)
3'b000: result = a + b; // add 3'b001:
result = a - b; // sub 3'b010: result =
a & b; // and
3'b011: result = a | b; // or
3'b100: begin if (a<b) result = 16'd1; else result =
16'd0; end
default:result = a + b; // add
endcase
end
assign zero = (result==16'd0) ? 1'b1: 1'b0; endmodule
ALU Control Unit in Verilog
// Submodule: ALU Control Unit in Verilog 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 JR control unit 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
Verilog code for 16 bit single cycle MIPS CPU:
// 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_zer
o(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
ALUContro
l 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),.res
ult(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_r
ead),
.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
Testbench
`timescale 1ns / 1ps
module tb_mips16; // 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
SC-CPU along with Data-path and control. The ISA support is given in above table
Output
Important term
1] ISA (instruction set architecture)
ISA decides how to represent an instruction of a processor in Instruction memory, how the
instruction is oriented, what is the length of an instruction, in which way the instruction should be
decoded so that the CPU gets appropriate data.
2] Arithmetic & Logic Unit (ALU):
It is a very important hardware component of central processing unit of a computer. ALU is the
main hardware part where the operations get executed.
3] Register File:
In processor, ALU gets data from a Register Bank / Register File. As we have designed our
ISA, our register file should have 16 registers. And the design should be easy enough to choose
rs, rt, rd values.
4] Control Unit
Control unit is the circuit in processor which controls the flow of data and operation of ALU.
Initially an instruction is loaded in instruction memory. According to Opcode of that instruction,
control unit gets understood what the instruction wants to execute. Then it flows the data in
such a path so that ALU gets necessary data to execute.
5] Single cycle processor
Single cycle processor is a processor that carries out 1 instruction in a single clock cycle.
6] FPGA
Field Programmable Gate Arrays (FPGAs) are semiconductor devices that are
based around a matrix of configurable logic blocks (CLBs) connected via
programmable interconnects. FPGAs can be reprogrammed to desired application or
functionality requirements after manufacturing.
Program to calculate Fibonacci series
Fibonacci Series - Fibonacci series is a series of numbers, where the next number is
found by adding up the 2 number beore it.
The Fibonacci numbers are the numbers in the following integer sequence.
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ……..
module fib(input clock, reset, input [5:0] n, output reg ready, output [15:0] value);
reg [15:0] previous, current; reg [5:0] counter;
// Reset the circuit
always @(posedge
reset) begin previous <=
16'd0; current <= 16'd1;
counter <= 16'd1; end
// Compute next Fibonacci
number always @(posedge
clock) begin
// Increment current index counter
<= counter + 1;
// Efficient adders are automatically
inferred current <= current + previous;
previous <= current;
if (counter == n) ready
<= 1;
end
// Read the value of the nth fibonacci number from the internal register assign
value = current;
endmodule
Output
Result
In this report, a 16-bit single-cycle MIPS processor is implemented in Verilog HDL.