Last active
January 6, 2025 14:55
-
-
Save kevinzhang96/c066e62a8d86325414fc to your computer and use it in GitHub Desktop.
multicycle mips processor verilog implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`include "alu_defines.v" | |
`include "mips_defines.v" | |
`include "mips_multicycle_defines.v" | |
`include "mips_memory_space_defines.v" | |
//`define VERBOSE | |
`default_nettype none | |
`timescale 1ns/1ps | |
/* | |
a version of the mips multicycle module with von Nuemann architecture | |
*/ | |
module mips_multicycle_vn(clk, rst, ena, mem_addr, mem_rd_data, mem_wr_data, mem_wr_ena, PC, full_register_file); | |
parameter N = 32; | |
input wire clk, rst, ena; | |
`include "mips_helper.v" | |
/* ---- instantiate main modules ---- */ | |
// the following are signals that are critical for the operation of your CPU | |
// note: these are all reg type so that you can easily drive combinational logic | |
// through always@(*) blocks. Not all of these need to be sequential. | |
reg [3:0] state, next_state; //state is register, next_state driven comb. | |
output reg [31:0] PC; //the program counter | |
//memory signals | |
output reg [N-1:0] mem_addr, mem_wr_data; | |
reg [N-1:0] mem_wr_addr, mem_rd_addr; | |
input wire [N-1:0] mem_rd_data; | |
output reg mem_wr_ena; | |
// CUT<<< | |
// data memory signals are an artifact of an earlier Harvard-architecture implementation, | |
// but I found it useful to abstract away fetch instruction signals from | |
// general purpose memory IO. The following comb. logic is OPTIONAL - you could also | |
// just drive the mem_* signals above directly | |
reg [N-1:0] dmem_wr_addr, dmem_rd_addr; | |
reg dmem_wr_ena; | |
always @(*) begin | |
if( (state == `S_FETCH1) || (state == `S_FETCH2) ) begin | |
mem_addr = PC; | |
mem_wr_ena = 0; | |
end | |
else begin //assume data access | |
mem_wr_ena = dmem_wr_ena; | |
mem_addr = dmem_wr_ena ? dmem_wr_addr : dmem_rd_addr; | |
end | |
end | |
//>>> | |
// the following are signals that should prove helpful in implementing your CPU | |
// but are not strictly required. Feel free to modify/disregard these if your | |
// particular implementation differs | |
reg [31:0] next_PC, last_PC; //next_PC is comb., last_PC is a fairly useful register that stores the last succesful PC value | |
reg PC_ena; //enables the PC to be written | |
//non-architectural registers | |
reg IR_ena; | |
reg [31:0] IR; //holds the current instruction | |
reg [31:0] DR; //holds the value read out of the data memory | |
reg [31:0] reg_A, reg_B; //register file read data registers | |
reg [31:0] alu_last_result, exec_result; | |
reg [31:0] instruction_count; // for debugging | |
reg [9:0] instruction_number; | |
/* the ALU */ | |
reg [N-1:0] alu_src_a, alu_src_b; | |
wire [N-1:0] alu_result; | |
wire alu_equal, alu_zero, alu_overflow; | |
reg [3:0] alu_op; | |
alu #(.N(N)) ALU (.x(alu_src_a), .y(alu_src_b), .z(alu_result), .op_code(alu_op), .zero(alu_zero), .equal(alu_equal), .overflow(alu_overflow)); | |
/* register file */ | |
reg reg_wr_ena; | |
reg [4:0] reg_rd_addr0, reg_rd_addr1, reg_wr_addr; | |
reg [N-1:0] reg_wr_data; | |
wire [N-1:0] reg_rd_data0, reg_rd_data1; | |
output wire [32*32-1:0] full_register_file; | |
register_file #(.N(N)) REGISTER_FILE( | |
.clk(clk), .rst(rst), .wr_ena(reg_wr_ena), | |
.rd_addr0(reg_rd_addr0), .rd_addr1(reg_rd_addr1), | |
.rd_data0(reg_rd_data0), .rd_data1(reg_rd_data1), | |
.wr_addr(reg_wr_addr), .wr_data(reg_wr_data), | |
.full_register_file(full_register_file) | |
); | |
//CUT<<< | |
//instruction decode | |
wire [31:0] sign_extended_immediate, sign_extended_shifted_immediate, jump_address; | |
wire [5:0] op_code, funct; | |
wire [4:0] shamt; | |
wire [4:0] rd, rt, rs; | |
wire [`MIPS_TYPE_WIDTH-1:0] instruction_type; | |
mips_decoder DECODER ( | |
.instruction(IR), | |
.PC(PC), | |
.sign_extended_immediate(sign_extended_immediate), | |
.sign_extended_shifted_immediate(sign_extended_shifted_immediate), | |
.op_code(op_code), | |
.funct(funct), | |
.shamt(shamt), | |
.rd(rd), | |
.rt(rt), | |
.rs(rs), | |
.jump_address(jump_address), | |
.type(instruction_type) | |
); | |
always @(posedge clk) begin | |
if(rst) begin | |
IR <= 0; | |
DR <= 0; | |
exec_result <= 0; | |
reg_A <= 0; | |
reg_B <= 0; | |
instruction_number <= 0; | |
end | |
else begin | |
if (IR_ena) begin | |
IR <= mem_rd_data; | |
end | |
alu_last_result <= alu_result; | |
if(state == `S_EXECUTE) begin | |
exec_result <= alu_result; | |
end | |
if(state == `S_DECODE) begin | |
reg_A <= reg_rd_data0; | |
reg_B <= reg_rd_data1; | |
end | |
if(state == `S_MEMORY2) begin //todo check that this works | |
DR <= mem_rd_data; | |
end | |
if((state == `S_FETCH1) && ena) begin | |
instruction_number <= (PC[11:2]) + 1; | |
end | |
end | |
end | |
//control muxes | |
reg [`ALU_SRC_A_SW_WIDTH-1:0] alu_src_a_sw; | |
reg [`ALU_SRC_B_SW_WIDTH-1:0] alu_src_b_sw; | |
reg [`PC_SRC_SW_WIDTH-1:0] pc_src_sw; | |
always @(*) begin | |
//alu src a | |
case (alu_src_a_sw) | |
`ALU_SRC_A_SW_REG_A : alu_src_a = reg_A; | |
`ALU_SRC_A_SW_PC : alu_src_a = PC; | |
default : alu_src_a = 0; | |
endcase | |
//alu src b | |
case (alu_src_b_sw) | |
`ALU_SRC_B_SW_REG_B : alu_src_b = reg_B; | |
`ALU_SRC_B_SW_SEI : alu_src_b = sign_extended_immediate; | |
`ALU_SRC_B_SW_SESI : alu_src_b = sign_extended_shifted_immediate; | |
`ALU_SRC_B_SW_4 : alu_src_b = 32'd4; | |
`ALU_SRC_B_SW_16 : alu_src_b = 32'd16; | |
`ALU_SRC_B_SW_SHAMT : alu_src_b = shamt; | |
default : alu_src_b = 0; | |
endcase | |
//next PC | |
case (pc_src_sw) | |
`PC_SRC_SW_ALU : next_PC = alu_result; | |
`PC_SRC_SW_ALU_LAST : next_PC = alu_last_result; | |
`PC_SRC_SW_JUMP : next_PC = jump_address; | |
`PC_SRC_SW_JUMPR : next_PC = reg_rd_data0; | |
default : next_PC = 32'hFFFF_FFFF; //failure mode | |
endcase | |
end | |
/* ----------------- moore FSM for different instruction types -----------------------*/ | |
always @(posedge clk) begin | |
if(rst) begin | |
state <= `S_FETCH1; | |
instruction_count <= 32'd0; | |
PC <= {`I_START_ADDRESS, 20'h0}; | |
end | |
else begin | |
if (ena || (~ena && (next_state !== `S_FETCH1))) begin | |
state <= next_state; | |
if (state == `S_FETCH2) begin | |
instruction_count <= instruction_count + 1; | |
end | |
if(PC_ena) begin | |
last_PC <= PC; | |
PC <= next_PC; | |
end | |
end | |
end | |
end | |
// combinational logic | |
always @(*) begin | |
if(rst) begin | |
PC_ena = 0; | |
IR_ena = 0; | |
dmem_wr_ena = 0; | |
reg_wr_ena = 0; | |
pc_src_sw = `PC_SRC_SW_ALU; | |
end | |
else begin | |
PC_ena = 0; | |
IR_ena = 0; | |
dmem_wr_ena = 0; | |
reg_wr_ena = 0; | |
pc_src_sw = `PC_SRC_SW_ALU; | |
alu_src_a_sw = 0; | |
alu_src_b_sw = 0; | |
alu_op = 0; | |
next_state = `S_FAILURE; | |
reg_rd_addr0 = 0; | |
reg_rd_addr1 = 0; | |
reg_wr_addr = 0; | |
reg_wr_data = 0; | |
dmem_rd_addr = 0; | |
alu_op = 0; | |
case (state) | |
/* ---------------- FETCH ---------------- */ | |
`S_FETCH1: begin | |
IR_ena = 0; | |
dmem_wr_ena = 0; | |
reg_wr_ena = 0; | |
//set PC to PC + 4 with this ena/mux src combo | |
PC_ena = 1; | |
pc_src_sw = `PC_SRC_SW_ALU; | |
alu_op = `ALU_OP_ADD; | |
alu_src_a_sw = `ALU_SRC_A_SW_PC; | |
alu_src_b_sw = `ALU_SRC_B_SW_4; | |
next_state = `S_FETCH2; | |
end | |
`S_FETCH2: begin | |
IR_ena = 1; | |
dmem_wr_ena = 0; | |
reg_wr_ena = 0; | |
//PC = PC + 4 | |
PC_ena = 0; | |
pc_src_sw = `PC_SRC_SW_ALU; | |
alu_op = `ALU_OP_ADD; | |
alu_src_a_sw = `ALU_SRC_A_SW_PC; | |
alu_src_b_sw = `ALU_SRC_B_SW_4; | |
next_state = `S_DECODE; | |
end | |
/* ---------------- DECODE ---------------- */ | |
`S_DECODE : begin | |
PC_ena = 0; | |
IR_ena = 0; | |
dmem_wr_ena = 0; | |
reg_wr_ena = 0; | |
reg_rd_addr0 = rs; | |
reg_rd_addr1 = rt; | |
//compute branch target just in case | |
alu_op = `ALU_OP_ADD; | |
alu_src_a_sw = `ALU_SRC_A_SW_PC; | |
alu_src_b_sw = `ALU_SRC_B_SW_SESI; | |
next_state = `S_EXECUTE; | |
end | |
/* ---------------- EXECUTE ---------------- */ | |
`S_EXECUTE: begin | |
`ifdef VERBOSE | |
if(op_code === 6'b000000) begin | |
$display("@%10t::E:: line %3d, op = %8s (%b), funct = %8s (%b)", $time, instruction_number, op_code_string(op_code), op_code, funct_code_string(funct), funct); | |
end | |
else begin | |
$display("@%10t::E:: line %3d, op = %8s (%b)", $time, instruction_number, op_code_string(op_code), op_code); | |
end | |
`endif | |
PC_ena = 0; | |
IR_ena = 0; | |
dmem_wr_ena = 0; | |
reg_wr_ena = 0; | |
case(instruction_type) | |
/* ---------------- EXEC R ---------------- */ | |
`MIPS_TYPE_R : begin | |
PC_ena = 0; | |
alu_src_a_sw = `ALU_SRC_A_SW_REG_A; | |
if( (funct === `MIPS_FUNCT_SLL) || (funct === `MIPS_FUNCT_SRL) || (funct === `MIPS_FUNCT_SRA) ) begin | |
alu_src_b_sw = `ALU_SRC_B_SW_SHAMT; | |
end | |
else begin | |
alu_src_b_sw = `ALU_SRC_B_SW_REG_B; | |
end | |
reg_wr_ena = 0; | |
case (funct) | |
`MIPS_FUNCT_AND : alu_op = `ALU_OP_AND; | |
`MIPS_FUNCT_OR : alu_op = `ALU_OP_OR ; | |
`MIPS_FUNCT_XOR : alu_op = `ALU_OP_XOR; | |
`MIPS_FUNCT_NOR : alu_op = `ALU_OP_NOR; | |
`MIPS_FUNCT_ADD : alu_op = `ALU_OP_ADD; | |
`MIPS_FUNCT_ADDU: alu_op = `ALU_OP_ADD; | |
`MIPS_FUNCT_SUB : alu_op = `ALU_OP_SUB; | |
`MIPS_FUNCT_SUBU: alu_op = `ALU_OP_SUB; | |
`MIPS_FUNCT_SLL : alu_op = `ALU_OP_SLL; | |
`MIPS_FUNCT_SLLV: alu_op = `ALU_OP_SLL; | |
`MIPS_FUNCT_SRL : alu_op = `ALU_OP_SRL; | |
`MIPS_FUNCT_SRLV: alu_op = `ALU_OP_SRL; | |
`MIPS_FUNCT_SRA : alu_op = `ALU_OP_SRA; | |
`MIPS_FUNCT_SRAV: alu_op = `ALU_OP_SRA; | |
`MIPS_FUNCT_SLT : alu_op = `ALU_OP_SLT; | |
`MIPS_FUNCT_JR : alu_op = `ALU_OP_ADD; | |
default : alu_op = `ALU_OP_ADD; | |
endcase | |
next_state = `S_WRITEBACK; | |
end | |
/* ---------------- EXEC I ---------------- */ | |
`MIPS_TYPE_I : begin | |
PC_ena = 0; | |
alu_src_a_sw = `ALU_SRC_A_SW_REG_A; | |
alu_src_b_sw = `ALU_SRC_B_SW_SEI; | |
reg_wr_ena = 0; | |
case (op_code) | |
`MIPS_OP_ADDI, `MIPS_OP_ADDIU : alu_op = `ALU_OP_ADD; | |
`MIPS_OP_SLTI, `MIPS_OP_SLTIU : alu_op = `ALU_OP_SLT; | |
`MIPS_OP_ANDI : alu_op = `ALU_OP_AND; | |
`MIPS_OP_ORI : alu_op = `ALU_OP_OR; | |
`MIPS_OP_XORI : alu_op = `ALU_OP_XOR; | |
default : alu_op = `ALU_OP_ADD; | |
endcase | |
next_state = `S_WRITEBACK; | |
end | |
/* ---------------- EXEC M ---------------- */ | |
`MIPS_TYPE_M : begin | |
PC_ena = 0; | |
alu_op = `ALU_OP_ADD; | |
alu_src_a_sw = `ALU_SRC_A_SW_REG_A; | |
alu_src_b_sw = `ALU_SRC_B_SW_SEI; | |
reg_wr_ena = 0; | |
next_state = `S_MEMORY1; | |
end | |
/* ---------------- EXEC J ---------------- */ | |
`MIPS_TYPE_J : begin | |
// TODO | |
reg_wr_ena = 0; | |
PC_ena = 1; | |
pc_src_sw = `PC_SRC_SW_JUMP; | |
case (op_code) | |
`MIPS_OP_J : begin | |
next_state = `S_FETCH1; | |
end | |
`MIPS_OP_JAL : begin | |
next_state = `S_WRITEBACK; | |
end | |
default : begin | |
reg_wr_ena = 0; | |
PC_ena = 0; | |
pc_src_sw = 0; | |
alu_src_a_sw = `ALU_SRC_A_SW_REG_A; | |
alu_src_b_sw = `ALU_SRC_B_SW_REG_B; | |
next_state = `S_FAILURE; | |
end | |
endcase | |
end | |
/* ---------------- EXEC B ---------------- */ | |
`MIPS_TYPE_B : begin | |
//you will need to update this code! | |
reg_wr_ena = 0; | |
next_state = `S_FAILURE; | |
PC_ena = 0; | |
pc_src_sw = 0; | |
alu_src_a_sw = `ALU_SRC_A_SW_REG_A; | |
alu_src_b_sw = `ALU_SRC_B_SW_REG_B; | |
end | |
default: begin | |
PC_ena = 0; | |
next_state = `S_FAILURE; | |
end | |
endcase | |
end | |
/* ---------------- MEMORY ---------------- */ | |
`S_MEMORY1: begin | |
PC_ena = 0; | |
IR_ena = 0; | |
reg_wr_ena = 0; | |
mem_wr_data = 0; | |
case (op_code) | |
`MIPS_OP_LW : begin | |
dmem_rd_addr = alu_last_result; | |
dmem_wr_ena = 0; | |
next_state = `S_MEMORY2; | |
end | |
`MIPS_OP_SW : begin | |
mem_wr_data = reg_B; | |
dmem_wr_addr = alu_last_result; | |
dmem_wr_ena = 1; | |
next_state = `S_FETCH1; | |
end | |
default: begin | |
IR_ena = 0; | |
next_state = `S_FAILURE; | |
end | |
endcase | |
end | |
`S_MEMORY2: begin | |
PC_ena = 0; | |
IR_ena = 0; | |
reg_wr_ena = 0; | |
case (op_code) | |
`MIPS_OP_LW : begin | |
dmem_rd_addr = alu_last_result; | |
dmem_wr_ena = 0; | |
next_state = `S_WRITEBACK; | |
`ifdef VERBOSE | |
$display("@%10t::M:: line %3d, op = %8s (%b),dmem_rd_addr = %h", $time, instruction_number, op_code_string(op_code), op_code, dmem_rd_addr); | |
`endif | |
end | |
default: begin | |
dmem_wr_ena = 0; | |
next_state = `S_FAILURE; | |
end | |
endcase | |
end | |
/* ---------------- WRITEBACK ---------------- */ | |
`S_WRITEBACK: begin | |
IR_ena = 0; | |
PC_ena = 0; | |
dmem_wr_ena = 0; | |
reg_wr_ena = 0; | |
next_state = `S_FETCH1; | |
case (instruction_type) | |
`MIPS_TYPE_R: begin | |
// MARK: OUR CODE | |
if (funct == `MIPS_FUNCT_JR) begin | |
PC_ena = 1; | |
pc_src_sw = `PC_SRC_SW_JUMPR; | |
end | |
else begin | |
reg_wr_ena = 1; | |
reg_wr_addr = rd; | |
reg_wr_data = alu_last_result; | |
pc_src_sw = `PC_SRC_SW_ALU; | |
PC_ena = 0; | |
end | |
end | |
`MIPS_TYPE_I: begin | |
reg_wr_ena = 1; | |
reg_wr_addr = rt; | |
reg_wr_data = alu_last_result; | |
end | |
`MIPS_TYPE_M: begin | |
case (op_code) | |
`MIPS_OP_LW : begin | |
`ifdef VERBOSE | |
$display("@%10t::W:: line %3d, op = %8s (%b), DR = %h", $time, instruction_number, op_code_string(op_code), op_code, DR); | |
`endif | |
reg_wr_ena = 1; | |
reg_wr_addr = rt; | |
reg_wr_data = DR; | |
end | |
default: begin | |
`ifdef VERBOSE | |
$display("@%10t::W:: line %3d, op = %8s (%b), FAILURE1", $time, instruction_number, op_code_string(op_code), op_code); | |
`endif | |
$finish; | |
reg_wr_ena = 0; | |
next_state = `S_FAILURE; | |
end | |
endcase | |
end //M-type | |
`MIPS_TYPE_J: begin | |
// TODO | |
reg_wr_ena = 1; | |
reg_wr_addr = 32'd31; | |
reg_wr_data = next_PC; | |
PC_ena = 0; | |
end | |
default: begin | |
`ifdef VERBOSE | |
$display("@%10t::W:: line %3d, op = %8s (%b), FAILURE2: i_type = %d", $time, instruction_number, op_code_string(op_code), op_code, instruction_type); | |
`endif | |
next_state = `S_FAILURE; | |
end | |
endcase | |
end | |
default: begin | |
PC_ena = 0; | |
dmem_wr_ena = 0; | |
`ifdef VERBOSE | |
$display("this fail6? state = %d", state); | |
`endif | |
next_state = `S_FAILURE; | |
end | |
endcase | |
end | |
end | |
//>>> | |
endmodule | |
`default_nettype wire |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment