/// 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<cnt; i=i+1) begin
regname[rno] = name + i;
rno = rno + 1;
end
endtask
task cpi_and_stop;
real cpi;
begin
cpi = icount ? cycle_count / icount : 0;
$display("Average instruction execution time of %.2f CPI.", cpi);
$display("End of testbench run.");
$stop;
end
endtask
initial begin
go = 0;
cycle_count = 0;
icount = 0;
reset = 1;
begin:INITNAMES
integer i;
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";
for(i=0; i<8; i=i+1) statename[i] = "S0"+i;
statename[1] = "IF";
statename[2] = "ID";
statename[3] = "EX";
statename[4] = "ME";
statename[5] = "WB";
end
if( read_trace ) begin
// `include "trace.v"
end
if( write_trace )
tr_fd = $fopen("trace.v");
`include `MIPS_PROG
for(i=0; i<32; i=i+1)
begin:B
reg [31:0] val;
val = i * 10;
p1.cpu1.gpr[i] = val;
p2.gpr[i] = val;
gpr_shadow[i] = val;
gpr_shadow2[i] = val;
end
p1.cpu1.pc = 'h400000;
p2.pc = 'h400000;
p1.cpu1.npc = p1.cpu1.pc + 4;
p2.npc = p1.cpu1.pc + 4;
icount = 0;
go = 1;
changed_reg = 0;
changed_reg_count = 0;
wait( clk == 0 );
@( posedge clk ); @( negedge clk );
reset = 0;
while( !exception ) begin
cycle_count = cycle_count + 1;
if( p1.cpu1.state == 1 ) begin
if( !exception && use_trace ) begin
if( changed_reg_count > 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<<amt ) & mask );
end
end
endfunction
function [31:0] peek_word;
input [31:0] peek_addr;
peek_word = access_mem(peek_addr,3,0,0);
endfunction
function [2:0] poke_word;
input [31:0] poke_addr;
input [31:0] poke_data;
reg [31:0] dummy;
begin
dummy = access_mem(poke_addr,3,1,poke_data);
poke_word = access_mem_err;
end
endfunction
always @( negedge clk ) begin
dout = access_mem(addr,size,we,din);
err = access_mem_err;
end
endmodule