////////////////////////////////////////////////////////////////////////////////
///
/// Solution to LSU EE 3755 Fall 2001 Homework 6
///
////////////////////////////////////////////////////////////////////////////////
/// Problem 1 and 2 Solution
///
`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 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;
// Values for funct field.
parameter f_sll = 6'h0;
parameter f_srl = 6'h2;
parameter f_jalr = 6'h9; // HW6: New function code.
parameter f_add = 6'h20;
parameter f_sub = 6'h22;
parameter f_or = 6'h25;
parameter f_nor = 6'h27; // HW6: New function code.
// 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_jalr : {nnpc,gpr[rd]}={gpr[rs],nnpc}; // HW6: jalr
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_nor : gpr[rd] = ~( gpr[rs] | gpr[rt] ); // HW6: nor
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
module orig_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;
// 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
module test_shell();
test_proc #(1) s();
endmodule
module test_sol();
test_proc #(2) s();
endmodule
module test_prog();
test_proc #(4) 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;
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<cnt; i=i+1) begin
regname[rno] = name + i;
rno = rno + 1;
end
endtask
initial begin
begin
regname[0] = "zero";
regname[1] = "at";
rno = 2;
initregs("v0",2);
initregs("a0",4);
initregs("t0",8);
initregs("s0",8);
initregs("t8",2);
initregs("k0",2);
regname[28] = "gp";
regname[29] = "sp";
regname[30] = "fp";
regname[31] = "ra";
end
case( mode )
mode_test_solution:
begin
read_trace = 1;
write_trace = 0;
`include "/home/classes/ee3755/com/v/hw6_test_solution_object.v"
`include "/home/classes/ee3755/com/v/hw6_test_solution_trace.v"
$display("Testing Problem 1 and 2 solution.");
end
mode_test_shell:
begin
read_trace = 1;
write_trace = 0;
`include "/home/classes/ee3755/com/v/hw6_test_shell_object.v"
`include "/home/classes/ee3755/com/v/hw6_test_shell_trace.v"
$display("Testing solution shell (should pass before and after modification).");
end
mode_write_trace:
begin
read_trace = 0;
write_trace = 1;
`ifdef MIPS_PROG
tr_fd = $fopen("hw6_trace.v");
`include `MIPS_PROG
$display("Running user program %s",`MIPS_PROG);
`else
//`include "hw6_test_solution_object.v"
// tr_fd = $fopen("hw6_test_solution_trace.v");
$display("To run this test define MIPS_PROG. See comment.");
$stop;
`endif
end
`ifdef READ_TRACE
mode_read_trace:
begin
read_trace = 1;
write_trace = 0;
`include `MIPS_PROG
`include "hw6_trace.v"
$display("Consuming trace for user program %s.",`MIPS_PROG);
end
`endif
default:
begin
$display("Unknown test mode.");
$stop;
end
endcase
if( write_trace ) begin
end
for(i=0; i<32; i=i+1) p1.gpr[i] = i*10;
for(i=0; i<32; i=i+1) gpr_shadow[i] = p1.gpr[i];
p1.pc = 'h400000;
p1.npc = p1.pc + 4;
icount = 0;
wait( exception === 0 );
wait( clk == 0 );
while( !exception ) begin
icount = icount + 1;
$display("PC 0x%h: %-s",p1.pc,isource[p1.pc>>2]);
if( write_trace ) $fdisplay(tr_fd," tr_pc[%d] = 'h%h;",icount,p1.pc);
if( read_trace && tr_pc[icount] !== p1.pc ) begin
$display("PC mismatch at instruction %d, 0x%h (correct) != %h",
icount, tr_pc[icount], p1.pc);
$stop;
end
@( negedge clk );
changed_reg = 0;
changed_reg_count = 0;
for(i=0; i<32; i=i+1)
if( gpr_shadow[i] !== p1.gpr[i] ) begin
changed_reg = i;
changed_reg_count = changed_reg_count + 1;
reg_old = gpr_shadow[i];
reg_new = p1.gpr[i];
$display(" Register %d (%s): 0x%h (%d) -> 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