/// LSU EE 3755 -- Fall 2001 -- Computer Organization // /// Practice Final Exam Solution Code // // Includes solution to Problems 1 and 6a and testbench. `define MIPS_PROG "fepp6a.v" module cpu(exc,data_out,addr,size,we,data_in,mem_error_in,reset,clk); input [31:0] data_in; input [2:0] mem_error_in; input reset,clk; output [7:0] exc; output [31:0] data_out, addr; output [1:0] size; output we; reg [31:0] data_out, addr; reg [1:0] size; reg we; reg [7:0] exc; // MIPS Registers // reg [31:0] gpr [0:31]; reg [31:0] pc, npc; reg [31:0] ir; // Instruction Fields // reg [4:0] rs, rt, rd, sa; reg [5:0] opcode, func; reg [25:0] ii; reg [15:0] immed; // Values Derived From Immediates and Read From Register File // reg [31:0] simmed, uimmed, limmed, jimmed; reg [31:0] rs_val, rt_val; // ALU Connections // wire [31:0] alu_out; reg [31:0] alu_a, alu_b; reg [5:0] alu_op; // Processor Control Logic State // reg [3:0] state; reg [4:0] wb_rd; // Register number to write. reg me_we; // we value to use in state st_me reg [1:0] me_size; // size value to use in state st_me alu our_alu(alu_out, alu_a, alu_b, alu_op); // Values for the MIPS funct field. // parameter f_yyy = 6'h3f; // YYY 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; parameter f_rol = 6'h26; // Prob 1 parameter f_slt = 6'h2a; // Values for the MIPS opcode field. // parameter o_xxx = 6'h3e; // XXX 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; // Processor Control Logic States // parameter st_yyy = 8; // YYY parameter st_if = 1; parameter st_id = 2; parameter st_ex = 3; parameter st_ex_addr = 5; parameter st_ex_cond = 6; parameter st_ex_targ = 7; parameter st_me = 4; // ALU Operations // parameter op_nop = 0; parameter op_sll = 1; parameter op_srl = 2; parameter op_add = 3; parameter op_sub = 4; parameter op_or = 5; parameter op_and = 6; parameter op_slt = 7; parameter op_seq = 8; parameter op_rol = 9; // Prob 1 /// Set Memory Connection Values: addr, we, and size. // always @( state or pc or alu_out or me_size or me_we ) case( state ) st_if : begin addr = pc; we = 0; size = 3; end st_yyy : begin addr = alu_b; we = me_we; size = me_size; end // YYY st_me : begin addr = alu_out; we = me_we; size = me_size; end default : begin addr = pc; we = 0; size = 0; end // Note: addr is set for default case to simplify synthesized hardware. endcase always @( posedge clk ) if( reset ) begin state = st_if; exc = 0; end else case ( state ) /// Instruction Fetch st_if: begin ir = data_in; state = st_id; end /// Instruction Decode (and Register Read) st_id: begin {opcode,rs,rt,rd,sa,func} = ir; ii = ir[25:0]; immed = ir[15:0]; simmed = { immed[15] ? 16'hffff : 16'h0, immed }; uimmed = { 16'h0, immed }; jimmed = { 6'h0, ii }; limmed = { immed, 16'h0 }; rs_val = gpr[rs]; rt_val = gpr[rt]; // Set alu_a, alu_b, alu_op, and wb_rd. // case( opcode ) o_rfmt: // R-Format Instructions case ( func ) f_yyy : begin alu_a = sa; alu_op = op_add; // YYY alu_b = rt_val; wb_rd = rt; end f_add : begin alu_a = rs_val; alu_op = op_add; alu_b = rt_val; wb_rd = rd; end f_sub : begin alu_a = rs_val; alu_op = op_sub; alu_b = rt_val; wb_rd = rd; end f_sll : begin alu_a = sa; alu_op = op_sll; alu_b = rt_val; wb_rd = rd; end f_rol : begin alu_a = sa; alu_op = op_rol; // Prob 1 alu_b = rt_val; wb_rd = rd; end f_slt : begin alu_a = rs_val; alu_op = op_slt; alu_b = rt_val; wb_rd = rd; end default : begin alu_a = rs_val; alu_op = op_nop; alu_b = rt_val; wb_rd = 0; exc = 1; end endcase // I- and J-Format Instructions o_xxx: begin alu_a = 0; alu_op = op_add; // XXX alu_b = jimmed; wb_rd = 25; end o_lbu: begin alu_a = rs_val; alu_op = op_add; alu_b = simmed; wb_rd = rt; end o_sb: begin alu_a = rs_val; alu_op = op_add; alu_b = simmed; wb_rd = 0; end o_lui: begin alu_a = rs_val; alu_op = op_or; alu_b = limmed; wb_rd = rt; end o_addi: begin alu_a = rs_val; alu_op = op_add; alu_b = simmed; wb_rd = rt; end o_andi: begin alu_a = rs_val; alu_op = op_and; alu_b = uimmed; wb_rd = rt; end o_ori: begin alu_a = rs_val; alu_op = op_or; alu_b = uimmed; wb_rd = rt; end o_slti: begin alu_a = rs_val; alu_op = op_slt; alu_b = simmed; wb_rd = rt; end o_j: begin alu_a = rs_val; alu_op = op_nop; alu_b = simmed; wb_rd = 0; end o_bne, o_beq: begin alu_a = rs_val; alu_op = op_seq; alu_b = rt_val; wb_rd = 0; end default:begin alu_a = rs_val; alu_op = op_nop; alu_b = simmed; wb_rd = 0; exc = 1; end endcase // Needed for a store instruction, doesn't hurt others. data_out = rt_val; // Set me_size and me_wb // case( opcode ) o_lbu : begin me_size = 1; me_we = 0; end o_sb : begin me_size = 1; me_we = 1; end o_rfmt : case ( func ) f_yyy : begin me_size = 3; me_we = 0; end // YYY default : begin me_size = 0; me_we = 0; end endcase default : begin me_size = 0; me_we = 0; end endcase pc = npc; // Set npc, branch instruction may change npc. // case( opcode ) o_j : npc = { pc[31:28], ii, 2'b0 }; default : npc = pc + 4; endcase case( opcode ) o_lbu, o_sb : state = st_ex_addr; o_bne, o_beq : state = st_ex_cond; o_j : state = st_if; o_rfmt : case ( func ) f_yyy : state = st_yyy; // YYY default : state = st_ex; endcase default : state = st_ex; endcase end /// Instruction YYY st_yyy: // YYY begin if( wb_rd ) gpr[wb_rd] = alu_out; alu_a = rs_val; alu_b = data_in; wb_rd = rd; state = st_ex; end /// Execute (ALU instructions) st_ex: begin if( wb_rd ) gpr[wb_rd] = alu_out; state = st_if; end /// Execute (Compute Effective Address for Loads and Stores) st_ex_addr: begin state = st_me; end /// Execute (Compute Branch Condition) st_ex_cond: begin if( opcode == o_beq == alu_out[0] ) begin alu_a = pc; alu_b = simmed << 2; alu_op = op_add; state = st_ex_targ; end else begin state = st_if; end end /// Execute (Compute Branch Target) st_ex_targ: begin npc = alu_out; state = st_if; end /// Memory st_me: begin if( wb_rd ) gpr[wb_rd] = data_in; state = st_if; end default: begin $display("Unexpected state."); $stop; end endcase endmodule module alu(alu_out,alu_a,alu_b,alu_op); output [31:0] alu_out; input [31:0] alu_a, alu_b; input [5:0] alu_op; reg [31:0] alu_out; // Control Signal Value Names parameter op_nop = 0; parameter op_sll = 1; parameter op_srl = 2; parameter op_add = 3; parameter op_sub = 4; parameter op_or = 5; parameter op_and = 6; parameter op_slt = 7; parameter op_seq = 8; parameter op_rol = 9; 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_sub : alu_out = alu_a - alu_b; // op_slt : alu_out = alu_a < alu_b; op_slt : alu_out = ( {alu_a[31],alu_a} - {alu_b[31],alu_b} ) >> 32; op_sll : alu_out = alu_b << alu_a; op_srl : alu_out = alu_b >> alu_a; op_rol : alu_out = ( alu_b << alu_a ) | ( alu_b >> ( 32 - alu_a ) ); op_seq : alu_out = alu_a == alu_b; op_nop : alu_out = 0; default : begin alu_out = 0; $stop; end endcase endmodule // exemplar translate_off module system(exc,reset,clk); input reset,clk; output [7:0] exc; wire [31:0] cpu_data_out, addr, mem_data_out; wire [2:0] mem_err_out; wire [1:0] size; wire we; cpu cpu1(exc,cpu_data_out,addr,size,we,mem_data_out,mem_err_out,reset,clk); memory_3 m1(mem_data_out,mem_err_out,addr,size,we,cpu_data_out,clk); endmodule `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)] module test_proc(); parameter read_trace = 0; parameter write_trace = 0; parameter shadow_trace = 1; parameter tr_size = 100; parameter use_trace = read_trace | shadow_trace; wire [7:0] exception; reg reset, clk, clk2; wire exc; system p1(exception,reset,clk); tb_proc p2(exc,clk2); reg [31:0] gpr_shadow [0:31]; reg [31:0] gpr_shadow2 [0:31]; reg [319:0] isource[p1.m1.text_base>>2:p1.m1.text_base+p1.m1.text_size>>2-1]; reg [31:0] regname [0:31]; reg [15:0] statename [0:7]; integer i, reg_old, reg_new; reg go; real cycle_count; integer icount; integer changed_reg, changed_reg_count; reg [31:0] tr_pc_a [0:tr_size]; reg [31:0] tr_regv_a [0:tr_size]; reg [4:0] tr_regno_a [0:tr_size]; reg [31:0] tr_pc; reg [31:0] tr_regv; reg [4:0] tr_regno; integer tr_fd, tr_icount_end; always wait( go ) begin clk = 0; #9; clk = 1; #1; end task initmem; input [31:0] addr; input [31:0] text; input [319:0] source; reg [2:0] err; begin err = p1.m1.poke_word(addr,text); if( err ) begin $display("Error %d initializing text address 0x%h.", err, addr); $stop; end {p2.mem[addr],p2.mem[addr+1],p2.mem[addr+2],p2.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; reg [2:0] err; begin err = p1.m1.poke_word(addr,word); if( err ) begin $display("Error %d initializing data address 0x%h.", err, addr); $stop; end daddr = `A(addr); {p2.mem[daddr],p2.mem[daddr+1],p2.mem[daddr+2],p2.mem[daddr+3]} = word; end endtask task get_trace_record; output [31:0] tr_pc; output [5:0] tr_regno; output [31:0] tr_regv; integer i; reg [31:0] cpy_reg [0:31]; if( read_trace ) begin tr_pc = tr_pc_a[icount]; tr_regno = tr_regno_a[icount]; tr_regv = tr_regv_a[icount]; end else if ( write_trace ) begin $fdisplay(tr_fd, " tr_regno_a[%d] = %d; tr_regv_a[%d] = 'h%h;", icount, changed_reg, icount, gpr_shadow[changed_reg]); $fdisplay(tr_fd," tr_pc_a[%d] = 'h%h;", icount,p1.cpu1.pc); end else if ( shadow_trace ) begin tr_pc = p2.pc; tr_regno = 0; tr_regv = 0; for(i=0; i<32; i=i+1) if( gpr_shadow2[i] != p2.gpr[i] ) begin tr_regno = i; tr_regv = p2.gpr[i]; gpr_shadow2[i] = tr_regv; end p2.step; end endtask integer rno; task initregs; input [31:0] name; input [3:0] cnt; integer i; for(i=0; i 2 ) begin $display("Too many registers, %d, changed.\n", changed_reg_count); cpi_and_stop; end begin:C integer i; integer errs; errs = 0; for(i=0; i<32; i=i+1) begin if( p1.cpu1.gpr[i] !== p2.gpr[i] ) begin $display("FAIL: Wrong value in register %d (%s): 0x%h (correct) != 0x%h", i, regname[i], p2.gpr[i], p1.cpu1.gpr[i]); errs = errs + 1; end if( errs ) cpi_and_stop; end end end get_trace_record(tr_pc,tr_regno,tr_regv); icount = icount + 1; changed_reg_count = 0; changed_reg = 0; $display("%s PC 0x%h: %-s", statename[p1.cpu1.state], p1.cpu1.pc, isource[p1.cpu1.pc>>2] ); if( use_trace && tr_pc !== p1.cpu1.pc ) begin $display("PC mismatch at instruction %d, 0x%h (correct) != %h", icount, tr_pc, p1.cpu1.pc); cpi_and_stop; end end @( negedge clk ); for(i=0; i<32; i=i+1) if( gpr_shadow[i] !== p1.cpu1.gpr[i] ) begin changed_reg = i; changed_reg_count = changed_reg_count + 1; reg_old = gpr_shadow[i]; reg_new = p1.cpu1.gpr[i]; $display(" Register %d (%s): 0x%h (%d) -> 0x%h (%d)", i, regname[i], gpr_shadow[i], reg_old, p1.cpu1.gpr[i], reg_new); gpr_shadow[i] = p1.cpu1.gpr[i]; 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.cpu1.ir == 'hc ) $display("Ending at a syscall instruction."); else $display("Illegal instruction exception at address 0x%h", p1.cpu1.pc); end cpi_and_stop; end endmodule module tb_proc(exc,clk); 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; parameter f_yyy = 6'h3f; parameter o_xxx = 6'h3e; // Values for funct field. parameter f_sll = 6'h0; parameter f_srl = 6'h2; parameter f_sllv = 6'h4; parameter f_srlv = 6'h6; parameter f_add = 6'h20; parameter f_sub = 6'h22; parameter f_or = 6'h25; parameter f_slt = 6'h2a; // 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_sltiu = 6'hb; 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 task step; 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_yyy : begin:Y reg [31:0] ma, new_rt; ma = `A(gpr[rt]); new_rt = gpr[rt] + sa; gpr[rd] = gpr[rs] + {mem[ma],mem[ma+1],mem[ma+2],mem[ma+3]}; gpr[rt] = new_rt; end f_slt : begin:B integer a, b; a = gpr[rs]; b = gpr[rt]; gpr[rd] = a < b; end f_sllv : gpr[rd] = gpr[rt] << ( gpr[rs] & 32'h1f ); f_srlv : gpr[rd] = gpr[rt] >> ( gpr[rs] & 32'h1f ); 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_sltiu : gpr[rt] = gpr[rs] < simm16; o_slti : begin:A integer a, b; a = gpr[rs]; b = simm16; gpr[rt] = a < b; end o_addi : gpr[rt] = gpr[rs] + simm16; o_ori : gpr[rt] = gpr[rs] | uimm16; o_xxx : gpr[rt] = { 6'b0, ii }; 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 endtask endmodule module memory_3(dout,err,addr,size,we,din,clk); input [31:0] addr; input [1:0] size; input we; input [31:0] din; input clk; output [31:0] dout; output [2:0] err; reg [31:0] dout; reg [2:0] err; parameter text_base = 'h400000; parameter text_size = 'h100; parameter data_base = 'h10010000; parameter data_size = 'h100; parameter err_none = 0; parameter err_bus = 1; // Bad alignment. parameter err_seg = 2; // Bad address. reg [31:0] storage [0:(text_size+data_size)>>2]; reg [31:0] saddr, mask, rd; reg [4:0] amt; function [31:0] addr_to_saddr; input [31:0] a; if( a >= text_base && a < text_base + text_size ) begin addr_to_saddr = a - text_base >> 2; end else if( a >= data_base && a < data_base + data_size ) begin addr_to_saddr = a - data_base + text_size >> 2; end else begin addr_to_saddr = -1; end endfunction reg [2:0] access_mem_err; function [31:0] access_mem; input [31:0] addr; input [1:0] size; input we; input [31:0] din; begin saddr = addr_to_saddr(addr); if( !size ) begin access_mem_err = err_none; access_mem = 0; end else if( saddr == -1 ) begin access_mem_err = err_seg; access_mem = 32'bz; end else begin access_mem_err = addr << 3 - size & 3 ? err_bus : err_none; amt = 3 - addr[1:0] - { &size, size[1] } << 3; mask = ( ( 1 << ( 8 << (size-1) ) ) - 1 ) << amt; rd = storage[ saddr ]; access_mem = err == err_bus ? 32'bzx : ( rd & mask ) >> amt; if( we ) storage[ saddr ] = ( rd & ~mask ) | ( (din<