/// LSU EE 3755 -- Fall 2013 -- Computer Organization // /// Very Simple MIPS Implementation // // This implementation shares as much hardware as possible and given // this restriction, does as much in one clock cycle as // possible. Cramming so much work into one clock cycle (particularly // for load instructions) forces the clock frequency to be slow // (which is a bad thing). A better implementation would execute // instructions such as lw over several states. // // Time-stamp: <2 December 2013, 9:39:56 CST, koppel@sky.ece.lsu.edu> // `define MIPS_PROG "uc.v" `default_nettype none module cpu(exc, mem_data_in, mem_addr, omem_size, omem_wr, mem_data_out, mem_error_in, reset, clk); input wire [31:0] mem_data_out; input wire [2:0] mem_error_in; input wire reset,clk; output reg [7:0] exc; output wire [31:0] mem_data_in; output reg [31:0] mem_addr; output reg [1:0] omem_size; output reg omem_wr; // 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; parameter F_swap = 6'h26; // 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; // Processor Control-Logic States // parameter ST_if = 1; parameter ST_id = 2; parameter ST_ni = 3; parameter ST_bt = 4; parameter ST_jt = 5; // ALU Operations // parameter OP_nop = 6'd0; parameter OP_sll = 6'd1; parameter OP_srl = 6'd2; parameter OP_add = 6'd3; parameter OP_sub = 6'd4; parameter OP_or = 6'd5; parameter OP_and = 6'd6; parameter OP_slt = 6'd7; parameter OP_seq = 6'd8; parameter OP_jp = 6'd9; // Control Settings for alu_1 Multiplexer // parameter SRC_rs = 2'd0; parameter SRC_sa = 2'd1; parameter SRC_npc = 2'd2; // Control Settings alu_2 Multiplexer // parameter SRC_rt = 3'd0; parameter SRC_ui = 3'd1; parameter SRC_si = 3'd2; parameter SRC_li = 3'd3; parameter SRC_bi = 3'd4; parameter SRC_ji = 3'd5; parameter SRC_4 = 3'd6; // Control Setting for Writeback Register Number Multiplexer // parameter DST_0 = 2'd0; parameter DST_rt = 2'd1; parameter DST_rd = 2'd2; parameter DST_31 = 2'd3; // Control Setting for Memory Address Multiplexer // parameter MA_pc = 1'b0; parameter MA_alu = 1'b1; /// /// Datapath Registers /// reg [31:0] pc, npc; reg [31:0] ir; // Instruction Register reg [31:0] gpr [0:31]; /// /// Datapath "Wires" /// // Instruction Fields // reg [4:0] rs, rt, rd, sa; reg [5:0] opcode, func; wire [25:0] ii; wire [15:0] immed; // Values Derived From Instruction Fields and Read From Register File // wire [31:0] simm, uimm, limm, bimm, jimm; wire [31:0] rs_val, rt_val, sa_val; reg [4:0] gpr_dst_reg; wire [31:0] gpr_data_in; // // ALU and ALU Connections // wire [31:0] alu_out; reg [31:0] alu_1, alu_2; reg [5:0] oalu; alu our_alu(alu_out, alu_1, alu_2, oalu); // /// Control Logic Declarations // // Write-enable signals for npc, pc, and ir. reg enpc, epc, eir; // Memory Address Multiplexer Select Signal reg xma; // Register File Multiplexer Select Signals reg xrws; reg [1:0] xrwr; // ALU Multiplexers Select Signals reg [1:0] xalu1; reg [2:0] xalu2; // Processor Control Logic State // reg [2:0] state, next_state; reg [75:0] bndl; // Collection of control signals. /// /// Initialization (Simulator Only) /// // cadence translate_off initial begin exc = 0; state = ST_if; func = 0; opcode = 0; ir = 0; xalu1 = 0; xalu2 = 0; oalu = 0; end // cadence translate_on /// /// Register Write /// // Write "real" registers at end of clock cycle. We expect // these to be the only registers that will be synthesized. // always @( posedge clk ) if ( reset ) begin pc <= 32'h400000; npc <= 32'h400004; ir <= 32'd0; state <= ST_if; end else begin if ( epc ) pc <= npc; if ( enpc ) npc <= alu_out; if ( eir ) ir <= mem_data_out; if ( gpr_dst_reg ) gpr[gpr_dst_reg] <= gpr_data_in; state <= next_state; end /// /// Memory Port Connections /// // Memory Address Multiplexer // always @* case ( xma ) MA_pc: mem_addr = pc; MA_alu: mem_addr = alu_out; endcase // Connect memory data in port to rt_val output of register file. // assign mem_data_in = rt_val; /// /// Extract IR Fields and Compute Some Values /// // Extract fields from IR (for convenience). always @* {opcode,rs,rt,rd,sa,func} = ir; assign ii = ir[25:0]; assign immed = ir[15:0]; assign uimm = { 16'h0, immed }; assign simm = { immed[15] ? 16'hffff : 16'h0, immed }; assign limm = { immed, 16'h0 }; assign bimm = { immed[15] ? 14'h3fff : 14'h0, immed, 2'b0 }; assign jimm = { 4'b0, ii, 2'b0 }; assign sa_val = {26'd0,sa}; // /// Register File (GPR) Connections // assign rs_val = gpr[rs]; assign rt_val = gpr[rt]; // GPR Destination Register Number Multiplexer // always @* case ( xrwr ) DST_rt: gpr_dst_reg = rt; DST_rd: gpr_dst_reg = rd; DST_31: gpr_dst_reg = 5'd31; DST_0 : gpr_dst_reg = 5'd0; endcase // Source of data written to the register file. // assign gpr_data_in = xrws ? alu_out : mem_data_out; /// /// ALU Connections /// // Upper ALU Input Multiplexer // always @* case ( xalu1 ) SRC_rs: alu_1 = rs_val; SRC_sa: alu_1 = sa_val; SRC_npc: alu_1 = npc; default: begin alu_1 = rs_val; // cadence translate_off $display("Unexpected ALU 1 source, %d\n", xalu1); $stop; // cadence translate_on end endcase // Lower ALU Input Multiplexer // always @* case ( xalu2 ) SRC_rt: alu_2 = rt_val; SRC_si: alu_2 = simm; SRC_ui: alu_2 = uimm; SRC_li: alu_2 = limm; SRC_bi: alu_2 = bimm; SRC_ji: alu_2 = jimm; SRC_4 : alu_2 = 32'd4; default: begin alu_2 = bimm; // cadence translate_off $display("Unexpected ALU 2 source, %d\n", xalu2); $stop; // cadence translate_on end endcase // Set to 1 if output of ALU is zero. // wire alu_out_z; assign alu_out_z = alu_out[0] == 0; /// /// Control Logic /// always @* begin /// /// Default Values /// // The enable signals which control whether a register is written. enpc = 0; epc = 0; eir = 0; // Memory Control Signals xma = MA_pc; // Multiplexer connected to memory address port. omem_wr = 0; // If 1, write, if 0 read. omem_size = 0; // Size of value loaded from memory; 0 means do nothing. // Register File Control Signals xrwr = DST_0; // Where to get the register number from. xrws = 1; // Source of data to write; 0, memory; 1, alu. // ALU Control Signals xalu1 = SRC_rs; // Upper ALU input. xalu2 = SRC_rt; // Lower ALU input. oalu = OP_add; // ALU operation. case ( state ) /// IF: Instruction Fetch State. // ST_if: begin xma = MA_pc; omem_wr = 0; omem_size = 3; eir = 1; next_state = ST_id; end /// ID: Instruction Decode, Register Read, Execute, Writeback State // Note: It would be better to break this into multiple steps. ST_id: begin // Determine values for xrwr, xalu1, oalu, and xalu2. // case ( opcode ) O_rfmt: // R-Format Instructions case ( func ) // xrwr xalu1 oalu xalu2 F_add: bndl = {DST_rd, SRC_rs, OP_add, SRC_rt}; F_or: bndl = {DST_rd, SRC_rs, OP_or, SRC_rt}; F_sub: bndl = {DST_rd, SRC_rs, OP_sub, SRC_rt}; F_sll: bndl = {DST_rd, SRC_sa, OP_sll, SRC_rt}; F_srl: bndl = {DST_rd, SRC_sa, OP_srl, SRC_rt}; default: // Unrecognized instruction. Set exc to alert testbench. begin bndl = {DST_rd, SRC_sa, OP_sll, SRC_rt}; exc = 1; end endcase // I- and J-Format Instructions // xrwr xalu1 oalu xalu2 O_lbu: bndl = {DST_rt, SRC_rs, OP_add, SRC_si }; O_sb: bndl = {DST_0 , SRC_rs, OP_add, SRC_si }; O_lui: bndl = {DST_rt, SRC_rs, OP_or, SRC_li }; O_addi: bndl = {DST_rt, SRC_rs, OP_add, SRC_si }; O_andi: bndl = {DST_rt, SRC_rs, OP_and, SRC_ui }; O_ori: bndl = {DST_rt, SRC_rs, OP_or, SRC_ui }; O_slti: bndl = {DST_rt, SRC_rs, OP_slt, SRC_si }; O_j: bndl = {DST_0 , SRC_rs, OP_add, SRC_si }; O_bne, O_beq: bndl = {DST_0, SRC_rs, OP_seq, SRC_rt }; default: // Unrecognized instruction. Set exc to alert testbench. begin bndl = {DST_0, SRC_rs, OP_seq, SRC_rt }; exc = 1; end endcase { xrwr, xalu1, oalu, xalu2 } = bndl; // Determine values for omem_size, me_wb, and xrws. // case ( opcode ) O_lbu : begin omem_size = 1; omem_wr = 0; xrws = 0; end O_sb : begin omem_size = 1; omem_wr = 1; xrws = 1; end default : begin omem_size = 0; omem_wr = 0; xrws = 1; end endcase // Determine value for xma. // // Note: for loads and stores xma must be MA_alu. For // other instructions it doesn't matter, so it's being // set to MA_alu to simplify the logic. // xma = MA_alu; // Determine value for next_state. // case ( opcode ) O_bne : next_state = alu_out_z ? ST_bt : ST_ni; O_beq : next_state = alu_out_z ? ST_ni : ST_bt; O_j : next_state = ST_jt; default: next_state = ST_ni; endcase end /// NI: Next Instruction State // Compute the address of the next instruction. ST_ni: begin xalu1 = SRC_npc; xalu2 = SRC_4; oalu = OP_add; enpc = 1; epc = 1; next_state = ST_if; end /// BT: Branch Target State // Compute the address of the branch target. ST_bt: begin xalu1 = SRC_npc; xalu2 = SRC_bi; oalu = OP_add; enpc = 1; epc = 1; next_state = ST_if; end /// JT: Jump Target State // Compute the address of the jump target. ST_jt: begin xalu1 = SRC_npc; xalu2 = SRC_ji; oalu = OP_jp; enpc = 1; epc = 1; next_state = ST_if; end /// Illegal State // It should be impossible to reach this state, but // if we do print a helpful error message. default: begin next_state = ST_if; // Avoid register for next_state; // cadence translate_off $display("Unexpected state, %d.\n", state); $stop; // cadence translate_on end endcase end endmodule module alu(alu_out,alu_1,alu_2,alu_op); output reg [31:0] alu_out; input wire [31:0] alu_1, alu_2; input wire [5:0] alu_op; // 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_jp = 9; always @* case ( alu_op ) OP_add : alu_out = alu_1 + alu_2; OP_and : alu_out = alu_1 & alu_2; OP_or : alu_out = alu_1 | alu_2; OP_sub : alu_out = alu_1 - alu_2; OP_slt : alu_out = {alu_1[31],alu_1} < {alu_2[31],alu_2}; OP_sll : alu_out = alu_2 << alu_1; OP_srl : alu_out = alu_2 >> alu_1; OP_seq : alu_out = alu_1 == alu_2; OP_jp : alu_out = { alu_1[31:26], alu_2[25:0] }; OP_nop : alu_out = 0; default : begin alu_out = 0; // cadence translate_off $display("Unrecognized alu op, %d",alu_op); $stop; // cadence translate_on end endcase endmodule // cadence translate_off module system(exc,reset,clk); input wire reset, clk; output wire [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); mips_memory_1p m1(mem_data_out,mem_err_out,addr,size,we,cpu_data_out,clk); endmodule // Include the testbench code. `include "mips_hc_tb.v" // cadence translate_on