/// LSU EE 4720 -- Spring 2002 -- Computer Architecture // /// Pipelined Hardwired Control MIPS with Bypassing and FP // // Implements a small subset of MIPS32 instructions and features. // // Time-stamp: <18 March 2002, 10:33:03 CST, koppel@sol> // /// Limitations // // This code will probably never implement all of MIPS32, but the // limitations below may be addressed at some time. // // Omits division and other FP instructions. // Does not handle double-precision numbers. // Does not perform format conversions. /// 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 "fptest.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_cop1 = 6'h11; parameter O_lw = 6'h23; parameter O_lbu = 6'h24; parameter O_sw = 6'h2b; parameter O_sb = 6'h28; parameter O_lwc1 = 6'h31; /// Not-So-Special Special 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; /// Coprocessor 1 (FP) function Field Values // parameter C1_add = 6'h0; parameter C1_sub = 6'h1; parameter C1_mul = 6'h2; /// Coprocessor 1 (FP) fmt Field Values // parameter FMT_S = 5'o20; parameter FMT_D = 5'o21; parameter FMT_W = 5'o24; /// /// 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 PC_npc = 2'd0; parameter PC_dsp = 2'd1; // Displacement (Branches) parameter PC_rgn = 2'd2; // Region (Jumps) parameter PC_rs = 2'd3; /// ALU Muxen // parameter SRC_xx = 3'd4; // Won't be used, avoids stalls on alu_a and alu_b. parameter SRC_me = 3'd1; parameter SRC_wb = 3'd2; parameter SRC_rs = 3'd3; parameter SRC_sa = 3'd4; parameter SRC_rt = 3'd3; parameter SRC_im = 3'd4; parameter SRC_np = 3'd5; /// Register Number to Write Back // parameter WB_00 = 3'd0; parameter WB_rd = 3'd1; parameter WB_rt = 3'd2; parameter WB_ft = 3'd3; /// 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; /// 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 }; /// FP Stuff // parameter INT_stages = 2; parameter AFU_stages = 4; parameter MFU_stages = 6; // FU WB DST Ver PC DIN parameter FPRR_entry_size = 3 + 1 + 5 + 3 + 32 + 32; `define FPRR_range FPRR_entry_size*(MFU_stages+2)-1:0 `define FPRR_add FPRR_entry_size*(AFU_stages+2)-1:FPRR_entry_size*(AFU_stages+1) `define FPRR_mul FPRR_entry_size*(MFU_stages+2)-1:FPRR_entry_size*(MFU_stages+1) `define FPRR_int FPRR_entry_size*(INT_stages+2)-1:FPRR_entry_size*(INT_stages+1) `define FPRR_neck FPRR_entry_size*2-1:FPRR_entry_size `define FPRR_head FPRR_entry_size-1:0 // Reservation Register Entries. // // Specifies which functional unit will be writing which register file. // FP functional units always write FP registers, so the register // file is omitted. (Only the last few entries explicitly specify // a register file.) // parameter FP_no = 3'd0; // No instruction writing back. parameter FP_add = 3'd1; parameter FP_mul = 3'd2; parameter FP_div = 3'd3; parameter FP_itf = 3'd4; // Integer pipeline to FP register. parameter FP_iti = 3'd5; // Integer pipeline to integer register. parameter FOP_xxx = 6'd0; parameter FOP_add = 6'd0; parameter FOP_sub = 6'd2; parameter FOP_mul = 6'd3; parameter FWB_00 = 2'd0; // No fp register written parameter FWB_fd = 2'd1; parameter FWB_rt = 2'd2; parameter FSRC_xxx = 2'd0; parameter FSRC_fpr = 2'd0; parameter FSRC_wb = 2'd1; parameter FSRC_stall = 2'd2; /// /// 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, id_ex_fs_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 [2:0] id_ex_me, next_id_ex_me; reg [7:0] id_ex_exc, next_id_ex_exc; reg [31:0] id_ex_rt_val, id_ex_ft_val; reg [2:0] id_fu_needed; reg [5:0] id_ex_fp_op, next_id_ex_fp_op; reg [1:0] id_ex_fs_src, next_id_ex_fs_src; reg [1:0] id_ex_ft_src, next_id_ex_ft_src; reg [2:0] id_next_ver; reg id_ex_occ; reg id_fp_op_wb; reg [31:0] tb_id_ex_din; reg [`FPRR_range] fp_res_reg; reg [31:0] ex_me_npc, ex_me_pc; reg [31:0] ex_me_alu; reg [31:0] ex_me_rt_val; 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 [31:0] tb_ex_me_din; reg ex_me_occ; reg [31:0] me_wb_pc; reg [31:0] me_wb_npc; reg [31:0] me_wb_alu; reg [31:0] me_wb_md; reg [4:0] me_wb_dst; reg [31:0] tb_me_wb_din; reg me_wb_occ; reg [7:0] me_wb_exc, next_me_wb_exc; reg me_wb_from_mem; /// Interstage Signals // reg hold_id; wire squash_if_id, squash_id; reg [1:0] pc_src; wire load_in_ex; /// Instruction Fields // reg [4:0] rs, rt, rd, sa, fs, ft, fd, fmt; 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); reg [31:0] fpr [0:31]; reg [2:0] fpr_id_ver [0:31]; reg [2:0] fpr_wb_ver [0:31]; wire wn_fpr_write; reg [31:0] fpr_write_val; reg wb_fpr_write; wire [2:0] wb_unit, wb_ver; wire [4:0] wb_fp_dst; wire [31:0] wb_fp_pc, tb_wb_din; wire wb_fpr_writer; assign {wb_unit,wb_fpr_writer,wb_fp_dst,wb_ver,wb_fp_pc,tb_wb_din} = fp_res_reg[`FPRR_head]; wire [2:0] wn_unit, wn_ver; // Write Next (or neck) wire [4:0] wn_fp_dst; wire [31:0] wn_fp_pc, tb_wn_din; wire wn_fpr_writer; assign {wn_unit,wn_fpr_writer,wn_fp_dst,wn_ver,wn_fp_pc,tb_wn_din} = fp_res_reg[`FPRR_neck]; wire [`FPRR_head] fp_res_pos_add = fp_res_reg[`FPRR_add]; wire [`FPRR_head] fp_res_pos_mul = fp_res_reg[`FPRR_mul]; wire [`FPRR_head] fp_res_pos_int = fp_res_reg[`FPRR_int]; wire [31:0] fpr_ft_val = wb_fpr_write && wb_fp_dst == ft ? fpr_write_val : fpr[ft]; wire [31:0] fpr_fs_val = wb_fpr_write && wb_fp_dst == fs ? fpr_write_val : fpr[fs]; /// /// PIPELINE STARTS HERE /// /// /// Instruction Fetch /// always @( if_pc or pc_src or ex_me_alu or if_id_npc or immed or gpr_rs_val or ii ) case( pc_src ) PC_npc : next_if_pc = if_pc + 4; PC_dsp : next_if_pc = if_id_npc + {immed[15]?14'h3fff:14'h0,immed,2'b0}; PC_rgn : next_if_pc = { if_id_npc[31:28], ii, 2'b0 }; PC_rs : next_if_pc = gpr_rs_val; 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 [2:0] immed_fmt; // Formating to apply to immediate. reg [2:0] dest_field; reg [1:0] fdest_field; reg extra_op_bit; reg op_rs, op_rt; reg cant_bypass; reg branch_in_id; reg [2:0] alu_a_src_maybe, alu_b_src_maybe; reg [2+3+6+3-1:0] fp_info; reg [2:0] runit, rver; reg rwb; reg [31:0] rpc, rdin; reg [4:0] rdst, id_fp_dst; reg fp_hold_id; reg fp_op_fs, fp_op_ft; reg [2:0] cur_ver; reg illegal_format; wire [2:0] fp_op_fs_ver = fpr_id_ver[fs]; wire [2:0] fp_op_ft_ver = fpr_id_ver[ft]; wire [2:0] fp_op_fs_wb_ver = fpr_wb_ver[fs]; wire [2:0] fp_op_ft_wb_ver = fpr_wb_ver[ft]; wire [2:0] id_incumb_ver = fpr_id_ver[id_fp_dst]; 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 or load_in_ex or fp_op_fs_ver or fp_op_ft_ver or fp_op_fs_wb_ver or fp_op_ft_wb_ver or fp_res_pos_add or fp_res_pos_mul or fp_res_pos_int or wn_ver or wn_fp_dst or id_incumb_ver ) begin {opcode,rs,rt,rd,sa,func} = if_id_ir; {fmt,ft,fs,fd} = {rs,rt,rd,sa}; ii = if_id_ir[25:0]; immed = if_id_ir[15:0]; // Note: Case statements below could be synthesized as a memory // which is why only constants appear on the RHS of the assignments. 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 O_cop1 : d_a_op_b = {WB_00, SRC_xx, OP_xxx, SRC_xx, IMM_x}; // I- and J-Format Instructions O_lw, O_lbu : d_a_op_b = {WB_rt, SRC_rs, OP_add, SRC_im, IMM_s}; O_lwc1 : d_a_op_b = {WB_ft, 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_xxx, SRC_xx, IMM_x}; O_bne, O_beq : d_a_op_b = {WB_00, SRC_xx, OP_xxx, SRC_xx, IMM_x}; default : d_a_op_b = {WB_00, SRC_rs, OP_ill, SRC_im, IMM_s}; endcase { dest_field, alu_a_src_maybe, extra_op_bit, next_id_ex_alu_op, alu_b_src_maybe, immed_fmt } = d_a_op_b; if( opcode == O_cop1 ) begin case( func ) C1_add: fp_info = { FWB_fd, FP_add, FOP_add, 3'b111 }; C1_sub: fp_info = { FWB_fd, FP_add, FOP_sub, 3'b111 }; C1_mul: fp_info = { FWB_fd, FP_mul, FOP_mul, 3'b111 }; default: `UNEXPECTED(fp_info, func); endcase end else if( dest_field == WB_ft ) fp_info = { FWB_rt, FP_itf, FOP_xxx, 3'b100 }; else fp_info = { FWB_00, FP_iti, FOP_xxx, 3'b000 }; {fdest_field, id_fu_needed, next_id_ex_fp_op, id_fp_op_wb, fp_op_fs, fp_op_ft} = fp_info; case( fdest_field ) FWB_fd: id_fp_dst = fd; FWB_rt: id_fp_dst = rt; default: id_fp_dst = rt; // Dummy endcase case( id_fu_needed ) FP_add, FP_mul: illegal_format = fmt != FMT_S; default: illegal_format = 0; endcase case( id_fu_needed ) FP_add : {runit,rwb,rdst,rver,rpc,rdin} = fp_res_pos_add; FP_mul : {runit,rwb,rdst,rver,rpc,rdin} = fp_res_pos_mul; FP_iti, FP_itf : {runit,rwb,rdst,rver,rpc,rdin} = fp_res_pos_int; FP_no : {runit,rwb,rdst,rver,rpc,rdin} = 0; default : begin $stop; {runit,rwb,rdst,rver,rpc,rdin} = 0; end endcase case( 1 ) !fp_op_fs: next_id_ex_fs_src = FSRC_xxx; fp_op_fs_ver == fp_op_fs_wb_ver: next_id_ex_fs_src = FSRC_fpr; {fs,fp_op_fs_ver} == {wn_fp_dst,wn_ver}: next_id_ex_fs_src = FSRC_wb; 1: next_id_ex_fs_src = FSRC_stall; endcase case( 1 ) !fp_op_ft: next_id_ex_ft_src = FSRC_xxx; fp_op_ft_ver == fp_op_ft_wb_ver: next_id_ex_ft_src = FSRC_fpr; {ft,fp_op_ft_ver} == {wn_fp_dst,wn_ver}: next_id_ex_ft_src = FSRC_wb; 1: next_id_ex_ft_src = FSRC_stall; endcase id_next_ver = id_incumb_ver + 1; fp_hold_id = runit != FP_no || next_id_ex_fs_src == FSRC_stall || next_id_ex_ft_src == FSRC_stall; case( opcode ) O_lbu : size_we = {ME_SIZE_1, 1'b0}; O_lw, O_lwc1 : 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( {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 = illegal_format ? EXC_id_ins : 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} = {alu_a_src_maybe == SRC_rs, alu_b_src_maybe == SRC_rt}; endcase case( opcode ) O_bne, O_beq: branch_in_id = 1; default: branch_in_id = 0; endcase case( 1 ) !rs || alu_a_src_maybe != SRC_rs : next_id_ex_alu_a_src = alu_a_src_maybe; rs == id_ex_dst : next_id_ex_alu_a_src = SRC_me; rs == ex_me_dst : next_id_ex_alu_a_src = SRC_wb; default : next_id_ex_alu_a_src = alu_a_src_maybe; endcase if( !rt || alu_b_src_maybe != SRC_rt ) next_id_ex_alu_b_src = alu_b_src_maybe; else if( rt == id_ex_dst ) next_id_ex_alu_b_src = SRC_me; else if( rt == ex_me_dst ) next_id_ex_alu_b_src = SRC_wb; else next_id_ex_alu_b_src = alu_b_src_maybe; //Check for: Store Branch cant_bypass = size_we & 1 || branch_in_id // Load || load_in_ex && ( next_id_ex_alu_a_src == SRC_me || next_id_ex_alu_b_src == SRC_me ); hold_id = fp_hold_id || if_id_occ && ~squash_id && cant_bypass && ( ( 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 }; default: `UNEXPECTED(next_id_ex_imm,immed_fmt); endcase case ( dest_field ) WB_ft, 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 next_id_ex_me = 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; reg [1:0] pc_src_maybe; always @( opcode or gpr_rs_val or gpr_rt_val or id_live ) begin case( opcode ) O_bne : pc_src_maybe = gpr_rs_val != gpr_rt_val ? PC_dsp : PC_npc; O_beq : pc_src_maybe = gpr_rs_val == gpr_rt_val ? PC_dsp : PC_npc; O_j : pc_src_maybe = PC_rgn; default : pc_src_maybe = PC_npc; endcase pc_src = id_live ? pc_src_maybe : PC_npc; end assign squash_if_id = 0; reg [`FPRR_head] res; wire [2:0] next_id_ex_fu = hold_id || !id_live ? FP_no : id_fu_needed; reg [`FPRR_range] fp_res_reg_cpy; always @( posedge clk ) begin res = {next_id_ex_fu, id_fp_op_wb, id_fp_dst, id_next_ver, if_id_pc, tb_if_id_din}; fp_res_reg_cpy = fp_res_reg; case( next_id_ex_fu ) FP_add : fp_res_reg_cpy[`FPRR_add] = res; FP_mul : fp_res_reg_cpy[`FPRR_mul] = res; FP_iti, FP_itf : fp_res_reg_cpy[`FPRR_int] = res; FP_no : ; default : `UNEXPECTED(res,next_id_ex_fu); endcase fp_res_reg <= reset ? 0 : fp_res_reg_cpy >> FPRR_entry_size; id_ex_ft_val <= fpr_ft_val; id_ex_fs_val <= fpr_fs_val; id_ex_fs_src <= next_id_ex_fs_src; id_ex_ft_src <= next_id_ex_ft_src; end always @( posedge clk ) if( reset ) begin:ID_VER integer i; for( i = 0; i < 32; i = i + 1 ) fpr_id_ver[i] <= 0; end else begin if( !hold_id && id_fp_op_wb ) fpr_id_ver[id_fp_dst] <= id_next_ver; end 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_fp_op <= next_id_ex_fp_op; 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; tb_id_ex_din <= tb_if_id_din; id_ex_occ <= id_live; end /// /// FP Execute /// wire [31:0] op_a = id_ex_fs_src == FSRC_fpr ? id_ex_fs_val : fpr_write_val; wire [31:0] op_b = id_ex_ft_src == FSRC_fpr ? id_ex_ft_val : fpr_write_val; wire [31:0] afu_result, mfu_result; wire afu_rdy, mfu_rdy; // Note: Initiation interval not yet coded and so rdy not yet used. add_fu #(AFU_stages,2) afu(afu_rdy,afu_result,op_a,op_b,id_ex_fp_op,clk); add_fu #(MFU_stages,2) mfu(mfu_rdy,mfu_result,op_a,op_b,id_ex_fp_op,clk); /// /// FP Writeback /// wire wn_matches = wn_ver == fpr_id_ver[wn_fp_dst]; assign wn_fpr_write = wn_fpr_writer && wn_matches; always @( posedge clk ) wb_fpr_write <= wn_fpr_write; always @( wb_unit or gpr_write_val or afu_result or mfu_result ) case( wb_unit ) FP_add: fpr_write_val = afu_result; FP_mul: fpr_write_val = mfu_result; FP_itf: fpr_write_val = gpr_write_val; FP_iti, FP_no : fpr_write_val = 0; default: `UNEXPECTED(fpr_write_val,wb_unit); endcase always @( posedge clk ) if( wb_fpr_write ) fpr[wb_fp_dst] <= fpr_write_val; always @( posedge clk ) if( reset ) begin:WB_VER integer i; for(i=0; i<32; i=i+1) fpr_wb_ver[i] <= 0; end else if( wn_fpr_write ) fpr_wb_ver[wn_fp_dst] <= wn_ver; /// /// Execute /// always @( id_ex_alu_a_src or id_ex_rs_val or id_ex_sa or id_ex_npc or ex_me_alu or gpr_write_val ) 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}; SRC_me: alu_a = ex_me_alu; SRC_wb: alu_a = gpr_write_val; 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 or ex_me_alu or gpr_write_val ) case( id_ex_alu_b_src ) SRC_rt: alu_b = id_ex_rt_val; SRC_im: alu_b = id_ex_imm; SRC_me: alu_b = ex_me_alu; SRC_wb: alu_b = gpr_write_val; default: `UNEXPECTED(alu_b,id_ex_alu_b_src); endcase wire [1:0] id_ex_size; wire id_ex_we; assign {id_ex_size,id_ex_we} = id_ex_me; assign load_in_ex = id_ex_occ && !id_ex_we && id_ex_size; 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_size, ex_me_we } <= id_ex_exc || reset ? ME_CONTROL_nop : id_ex_me; ex_me_dst <= reset ? 5'd0 : id_ex_dst; 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 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; /// /// TESTBENCH INTERFACE CODE /// // exemplar translate_off wire wb_fp_occ = wb_unit != FP_iti && wb_unit != FP_no; reg tbi_inst_done; reg [31:0] tbi_done_pc; always @( posedge clk ) tbi_inst_done <= me_wb_occ; always @( posedge clk ) tbi_done_pc <= me_wb_pc; task tbi_poke_gpr; input [4:0] r; input [31:0] value; gpr.storage[r] = value; endtask function [31:0] tbi_peek_gpr; input [4: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 ) iterate_rr(valid,name,pc,din,exc,occ); if( !valid ) stage = 0; end endtask task iterate_rr; output valid; output [15:0] name; output [31:0] pc; output [31:0] din; output [7:0] exc; output occ; integer pos; reg [2:0] unit, ver; reg [4:0] dst; reg [`FPRR_range] res_reg; reg wb; begin exc = 0; if( pos === 32'bx ) pos = 0; if( pos == 0 ) res_reg = fp_res_reg; for( occ = 0; res_reg && !occ; occ = name != 0) begin {unit,wb,dst,ver,pc,din} = res_reg[`FPRR_head]; case( unit ) FP_add: name = pos == 0 ? "WB" : "A1"+AFU_stages-pos; FP_mul: name = pos == 0 ? "WB" : "M1"+MFU_stages-pos; FP_iti, FP_itf, FP_no: name = 0; default: `UNEXPECTED(name,unit); endcase res_reg = res_reg >> FPRR_entry_size; pos = pos + 1; end valid = occ; if( !valid ) pos = 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 add_fu(ready_next_cycle,result,a,b,op,clk); input [31:0] a, b; input [5:0] op; input clk; output [31:0] result; output ready_next_cycle; parameter Stages = 1; parameter II = 1; // exemplar translate_off real ar, br, resultr; reg [64*Stages-1:0] simulated_afu_really_long_shift_register; reg [1:0] busy; assign result = simulated_afu_really_long_shift_register[31:0]; always @( posedge clk ) begin ar = test_proc.ftor(a); br = test_proc.ftor(b); case( op ) cpu_p1.FOP_add: resultr = ar + br; cpu_p1.FOP_sub: resultr = ar - br; cpu_p1.FOP_mul: resultr = ar * br; default: `UNEXPECTED(resultr,op); endcase simulated_afu_really_long_shift_register <= { 32'd0, test_proc.rtof(resultr), simulated_afu_really_long_shift_register[64*Stages-1:64] }; end // exemplar translate_on 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 `define FP_PROC `include "mipspipetb.v"