/// LSU EE 3755 -- Fall 2013 -- Computer Architecture // /// MIPS Processor Functional Simulator // // Implements a small subset of MIPS32 instructions and features. // // Time-stamp: <22 November 2013, 13:29:30 CST, koppel@sky.ece.lsu.edu> // /// Dependencies /// To compile run this the following is also needed: // // mips_fs_tb.v The testbench. It is included in here for convenience. // mipsmemmacros.v Macros needed for memory access. // // An "assembled" program, for example tthird.v. The name of the // file is specified in the MIPS_PROG macro, the testbench uses the // macro to include the file. /// Limitations // // This code will probably never implement all of MIPS32. The intent // is to demonstrate basic MIPS simulation techniques as a first step // towards developing synthesizable and implementatble descriptions. // /// Specify Program to Load // // A quick-and-dirty method of loading a program is used. // 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 "tthird.v" `define MIPS_PROG "tfirst-live.v" //`define MIPS_PROG "tfirst.v" `include "mipsmemmacros.v" module proc(exc,clk); input wire clk; output reg exc; // Storage defined by the MIPS instruction set. // reg [31:0] pc, npc; reg [31:0] gpr [0:31]; // Memory. // // The full range of addresses would be mem [0:'hffffffff] however // that might make simulation too slow. The range of addresses below // covers the region of memory used for code and data by the Spim // simulator, which we are using to prepare programs for this // simulator. // reg [7:0] mem ['h400000:'h400200]; // // Storage needed to implement MIPS. // reg [31:0] nnpc; reg [31:0] ir; // Instruction Fields // reg [5:0] opcode, func; reg [4:0] rs, rt, rd, sa; reg [15:0] immed; reg [25:0] ii; // Values retrieved from the register file and memory. // reg [31:0] rs_val, rt_val; reg [7:0] md_byte; reg [15:0] md_half; // Values computed using instruction fields and register data. // reg [31:0] simmed, uimmed; reg [31:0] addr; // Used by load and store instructions. // Instruction opcodes. (Not a complete list.) // parameter F_add = 6'h20; parameter F_sll = 6'h0; parameter F_srl = 6'h2; parameter F_sub = 6'h22; parameter F_or = 6'h25; parameter F_jr = 6'h8; // Instruction function-field values. (Not a complete list.) // parameter O_tyr = 6'h0; parameter O_addi = 6'h8; parameter O_j = 6'h2; parameter O_jal = 6'h3; parameter O_beq = 6'h4; parameter O_bne = 6'h5; 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_lhu = 6'h25; parameter O_lh = 6'h21; parameter O_lbu = 6'h24; parameter O_lb = 6'h20; parameter O_sw = 6'h2b; parameter O_sb = 6'h28; always @( posedge clk ) begin /// Execute Instruction at pc. // // Entire execution is in this one clock cycle. // Since this is a functional simulator, that's fine. // If this were to be synthesized the resulting hardware // would be slow and expensive. // Load instruction from memory. // ir = { mem[pc], mem[pc+1], mem[pc+2], mem[pc+3] }; // Split instruction into Type-r fields. // {opcode, rs, rt, rd, sa, func} = ir; // Extract immed, in case instruction is Type I. // immed = ir[15:0]; // Extract ii, in case instruction is Type J. // ii = ir[25:0]; // Prepare signed immediate. // simmed = {immed[15] ? 16'hffff : 16'h0, immed}; // Prepare unsigned immediate. // uimmed = { 16'h0, immed }; // Retrieve register values. // rs_val = gpr[rs]; rt_val = gpr[rt]; // Provisionally increment next-next program counter. // (If we are executing a taken control-transfer insn nnpc will be // changed, that's why it's provisional.) // nnpc = npc + 4; // Compute address used for load and store instructions. // // (The 'A macro adjusts the address, this is to accommodate our // quick-and-dirty machine model. The next processor model // doesn't need to do this.) // addr = `A( rs_val + simmed ); case ( opcode ) // Handle Type-R instructions. // O_tyr: // Look at the func field to determine what kind of type R // instruction it is. // case ( func ) F_add: gpr[rd] = rs_val + rt_val; F_sub: gpr[rd] = rs_val - rt_val; F_sll: gpr[rd] = rt_val << sa; F_jr: nnpc = rs_val; // cadence translate_off default: begin $display("Unexpected func-field value: 0x%x\n",func); $stop; end // cadence translate_on endcase // // Handle Type-I and Type-J instructions. // O_addi: gpr[rt] = rs_val + simmed; O_andi: gpr[rt] = rs_val & uimmed; O_ori: gpr[rt] = rs_val | uimmed; O_slti: gpr[rt] = rs_val < simmed; O_lui: gpr[rt] = {immed,16'h0}; // Control-Transfer Instructions // O_bne: if ( rt_val != rs_val ) nnpc = npc + ( simmed << 2 ); O_beq: if ( rt_val == rs_val ) nnpc = npc + ( simmed << 2 ); O_j: nnpc = {npc[31:28],ii,2'd0}; O_jal: begin gpr[31] = nnpc; nnpc = {npc[31:28],ii,2'd0}; end // Load and Store Instructions // O_lw: gpr[rt] = { mem[addr], mem[addr+1], mem[addr+2], mem[addr+3] }; O_lhu: gpr[rt] = { 16'h0, mem[addr], mem[addr+1] }; O_lh: begin md_half = { mem[addr], mem[addr+1] }; gpr[rt] = { md_half[15] ? 16'hffff : 16'h0, md_half }; end O_lbu: gpr[rt] = { 24'b0, mem[addr] }; O_lb: begin md_byte = mem[addr]; gpr[rt] = { md_byte[7] ? 24'hffffff : 24'h0, md_byte }; end O_sb: mem[addr] = rt_val; // cadence translate_off default: begin $display("Unexpected opcode: 0x%x\n",opcode); $stop; end // cadence translate_on endcase // Reset zero register. gpr[0] = 0; // Update the program counters. pc = npc; // pc now holds the next instruction we execute ... npc = nnpc; // ... and npc now holds the instruction after that. end endmodule // cadence translate_off `include "mips_fs_tb.v" // cadence translate_on