/// LSU EE 3755 -- Fall 2001 -- Computer Organization
//
/// Microcoded MIPS
//
// Time-stamp: <3 December 2001, 16:29:36 CST, koppel@sol>
//
`define MIPS_PROG "prog1.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;
wire [31:0] data_out;
reg [31:0] addr;
reg [7:0] exc;
// MIPS Registers
//
reg [31:0] gpr [0:31];
reg [31:0] pc, npc;
reg [31:0] ir;
// Instruction Fields
//
wire [4:0] rs, rt, rd, sa;
wire [5:0] opcode, func;
wire [25:0] ii = ir[25:0];
wire [15:0] immed = ir[15:0];
assign {opcode,rs,rt,rd,sa,func} = ir;
// Values From Register File
//
wire [31:0] rs_val = gpr[rs];
wire [31:0] rt_val = gpr[rt];
// ALU Connections
//
wire [31:0] alu_out;
reg [31:0] alu_a, alu_b;
reg [5:0] alu_op;
alu our_alu(alu_out, alu_a, alu_b, alu_op);
// Branch Condition
//
reg branch_cond;
// Values for the MIPS 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 the MIPS opcode field.
//
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;
// ALU Operations
//
parameter op_nop = 5'd0;
parameter op_sll = 5'd1;
parameter op_srl = 5'd2;
parameter op_add = 5'd3;
parameter op_sub = 5'd4;
parameter op_or = 5'd5;
parameter op_and = 5'd6;
parameter op_slt = 5'd7;
parameter op_seq = 5'd8;
parameter op_sne = 5'd9;
parameter op_exc = 5'd31;
///
/// Control Signal Constants
///
// PC/NPC Assignment
//
parameter PC_000 = 2'd0; // Do nothing.
parameter PC_ADV = 2'd1; // npc <- pc
parameter PC_BRN = 2'd2; // npc <- alu_out if branch_cond true.
parameter PC_ALU = 2'd3; // npc <- alu_out
// Memory Address, Write Enable, and Size
//
parameter ADDR_PC = 1'b0;
parameter ADDR_ALU = 1'b1;
parameter WE_0 = 1'b0;
parameter WE_1 = 1'b1;
parameter SIZE_OFF = 2'd0;
parameter SIZE_BYTE = 2'd1;
parameter SIZE_HALF = 2'd2;
parameter SIZE_WORD = 2'd3;
// Memory Operations (For compactness combine three constants.)
//
parameter MEM_IF = {ADDR_PC, WE_0,SIZE_WORD};
parameter MEM_LB = {ADDR_ALU,WE_0,SIZE_BYTE};
parameter MEM_SB = {ADDR_ALU,WE_1,SIZE_BYTE};
parameter MEM_00 = {ADDR_ALU,WE_0,SIZE_OFF};
// IR and branch condition (branch_cond) Register Write Enables
//
parameter IR_000 = 1'b0; // No change to ir.
parameter IR_DIN = 1'b1; // ir <- data_in
parameter BR_000 = 1'b0; // No change to branch_cond.
parameter BR_ALU = 1'b1; // branch_cond <- alu_out.
// GPR Writeback
//
parameter DEST_00 = 2'd0; // Write register zero.
parameter DEST_RD = 2'd1; // Write register specified in rd field.
parameter DEST_RT = 2'd2; // Write register specified in rt field.
parameter GPR_ALU = 1'b0; // Write register with alu_out.
parameter GPR_MEM = 1'b1; // Write register with data_in.
// Handy combinations of ir, branch_cond, and GPR control signal constants.
//
parameter W_RD_ALU = {IR_000, BR_000, DEST_RD, GPR_ALU }; // gpr[rd]=alu_out;
parameter W_RT_ALU = {IR_000, BR_000, DEST_RT, GPR_ALU }; // etc.
parameter W_RT_MEM = {IR_000, BR_000, DEST_RT, GPR_MEM };
parameter W_IR_DIN = {IR_DIN, BR_000, DEST_00, GPR_MEM };
parameter W_BR_ALU = {IR_000, BR_ALU, DEST_00, GPR_MEM };
parameter W_00_000 = {IR_000, BR_000, DEST_00, GPR_MEM };
// ALU Operation Control Signal Constants
//
parameter ALU_DSP = 1'b1; // Get alu operation from dispatch table.
parameter ALU_ADD = 1'b0; // Nothing fancy, just add.
parameter ALU_000 = 1'b0; // Add, but intend to ignore result.
// ALU A Input Control Signal Constants
//
parameter ALU_A_00 = 2'd0;
parameter ALU_A_RS = 2'd1;
parameter ALU_A_SA = 2'd2;
parameter ALU_A_PC = 2'd3;
// ALU B Input Control Signal Constants
//
parameter ALU_B_00 = 3'd0; // rt_val, but intend to ignore result.
parameter ALU_B_RT = 3'd0; // Contents of register number rt
parameter ALU_B_UI = 3'd1; // Unsigned Immediate
parameter ALU_B_SI = 3'd2; // Signed Immediate
parameter ALU_B_BI = 3'd3; // Branch Displacement
parameter ALU_B_LI = 3'd4; // Upper Immediate
parameter ALU_B_PI = 3'd5; // PC to use after a reset.
parameter ALU_B_JI = 3'd6; // Jump Target (region, j and jal instructions)
parameter ALU_B_04 = 3'd7; // Four
// Next Microinstruction Constants
//
parameter NXT_IF = 2'd1; // Go to instruction fetch microinstruction.
parameter NXT_SEQ = 2'd2; // Go to next microinstruction.
parameter NXT_DSP = 2'd3; // Use dispatch table to get next micro_pc.
// Microinstruction Labels (See micro_rom initialization.)
//
parameter MPC_IF = 6'd1;
parameter MPC_ID = 6'd2;
parameter MPC_I1 = 6'd3;
parameter MPC_I2 = 6'd4;
parameter MPC_I3 = 6'd5;
parameter MPC_R2_EX = 6'd6;
parameter MPC_R1_EX = 6'd7;
parameter MPC_IU_EX = 6'd8;
parameter MPC_IS_EX = 6'd9;
parameter MPC_IL_EX = 6'd10;
parameter MPC_BR_CO = 6'd11;
parameter MPC_BR_TA = 6'd12;
parameter MPC_LB_EA = 6'd13;
parameter MPC_LB_ME = 6'd14;
parameter MPC_SB_EA = 6'd15;
parameter MPC_SB_ME = 6'd16;
parameter MPC_JP_EA = 6'd17;
/// Microinstruction ROM and its Program Counter
//
reg [18:0] micro_rom[0:31];
reg [4:0] micro_pc;
// Fetch Microinstruction
//
wire [22:0] micro_ir = micro_rom[micro_pc];
// Microinstruction Fields
//
// Sample: { W_RD_ALU, PC_000, MEM_00, ALU_DSP, ALU_A_RS, ALU_B_RT, NXT_IF };
//
wire ir_en, br_en; // Clock ir, branch_cond.
wire [1:0] dest_field; // Source of register number for gpr write.
wire dest_src; // Source of data for gpr write.
wire [1:0] pc_op; // How to update pc and npc.
wire addr_src; // Source of memory address.
wire we; // Memory's we input.
wire [1:0] size; // Memory's size input.
wire alu_op_src; // Source of alu operation.
wire [1:0] alu_a_src; // Source of alu input a.
wire [2:0] alu_b_src; // Source of alu input b.
wire [1:0] seq; // Source of next microinstruction.
// Assign Microinstruction Fields
//
assign {ir_en, br_en,
dest_field, dest_src,
pc_op,
addr_src, we, size,
alu_op_src, alu_a_src, alu_b_src,
seq } = micro_ir;
/// Dispatch Table ROM
//
reg [10:0] micro_disp[0:127];
// Dispatch Table Address: Combine opcode and func fields
//
wire [6:0] d_tab_addr = opcode == o_rfmt ? {1'b0,func} : {1'b1,opcode};
// Dispatch Table Fields
//
wire [4:0] micro_alu_op; // ALU operation from dispatch table.
wire [4:0] micro_disp_pc; // micro_pc value for MIPS instruction.
// Read Dispatch Table
//
assign {micro_disp_pc, micro_alu_op} = micro_disp[d_tab_addr];
// Advance Microprogram Program Counter
//
always @( posedge clk )
if( reset )
micro_pc = MPC_I1;
else
case( seq )
NXT_SEQ : micro_pc = micro_pc + 1;
NXT_IF : micro_pc = 1;
NXT_DSP : micro_pc = micro_disp_pc;
default : begin micro_pc = 0; $stop; end
endcase
// Select ALU A Input
//
always @( alu_a_src or rs_val or pc or sa or reset )
case( alu_a_src )
ALU_A_00: alu_a = 32'h0;
ALU_A_RS: alu_a = rs_val;
ALU_A_PC: alu_a = pc;
ALU_A_SA: alu_a = sa;
default: begin alu_a = sa; if( !reset ) $stop; end
endcase
// Select ALU B Input
//
always @( alu_b_src or rt_val or immed or pc or ii or reset )
case( alu_b_src )
ALU_B_RT: alu_b = rt_val;
ALU_B_SI: alu_b = { immed[15] ? 16'hffff : 16'h0, immed };
ALU_B_BI: alu_b = { immed[15] ? 14'h3fff : 14'h0, immed, 2'h0 };
ALU_B_UI: alu_b = { 16'h0, immed };
ALU_B_LI: alu_b = { immed, 16'h0 };
ALU_B_JI: alu_b = { pc[31:28], ii, 2'h0 };
ALU_B_PI: alu_b = 32'h400000;
ALU_B_04: alu_b = 32'h4;
default: begin alu_b = rt_val; if( !reset ) $stop; end
endcase
// Select ALU Operation Source
//
always @( alu_op_src or micro_alu_op )
case( alu_op_src )
ALU_ADD: alu_op = op_add; // Add.
ALU_DSP: alu_op = micro_alu_op; // Use dispatch table operation.
endcase
// Set Exception Code (using dispatch table output.)
//
always @( micro_alu_op or reset )
case( micro_alu_op )
op_exc : exc = 1;
default : exc = 0;
endcase
// Select Memory Address Source
//
always @( addr_src or pc or alu_out )
case( addr_src )
ADDR_PC: addr = pc;
ADDR_ALU: addr = alu_out;
endcase
assign data_out = rt_val;
//
/// Register Writes (GPR, pc, ir, etc.)
//
// Write IR and Branch Condition
//
always @( posedge clk ) if( ir_en ) ir = data_in;
always @( posedge clk ) if( br_en ) branch_cond = alu_out[0];
// Write PC or NPC (or neither).
//
always @( posedge clk )
case( pc_op )
PC_000: ;
PC_ADV: pc = npc;
PC_BRN: if( branch_cond ) npc = alu_out;
PC_ALU: npc = alu_out;
endcase
// Write GPR
//
reg [31:0] wb_data;
reg [5:0] wb_rd;
always @( posedge clk ) begin
case( dest_field )
DEST_00: wb_rd = 0;
DEST_RD: wb_rd = rd;
DEST_RT: wb_rd = rt;
default: begin wb_rd = 0; if( !reset ) $stop; end
endcase
case( dest_src )
GPR_ALU: wb_data = alu_out;
GPR_MEM: wb_data = data_in;
endcase
if( wb_rd ) gpr[wb_rd] = wb_data;
end
///
/// Initialize Microprogram ROM and Dispatch Table
///
integer i;
// The always below is an inelegant way to have the synthesis
// program read the micro_rom and micro_disp tables.
always @( posedge clk ) if( i !== 128 ) begin
// Initialize the dispatch table to exceptions.
// Entries that are actually used are re-written further below.
//
for(i=0; i<127; i=i+1) micro_disp[i] = {MPC_IF,op_exc};
// Instruction Fetch and Decode
//
micro_rom[MPC_IF] = { W_IR_DIN, PC_ADV, MEM_IF, ALU_000, ALU_A_00, ALU_B_00, NXT_SEQ };
micro_rom[MPC_ID] = { W_00_000, PC_ALU, MEM_00, ALU_ADD, ALU_A_PC, ALU_B_04, NXT_DSP };
// Initialize PC and NPC (Done on Reset)
//
micro_rom[MPC_I1] = { W_00_000, PC_ALU, MEM_00, ALU_ADD, ALU_A_00, ALU_B_PI, NXT_SEQ };
micro_rom[MPC_I2] = { W_00_000, PC_ADV, MEM_00, ALU_ADD, ALU_A_00, ALU_B_00, NXT_SEQ };
micro_rom[MPC_I3] = { W_00_000, PC_ALU, MEM_00, ALU_ADD, ALU_A_PC, ALU_B_04, NXT_IF };
// Format R, Two-register ALU
//
micro_disp[{1'b0,f_add}] = { MPC_R2_EX,op_add };
micro_disp[{1'b0,f_sub}] = { MPC_R2_EX,op_sub };
micro_disp[{1'b0,f_or}] = { MPC_R2_EX,op_or };
//
micro_rom[MPC_R2_EX] = { W_RD_ALU, PC_000, MEM_00, ALU_DSP, ALU_A_RS, ALU_B_RT, NXT_IF };
// Format R, One-register ALU (sll, slr, etc.)
//
micro_disp[{1'b0,f_sll}] = { MPC_R1_EX,op_sll };
micro_disp[{1'b0,f_srl}] = { MPC_R1_EX,op_srl };
//
micro_rom[MPC_R1_EX] = { W_RD_ALU, PC_000, MEM_00, ALU_DSP, ALU_A_SA, ALU_B_RT, NXT_IF };
// Format I, Unsigned ALU
//
micro_disp[{1'b1,o_andi}] = { MPC_IU_EX,op_and };
micro_disp[{1'b1,o_ori}] = { MPC_IU_EX,op_or };
//
micro_rom[MPC_IU_EX] = { W_RT_ALU, PC_000, MEM_00, ALU_DSP, ALU_A_RS, ALU_B_UI, NXT_IF };
// Format I, Signed ALU
//
micro_disp[{1'b1,o_addi}] = { MPC_IS_EX,op_add };
micro_disp[{1'b1,o_slti}] = { MPC_IS_EX,op_slt };
//
micro_rom[MPC_IS_EX] = { W_RT_ALU, PC_000, MEM_00, ALU_DSP, ALU_A_RS, ALU_B_SI, NXT_IF };
// Format I, Load Upper Immediate
//
micro_disp[{1'b1,o_lui}] = { MPC_IL_EX,op_add }; // rs = 0.
//
micro_rom[MPC_IL_EX] = { W_RT_ALU, PC_000, MEM_00, ALU_DSP, ALU_A_RS, ALU_B_LI, NXT_IF };
// Format I, Branch
//
micro_disp[{1'b1,o_bne}] = { MPC_BR_CO,op_sne };
micro_disp[{1'b1,o_beq}] = { MPC_BR_CO,op_seq };
//
micro_rom[MPC_BR_CO] = { W_BR_ALU, PC_000, MEM_00, ALU_DSP, ALU_A_RS, ALU_B_RT, NXT_SEQ };
micro_rom[MPC_BR_TA] = { W_00_000, PC_BRN, MEM_00, ALU_ADD, ALU_A_PC, ALU_B_BI, NXT_IF };
// Format I, Load Unsigned Byte
//
micro_disp[{1'b1,o_lbu}] = { MPC_LB_EA,op_add };
//
micro_rom[MPC_LB_EA] = { W_00_000, PC_000, MEM_00, ALU_DSP, ALU_A_RS, ALU_B_SI, NXT_SEQ };
micro_rom[MPC_LB_ME] = { W_RT_MEM, PC_000, MEM_LB, ALU_ADD, ALU_A_RS, ALU_B_SI, NXT_IF };
// Format I, Store Byte
//
micro_disp[{1'b1,o_sb}] = { MPC_SB_EA,op_add };
//
micro_rom[MPC_SB_EA] = { W_00_000, PC_000, MEM_00, ALU_DSP, ALU_A_RS, ALU_B_SI, NXT_SEQ };
micro_rom[MPC_SB_ME] = { W_00_000, PC_000, MEM_SB, ALU_ADD, ALU_A_RS, ALU_B_SI, NXT_IF };
// Format J, Jump
//
micro_disp[{1'b1,o_j}] = { MPC_JP_EA,op_add };
//
micro_rom[MPC_JP_EA] = { W_00_000, PC_ALU, MEM_00, ALU_DSP, ALU_A_00, ALU_B_JI, NXT_IF };
end
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_sne = 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[31],alu_a} < {alu_b[31],alu_b};
op_sll : alu_out = alu_b << alu_a;
op_srl : alu_out = alu_b >> alu_a;
op_seq : alu_out = alu_a == alu_b;
op_sne : alu_out = alu_a != alu_b;
op_nop : alu_out = 0;
default : alu_out = 0;
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
`include "mipsmctb.v"