/// LSU EE 4720 -- Spring 2002 -- Computer Architecture // /// Pipelined Hardwired Control MIPS // // Implements a small subset of MIPS32 instructions and features. // // Time-stamp: <19 March 2002, 18:48:31 CST, koppel@sol> // /// Specify Program to Load // // A quick-and-dirty method of loading a program is used. (For now.) // Therefore the description must be re-compiled each time the program // is changed. (The program is assembled by SPIM and some perl code.) // `define MIPS_PROG "hw02.v" /// Unexpected Macro // // Used in the default case item of case statements when the default // item should not be executed. Used in case statements that assign // the same register in each item. If the default item is executed // (indicating a programming bug) simulation is stopped and hopefully // the bug is fixed. For synthesis, the unexpected macro would assign // the variable, avoiding the synthesis of a latch. Alas, Leonardo // Spectrum does not recognize macros with parameters so a task is // used to assign the variable. That task must be included in each // module. (If Leonardo understood hierarchical references, the task // could be put in a special utility module, but it can't.) `ifdef exemplar `define UNEXPECTED unexpected `else `define UNEXPECTED(var,ctrl) begin var = 0; if( ctrl[0] !== 1'bx ) $stop; end 0: `endif /// /// MIPS Processor /// module cpu_p1(exc,data_out_2, addr_1,addr_2,size_2,we_2, data_in_1,data_in_2, mem_error_in_1,mem_error_in_2,reset,clk); input [31:0] data_in_1, data_in_2; input [2:0] mem_error_in_1, mem_error_in_2; input reset,clk; output [7:0] exc; output [31:0] data_out_2, addr_1, addr_2; output [1:0] size_2; output we_2; task unexpected; inout [31:0] var; input [10:0] control; var = 0; endtask /// /// MIPS CONSTANTS /// // // Defined by the ISA /// "opcode" Field Values // parameter O_rfmt = 6'h0; parameter O_j = 6'h2; parameter O_beq = 6'h4; parameter O_bne = 6'h5; parameter O_addi = 6'h8; parameter O_slti = 6'ha; parameter O_andi = 6'hc; parameter O_ori = 6'hd; parameter O_lui = 6'hf; parameter O_lw = 6'h23; parameter O_lbu = 6'h24; parameter O_sw = 6'h2b; parameter O_sb = 6'h28; /// "function" Field Values // parameter F_sll = 6'h0; parameter F_srl = 6'h2; parameter F_sys = 6'hc; parameter F_add = 6'h20; parameter F_sub = 6'h22; parameter F_and = 6'h24; parameter F_or = 6'h25; parameter F_xor = 6'h26; /// /// IMPLEMENTATION CONSTANTS /// // // Defined for this implementation, not standardized. These codes // are used by other modules in this implementation, so they should // be changed consistently. /// Processor Exception Codes // parameter EXC_none = 8'd0; parameter EXC_if_bus = 8'd1; // Bus Error (Mis-aligned address.) parameter EXC_if_seg = 8'd2; // Bad Address parameter EXC_id_ins = 8'd3; // Illegal (Reserved) Instruction parameter EXC_id_sys = 8'd5; // Syscall Instruction parameter EXC_me_bus = 8'd6; // Bus Error (Mis-aligned address.) parameter EXC_me_seg = 8'd7; // Bad Address // // The exception codes above are not MIPS codes, though they are // similar. (For one thing, MIPS does not use the bus exception // for a misaligned address.) For the real codes see Table 6-17 in //http://www.ece.lsu.edu/ee4720/mips32v3.pdf /// ALU Operations + Extra Bit // // The lower 5 bits of the codes below are used by the ALU, the // sixth bit is used for special cases and is removed in the ID // stage. // parameter OP_xxx = 6'h0; // Don't care. parameter OP_add = 6'h0; parameter OP_sll = 6'h1; parameter OP_srl = 6'h2; parameter OP_xor = 6'h3; parameter OP_sub = 6'h4; parameter OP_or = 6'h5; parameter OP_and = 6'h6; parameter OP_slt = 6'h7; parameter OP_seq = 6'h8; parameter OP_b = 6'h9; parameter OP_ill = 6'h20; // Illegal Instruction parameter OP_sys = 6'h21; // Syscall /// Memory Exception Codes // parameter MEM_ERR_none = 0; parameter MEM_ERR_bus = 1; // Bad alignment. parameter MEM_ERR_seg = 2; // Bad address. /// /// MODULE CONSTANTS /// // // Defined for this module, intended to improve readability of code. /// PC Mux // parameter SRC_npc = 1'b0; parameter SRC_alu = 1'b1; /// ALU Muxen // parameter SRC_xx = 3'd2; // Won't be used, avoids stalls on alu_a and alu_b. parameter SRC_rs = 3'd1; parameter SRC_sa = 3'd2; parameter SRC_rt = 3'd1; parameter SRC_im = 3'd2; parameter SRC_np = 3'd3; /// Register Number to Write Back // parameter WB_00 = 3'd0; parameter WB_rd = 3'd1; parameter WB_rt = 3'd2; /// Immediate Formatting // parameter IMM_x = 3'd0; // Don't care. parameter IMM_s = 3'd0; parameter IMM_u = 3'd1; parameter IMM_l = 3'd2; parameter IMM_j = 3'd3; parameter IMM_b = 3'd4; /// Memory Access Size // parameter ME_SIZE_0 = 2'd0; parameter ME_SIZE_1 = 2'd1; parameter ME_SIZE_2 = 2'd2; parameter ME_SIZE_4 = 2'd3; parameter ME_CONTROL_nop = { ME_SIZE_0, 1'd0 }; /// /// DECLARATIONS /// /// Pipeline Latches and Some Pipeline-Latch Inputs // reg [31:0] if_pc, next_if_pc; reg [31:0] if_id_ir; reg [31:0] if_id_npc, if_id_pc; reg [7:0] if_id_exc, next_if_id_exc; reg if_id_occ; reg [31:0] tb_if_din, tb_if_id_din; reg [31:0] id_ex_npc, id_ex_pc; reg [31:0] id_ex_rs_val; reg [31:0] id_ex_rt_val; reg [4:0] id_ex_sa, next_id_ex_sa; reg [31:0] id_ex_imm, next_id_ex_imm; reg [4:0] id_ex_dst, next_id_ex_dst; reg [2:0] id_ex_alu_a_src, next_id_ex_alu_a_src; reg [4:0] id_ex_alu_op, next_id_ex_alu_op; reg [2:0] id_ex_alu_b_src, next_id_ex_alu_b_src; reg [8:0] id_ex_me, next_id_ex_me; reg [7:0] id_ex_exc, next_id_ex_exc; reg id_ex_occ; reg [31:0] tb_id_ex_din; reg [31:0] ex_me_npc, ex_me_pc; reg [31:0] ex_me_alu; reg [31:0] ex_me_rt_val; reg [2:0] ex_me_flags, ex_me_f_mask, ex_me_f_inv; reg [1:0] ex_me_size; reg ex_me_we; reg [4:0] ex_me_dst; reg [7:0] ex_me_exc; wire [7:0] next_ex_me_exc; reg ex_me_occ; reg [31:0] tb_ex_me_din; reg [31:0] me_wb_npc, me_wb_pc; reg [31:0] me_wb_alu; reg [31:0] me_wb_md; reg [4:0] me_wb_dst; reg me_wb_occ; reg [7:0] me_wb_exc, next_me_wb_exc; reg me_wb_from_mem; reg [31:0] tb_me_wb_din; /// Interstage Signals // reg hold_id; wire squash_if_id, squash_id; wire [1:0] pc_src; // Could be 1 bit, but then could not use UNEXPECTED. /// Instruction Fields // reg [4:0] rs, rt, rd, sa; reg [5:0] opcode, func; reg [25:0] ii; reg [15:0] immed; /// Some ALU AND GPR Connections // wire [31:0] alu_out; reg [31:0] alu_a, alu_b; wire [31:0] gpr_rs_val, gpr_rt_val, gpr_write_val; /// Some Memory Connections // assign addr_1 = if_pc; assign addr_2 = ex_me_alu; assign we_2 = ex_me_we; assign size_2 = ex_me_size; assign data_out_2 = ex_me_rt_val; /// /// INSTANTIATIONS /// alu our_alu(alu_out, alu_a, alu_b, id_ex_alu_op); reg_file gpr(gpr_rs_val, gpr_rt_val, rs, rt, me_wb_dst, gpr_write_val, clk); /// /// PIPELINE STARTS HERE /// /// /// Instruction Fetch /// always @( if_pc or pc_src or ex_me_alu ) case( pc_src ) SRC_npc: next_if_pc = if_pc + 4; SRC_alu: next_if_pc = ex_me_alu; default: `UNEXPECTED(next_if_pc,pc_src); endcase always @( mem_error_in_1 ) case( mem_error_in_1 ) MEM_ERR_none : next_if_id_exc = 0; MEM_ERR_seg : next_if_id_exc = EXC_if_seg; MEM_ERR_bus : next_if_id_exc = EXC_if_bus; default : `UNEXPECTED(next_if_id_exc, mem_error_in_1); endcase always @( posedge clk ) if( reset ) begin if_id_occ <= 0; // The value below is the usual entry point for SPIM-compiled // code. Real MIPS processors reset PC to 'hbfc00000. if_pc <= 'h400000; tb_if_din <= 1; end else if( ~hold_id || squash_if_id ) begin if_id_pc <= if_pc; if_pc <= next_if_pc; if_id_ir <= data_in_1; if_id_npc <= next_if_pc; if_id_exc <= next_if_id_exc; if_id_exc <= {5'b0, mem_error_in_1}; if_id_occ <= ~squash_if_id; tb_if_id_din <= tb_if_din; tb_if_din <= tb_if_din + 1; end /// /// Instruction Decode /// // Stage-Local Declarations // reg [17:0] d_a_op_b; // Dest <- operand_a op operand_b, immed_fmt reg [2:0] size_we; // Memory control bits. reg [5:0] mask_inv; // Condition flag mask and invert bits. reg [2:0] immed_fmt; // Formating to apply to immediate. reg [2:0] dest_field; reg extra_op_bit; reg op_rs, op_rt; always @( if_id_ir or id_ex_dst or ex_me_dst or me_wb_dst or squash_id or if_id_occ or if_id_npc ) begin {opcode,rs,rt,rd,sa,func} = if_id_ir; ii = if_id_ir[25:0]; immed = if_id_ir[15:0]; case( opcode ) O_rfmt: // R-Format Instructions case( func ) F_sll : d_a_op_b = {WB_rd, SRC_sa, OP_sll, SRC_rt, IMM_x}; F_sys : d_a_op_b = {WB_00, SRC_xx, OP_sys, SRC_xx, IMM_x}; F_add : d_a_op_b = {WB_rd, SRC_rs, OP_add, SRC_rt, IMM_x}; F_sub : d_a_op_b = {WB_rd, SRC_rs, OP_sub, SRC_rt, IMM_x}; F_and : d_a_op_b = {WB_rd, SRC_rs, OP_and, SRC_rt, IMM_x}; F_or : d_a_op_b = {WB_rd, SRC_rs, OP_or, SRC_rt, IMM_x}; F_xor : d_a_op_b = {WB_rd, SRC_rs, OP_xor, SRC_rt, IMM_x}; default : d_a_op_b = {WB_00, SRC_rs, OP_ill, SRC_rt, IMM_x}; endcase // I- and J-Format Instructions O_lw, O_lbu : d_a_op_b = {WB_rt, SRC_rs, OP_add, SRC_im, IMM_s}; O_sb : d_a_op_b = {WB_00, SRC_rs, OP_add, SRC_im, IMM_s}; O_lui : d_a_op_b = {WB_rt, SRC_rs, OP_or, SRC_im, IMM_l}; O_addi : d_a_op_b = {WB_rt, SRC_rs, OP_add, SRC_im, IMM_s}; O_andi : d_a_op_b = {WB_rt, SRC_rs, OP_and, SRC_im, IMM_u}; O_ori : d_a_op_b = {WB_rt, SRC_rs, OP_or, SRC_im, IMM_u}; O_slti : d_a_op_b = {WB_rt, SRC_rs, OP_slt, SRC_im, IMM_s}; O_j : d_a_op_b = {WB_00, SRC_xx, OP_b, SRC_im, IMM_j}; O_bne, O_beq : d_a_op_b = {WB_00, SRC_np, OP_add, SRC_im, IMM_b}; default : d_a_op_b = {WB_00, SRC_rs, OP_ill, SRC_im, IMM_s}; endcase { dest_field, next_id_ex_alu_a_src, extra_op_bit, next_id_ex_alu_op, next_id_ex_alu_b_src, immed_fmt } = d_a_op_b; case( {extra_op_bit,next_id_ex_alu_op} ) OP_sys: next_id_ex_exc = EXC_id_sys; OP_ill: next_id_ex_exc = EXC_id_ins; default: next_id_ex_exc = EXC_none; endcase case( opcode ) O_bne, O_beq: {op_rs, op_rt} = 2'd3; O_sb: {op_rs, op_rt} = 2'd3; default: {op_rs, op_rt} = {next_id_ex_alu_a_src == SRC_rs, next_id_ex_alu_b_src == SRC_rt}; endcase hold_id = if_id_occ && ~squash_id && ( ( op_rs && rs && ( rs == id_ex_dst || rs == ex_me_dst) ) || ( op_rt && rt && ( rt == id_ex_dst || rt == ex_me_dst) ) ); case( immed_fmt ) IMM_s: next_id_ex_imm = { immed[15] ? 16'hffff : 16'h0, immed }; IMM_l: next_id_ex_imm = { immed, 16'h0 }; IMM_u: next_id_ex_imm = { 16'h0, immed }; IMM_j: next_id_ex_imm = { if_id_npc[31:28], ii, 2'b0 }; IMM_b: next_id_ex_imm = { immed[15] ? 14'h3fff : 14'h0, immed, 2'b0 }; default: `UNEXPECTED(next_id_ex_imm,immed_fmt); endcase case ( dest_field ) WB_00: next_id_ex_dst = 0; WB_rd: next_id_ex_dst = rd; WB_rt: next_id_ex_dst = rt; default: `UNEXPECTED(next_id_ex_dst,dest_field); endcase case( opcode ) O_lbu : size_we = {ME_SIZE_1, 1'b0}; O_lw : size_we = {ME_SIZE_4, 1'b0}; O_sb : size_we = {ME_SIZE_1, 1'b1}; default : size_we = {ME_SIZE_0, 1'b0}; endcase case( opcode ) // Flags: EZN O_bne : mask_inv = { 3'b100, 3'b100 }; O_beq : mask_inv = { 3'b100, 3'b000 }; O_j : mask_inv = { 3'b000, 3'b100 }; default : mask_inv = { 3'b000, 3'b000 }; endcase next_id_ex_me = {mask_inv,size_we}; next_id_ex_sa = sa; end assign squash_id = squash_if_id && id_ex_occ; wire id_live = if_id_occ && ~squash_id; always @( posedge clk ) if ( hold_id || reset ) begin id_ex_dst <= 0; id_ex_me <= ME_CONTROL_nop; id_ex_occ <= 0; end else begin id_ex_npc <= if_id_npc; id_ex_pc <= if_id_pc; id_ex_rs_val <= gpr_rs_val; id_ex_rt_val <= gpr_rt_val; id_ex_sa <= next_id_ex_sa; id_ex_alu_a_src <= next_id_ex_alu_a_src; id_ex_alu_b_src <= next_id_ex_alu_b_src; id_ex_alu_op <= next_id_ex_alu_op; id_ex_imm <= next_id_ex_imm; id_ex_me <= id_live ? next_id_ex_me : ME_CONTROL_nop; id_ex_dst <= id_live ? next_id_ex_dst : 5'b0; id_ex_exc <= if_id_exc ? if_id_exc : next_id_ex_exc; id_ex_occ <= id_live; tb_id_ex_din <= tb_if_id_din; end /// /// Execute /// always @( id_ex_alu_a_src or id_ex_rs_val or id_ex_sa or id_ex_npc ) case( id_ex_alu_a_src ) SRC_rs: alu_a = id_ex_rs_val; SRC_np: alu_a = id_ex_npc; SRC_sa: alu_a = {27'd0, id_ex_sa}; default: `UNEXPECTED(alu_a,id_ex_alu_a_src); endcase always @( id_ex_alu_b_src or id_ex_rt_val or id_ex_imm ) case( id_ex_alu_b_src ) SRC_rt: alu_b = id_ex_rt_val; SRC_im: alu_b = id_ex_imm; default: `UNEXPECTED(alu_b,id_ex_alu_b_src); endcase wire [2:0] next_ex_me_flags = { id_ex_rs_val == id_ex_rt_val, id_ex_rs_val == 0, id_ex_rs_val[31] }; assign next_ex_me_exc = 0; always @( posedge clk ) begin ex_me_npc <= id_ex_npc; ex_me_pc <= id_ex_pc; ex_me_alu <= alu_out; ex_me_rt_val <= id_ex_rt_val; { ex_me_f_mask, ex_me_f_inv, ex_me_size, ex_me_we} <= id_ex_exc || reset ? {6'd0,ME_CONTROL_nop} : id_ex_me; ex_me_dst <= reset ? 5'd0 : id_ex_dst; ex_me_flags <= next_ex_me_flags; ex_me_exc <= id_ex_exc ? id_ex_exc : next_ex_me_exc; ex_me_occ <= ~reset & id_ex_occ; tb_ex_me_din<= tb_id_ex_din; end /// Memory assign pc_src = ex_me_occ && ( ex_me_flags & ex_me_f_mask ^ ex_me_f_inv ) ? SRC_alu : SRC_npc; assign squash_if_id = pc_src === SRC_alu; always @( mem_error_in_2 ) case( mem_error_in_2 ) MEM_ERR_none : next_me_wb_exc = 0; MEM_ERR_seg : next_me_wb_exc = EXC_me_seg; MEM_ERR_bus : next_me_wb_exc = EXC_me_bus; default : `UNEXPECTED(next_me_wb_exc, mem_error_in_2); endcase always @( posedge clk ) begin me_wb_npc <= ex_me_npc; me_wb_pc <= ex_me_pc; me_wb_dst <= next_me_wb_exc || reset ? 5'd0 : ex_me_dst; me_wb_from_mem <= ex_me_size != 0; me_wb_alu <= ex_me_alu; me_wb_md <= data_in_2; me_wb_exc <= ex_me_exc ? ex_me_exc : next_me_wb_exc; me_wb_occ <= ~reset & ex_me_occ; tb_me_wb_din <= tb_ex_me_din; end /// Writeback assign gpr_write_val = me_wb_from_mem ? me_wb_md : me_wb_alu; assign exc = me_wb_occ ? {1'b0,me_wb_exc} : 8'd0; /// /// END OF HARDWARE DESCRIPTION /// /// /// TESTBENCH INTERFACE CODE /// // exemplar translate_off reg [31:0] tbi_done_pc; reg tbi_inst_done; always @( posedge clk ) tbi_inst_done <= me_wb_occ; always @( posedge clk ) tbi_done_pc <= me_wb_pc; task tbi_poke_gpr; input [5:0] r; input [31:0] val; gpr.storage[r] = val; endtask function [31:0] tbi_peek_gpr; input [5:0] r; tbi_peek_gpr = gpr.storage[r]; endfunction task tbi_iterate_pipeline_segments; output valid; output [15:0] name; output [31:0] pc; output [31:0] din; output [7:0] exc; output occ; reg [88:0] info; integer stage; begin if( stage === 32'bx ) stage = 0; for(valid = 0; stage < 5 && !valid; valid = occ == 1 ) begin case( stage ) 0: info = {"IF",if_pc, tb_if_din, next_if_id_exc,1'd1}; 1: info = {"ID",if_id_pc,tb_if_id_din,next_id_ex_exc,if_id_occ}; 2: info = {"EX",id_ex_pc,tb_id_ex_din,next_ex_me_exc,id_ex_occ}; 3: info = {"ME",ex_me_pc,tb_ex_me_din,next_me_wb_exc,ex_me_occ}; 4: info = {"WB",me_wb_pc,tb_me_wb_din,8'b0, me_wb_occ}; default `UNEXPECTED(info,stage); endcase {name,pc,din,exc,occ} = info; stage = stage + 1; end if( !valid ) stage = 0; end endtask // exemplar translate_on endmodule module reg_file(data_out_1, data_out_2, addr_1, addr_2, addr_3, data_in_3, clk); input [4:0] addr_1, addr_2, addr_3; input [31:0] data_in_3; input clk; output [31:0] data_out_1, data_out_2; reg [31:0] storage [0:31]; assign data_out_1 = addr_1 && addr_1 == addr_3 ? data_in_3 : storage[addr_1]; assign data_out_2 = addr_2 && addr_2 == addr_3 ? data_in_3 : storage[addr_2]; always @( posedge clk ) if( addr_3 ) storage[addr_3] <= data_in_3; endmodule module alu(alu_out,alu_a,alu_b,alu_op); output [31:0] alu_out; input [31:0] alu_a, alu_b; input [4:0] alu_op; reg [31:0] alu_out; task unexpected; inout [31:0] var; input [10:0] control; var = 0; endtask // Control Signal Value Names parameter OP_xxx = 5'h0; // Don't care. parameter OP_add = 5'h0; parameter OP_sll = 5'h1; parameter OP_srl = 5'h2; parameter OP_xor = 5'h3; parameter OP_sub = 5'h4; parameter OP_or = 5'h5; parameter OP_and = 5'h6; parameter OP_slt = 5'h7; parameter OP_seq = 5'h8; parameter OP_b = 5'h9; always @( alu_a or alu_b or alu_op ) case( alu_op ) OP_add : alu_out = alu_a + alu_b; OP_and : alu_out = alu_a & alu_b; OP_or : alu_out = alu_a | alu_b; OP_xor : alu_out = alu_a ^ alu_b; OP_sub : alu_out = alu_a - alu_b; OP_slt : alu_out = {alu_a[31],alu_a} < {alu_b[31],alu_b}; OP_sll : alu_out = alu_b << alu_a[4:0]; OP_srl : alu_out = alu_b >> alu_a[4:0]; OP_seq : alu_out = alu_a == alu_b; OP_b : alu_out = alu_b; default : `UNEXPECTED(alu_out,alu_op); endcase endmodule // exemplar translate_off module system_p1(exc,reset,clk); input reset,clk; output [7:0] exc; wire [31:0] cpu_data_out_2, addr_1, addr_2, mem_data_out_1, mem_data_out_2; wire [2:0] mem_err_out_1, mem_err_out_2; wire [1:0] size_2; wire we_2; cpu_p1 cpu1(exc, cpu_data_out_2, addr_1, addr_2, size_2, we_2, mem_data_out_1, mem_data_out_2, mem_err_out_1,mem_err_out_2, reset,clk); memory_2p m1( mem_data_out_1, mem_err_out_1, addr_1, mem_data_out_2 ,mem_err_out_2, addr_2, size_2, we_2, cpu_data_out_2, clk); endmodule `include "mipspipetb.v"