//////////////////////////////////////////////////////////////////////////////// /// /// Template for LSU EE 3755 Fall 2001 Homework 6 /// /// Name: /// Instructions: // // Copy this to a file named hw06.v to directory ~/hw in your // class account. (~ is your home directory.) Use this // file for your solution. Your entire solution should be in // this file. // // Do not rename the modules in this file and be sure to use the // directory and filename given above. // // Modify the same proc module for Problems 1 and 2. // Assignment: http://www.ece.lsu.edu/ee3755/2001f/hw06.pdf //////////////////////////////////////////////////////////////////////////////// `define MEMBASE 'h400000 `define DATABASE 'h10010000 `define TEXTSIZE 'h100 `define MEMSIZE 'h200 `define MEMRANGE `MEMBASE:`MEMBASE+`MEMSIZE-1 `define A(addr) ((addr)-`DATABASE+`MEMBASE+`TEXTSIZE) `define MEM(addr) mem[((addr)-`DATABASE+`MEMBASE+`TEXTSIZE)] //////////////////////////////////////////////////////////////////////////////// /// Problem 1 and 2 /// // Problem 1: Implement nor // Problem 2: Implement jalr // MIPS Documentation: http://www.ece.lsu.edu/ee4720/mips32v2.pdf // Put the solutions to Problem 1 and 2 in proc module below. Don't copy // or rename it. // // A correct solution requires only four lines of Verilog code, and // two of those are parameters. // The testbench for the solution is in module test_sol. // It runs code in the file /home/classes/ee3755/com/v/hw6_test_solution.s // A testbench for the unmodified proc (to make sure a change hasn't // broken anything) is in module test_shell. // It runs code in the file /home/classes/ee3755/com/v/hw6_test_shell.s // The testbench initializes register 1 to 10, 2 to 20, 3 to 30, etc. // If you want to run your own MIPS code on the proc module do the following: // // Test the code using SPIM. // // Create the quick-and-dirty object file by pressing S-F9 (shift F9) // in an Emacs buffer with the assembly code (using the class // installation of Emacs). // // Uncomment the define below, and if necessary change "myprog.v". // Note that a .v file is generated from a .s file. // // `define MIPS_PROG "myprog.v" // // Simulate the module test_prog. module proc(exc,clk); // Strawman MIPS Implementation input clk; output exc; reg exc; reg [7:0] mem [`MEMRANGE]; reg [31:0] pc, npc, nnpc, ir; reg [31:0] gpr [0:31]; reg [5:0] opcode, funct; reg [4:0] rs, rt, rd, sa; reg [15:0] immed; reg [25:0] ii; reg [31:0] uimm16, simm16; reg [31:0] branch_target; // Values for funct field. parameter f_sll = 6'h0; parameter f_srl = 6'h2; parameter f_add = 6'h20; parameter f_sub = 6'h22; parameter f_or = 6'h25; // Values for opcode field. 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; initial begin exc = 0; pc = 0; npc = 4; end always @( posedge clk ) begin ir = {mem[pc],mem[pc+1],mem[pc+2],mem[pc+3]}; // R Format {opcode,rs,rt,rd,sa,funct} = ir; nnpc = npc + 4; // May be reassigned below. if( !opcode ) begin // // R-Format Instructions case( funct ) f_sll : gpr[rd] = gpr[rt] << sa; f_srl : gpr[rd] = gpr[rt] >> sa; f_add : gpr[rd] = gpr[rs] + gpr[rt]; f_or : gpr[rd] = gpr[rs] | gpr[rt]; f_sub : gpr[rd] = gpr[rs] - gpr[rt]; default : exc = 1; endcase end else begin // // I- and J-Format Instructions // I Format (Also uses opcode, rs, and rt.) immed = ir[15:0]; // J Format (Also uses opcode.) ii = ir[25:0]; uimm16 = { 16'b0, immed }; simm16 = immed[15] ? { 16'hffff, immed } : uimm16; branch_target = npc + ( simm16 << 2 ); case( opcode ) o_j : nnpc = {npc[31:28],ii,2'b0}; o_beq : if( gpr[rs] == gpr[rt] ) nnpc = branch_target; o_bne : if( gpr[rs] != gpr[rt] ) nnpc = branch_target; o_andi : gpr[rt] = gpr[rs] & uimm16; o_slti : gpr[rt] = gpr[rs] < simm16; o_addi : gpr[rt] = gpr[rs] + simm16; o_ori : gpr[rt] = gpr[rs] | uimm16; o_lui : gpr[rt] = { immed, 16'b0 }; o_lbu : gpr[rt] = { 24'b0, `MEM( gpr[rs] + simm16 ) }; o_sb : `MEM( gpr[rs] + simm16 ) = gpr[rt]; default : exc = 1; endcase end gpr[0] = 0; pc = npc; npc = nnpc; end endmodule //////////////////////////////////////////////////////////////////////////////// /// Testbench for Problems 1 and 2 /// module test_shell(); test_proc #(1) s(); endmodule module test_sol(); test_proc #(2) s(); endmodule module test_prog(); test_proc #(5) s(); endmodule module test_proc(); parameter mode = 2; parameter mode_test_shell = 1; parameter mode_test_solution = 2; parameter mode_read_trace = 3; parameter mode_write_trace = 4; parameter mode_just_run = 5; integer read_trace, write_trace; parameter tr_size = 50; wire exception; reg clk; proc p1(exception,clk); reg [31:0] gpr_shadow [0:31]; reg [319:0] isource[`MEMBASE>>2:((`MEMBASE+`MEMSIZE)>>2)-1]; reg [31:0] regname [0:31]; integer i, reg_old, reg_new; integer icount; reg [31:0] tr_pc [1:tr_size]; reg [31:0] tr_regv [1:tr_size]; reg [4:0] tr_regno [1:tr_size]; integer tr_fd, tr_icount_end; integer changed_reg, changed_reg_count; always begin clk = 0; #10; clk = 1; #10; end task initmem; input [31:0] addr; input [31:0] text; input [319:0] source; begin if( addr & 3 ) begin $display("Unaligned instruction address, 0x%h",addr); $stop; end if( addr < `MEMBASE || addr >= `MEMBASE + `TEXTSIZE ) begin $display("Address 0x%h out of range.",addr); $stop; end {p1.mem[addr],p1.mem[addr+1],p1.mem[addr+2],p1.mem[addr+3]} = text; while( source[319:312] === 8'b0 ) source = {source[311:0]," "}; isource[addr>>2] = source; end endtask task initdmem; input [31:0] addr; input [31:0] word; reg [31:0] daddr; begin if( addr & 3 ) begin $display("Unaligned data address, 0x%h",addr); $stop; end if( addr < `DATABASE || `A(addr) >= `MEMBASE + `MEMSIZE ) begin $display("Data address 0x%h out of range.",addr); $stop; end daddr = `A(addr); {p1.mem[daddr],p1.mem[daddr+1],p1.mem[daddr+2],p1.mem[daddr+3]} = word; end endtask integer rno; task initregs; input [31:0] name; input [3:0] cnt; integer i; for(i=0; i 0x%h (%d)", i, regname[i], gpr_shadow[i], reg_old, p1.gpr[i], reg_new); gpr_shadow[i] = p1.gpr[i]; end if( write_trace ) $fdisplay(tr_fd, " tr_regno[%d] = %d; tr_regv[%d] = 'h%h;", icount, changed_reg, icount, gpr_shadow[changed_reg]); if( !exception && read_trace ) begin if( changed_reg_count > 1 ) begin $display("Too many registers, %d, changed.\n", changed_reg_count); $stop; end if( tr_regno[icount] != changed_reg ) begin $display("FAIL: Wrong register modified %d should change, %d changed.", tr_regno[icount],changed_reg); $stop; end if( tr_regv[icount] != gpr_shadow[changed_reg] ) begin $display("FAIL: Wrong value written. 0x%h (correct) != 0x%h", tr_regv[icount],gpr_shadow[changed_reg]); $stop; end end end if( write_trace ) begin if( icount > tr_size ) $display("WARNING!!! Increase tr_size from %d to at least %d.\n", tr_size,icount); $fdisplay(tr_fd," tr_icount_end = %d;",icount); $fclose(tr_fd); end if( read_trace ) begin if( icount != tr_icount_end ) begin $display("FAIL: Valid instruction unrecognized."); end else begin $display("PASS: Correctly executed %d instructions.",icount); end end if( exception ) begin if( p1.ir == 'hc ) $display("Ending at a syscall instruction."); else $display("Illegal instruction exception at address 0x%h", p1.pc); end $stop; end endmodule