// Beta PC logic
module pc(clk,reset,pcsel,offset,jump_addr,branch_addr,pc,pc_plus_4);
  input clk;
  input reset;                    // forces PC to 0x80000000
  input [2:0] pcsel;              // selects source of next PC
  input [15:0] offset;            // inst[15:0]
  input [31:0] jump_addr;         // from Reg[RA], used in JMP instruction
  output [31:0] branch_addr;      // send to datapath for LDR instruction
  output [31:0] pc;               // used as address for instruction fetch
  output [31:0] pc_plus_4;        // saved in regfile during branches, JMP, traps

  reg [31:0] pc;
  wire [30:0] pcinc;
  wire [31:0] npc;

  // the Beta PC increments by 4, but won't change supervisor bit
  assign pcinc = pc + 4;
  assign pc_plus_4 = {pc[31],pcinc};

  // branch address = PC + 4 + 4*sxt(offset)
  assign branch_addr = {1'b0, pcinc + {{13{offset[15]}},offset[15:0],2'b00}};

  assign npc = reset ? 32'h80000000 :
               (pcsel == 0) ? {pc[31],pcinc} :               // normal
               (pcsel == 1) ? {pc[31],branch_addr[30:0]} :   // branch
               (pcsel == 2) ? {pc[31] & jump_addr[31],jump_addr[30:0]} :  // jump
               (pcsel == 3) ? 32'h80000004 :
               (pcsel == 4) ?  32'h80000008 :               // illop, trap
               32'hXXXXXXXX;                                // catch errors...

  // pc register, pc[31] is supervisor bit and gets special treatment
  always @(posedge clk) pc <= npc;
endmodule


// 2-read, 1-write 32-location register file module regfile(ra1,rd1,ra2,rd2,clk,werf,wa,wd); input [4:0] ra1; // address for read port 1 (Reg[RA]) output [31:0] rd1; // read data for port 1 input [4:0] ra2; // address for read port 2 (Reg[RB], Reg[RC] for ST) output [31:0] rd2; // read data for port 2 input clk; input werf; // write enable, active high input [4:0] wa; // address for write port (Reg[RC]) input [31:0] wd; // write data reg [31:0] registers[31:0]; // the register file itself // read paths are combinational // logic to ensure R31 reads as zero is in main datapath assign rd1 = registers[ra1]; assign rd2 = registers[ra2]; // write port is active only when WERF is asserted always @(posedge clk) if (werf) registers[wa] <= wd; endmodule
module dp_addsub(fn,alu_a,alu_b,result,n,v,z); input fn; // 0 for add, 1 for subtract input [31:0] alu_a; // A operand input [31:0] alu_b; // B operand output [31:0] result; // result output n,v,z; // condition codes computed from result reg n,v,z; reg [31:0] result; always @(fn or alu_a or alu_b) begin: ripple integer i; // FOR loop index, not in hardware reg cin,p,g; // expanded at compile time into many signals reg [31:0] xb; // hold's complement of ALU_B during subtract // simple ripple-carry adder for now xb = fn ? ~alu_b : alu_b; // a - b == a + ~b + 1 cin = fn; // carry in is 0 for add, 1 for sub // remember: this FOR is expanded at *compile* time for (i = 0; i < 32; i = i + 1) begin p = alu_a[i] ^ xb[i]; // carry propagate g = alu_a[i] & xb[i]; // carry generate result[i] = p ^ cin; cin = g | (p & cin); // carry into next stage end n = result[31]; // negative z = ~|result; // zero v = (alu_a[31] & xb[31] & !n) | (~alu_a[31] & ~xb[31] & n); // overflow end endmodule
// fn == 2'b01: eq (Z) // fn == 2'b10: lt (N ^ V) // fn == 2'b11: le (Z | (N ^ V)) // result is 1 if comparison is true, 0 otherwise module dp_cmp(fn,n,v,z,result); input [1:0] fn; input n,v,z; output [31:0] result; assign result = {31'b0,(fn[0] & z) | (fn[1] & (n ^ v))}; endmodule
// fn == 2'b00: logical shift left // fn == 2'b01: logical shift right // fn == 2'b11: arithmetic shift right module dp_shift(fn,alu_a,alu_b,result); input [1:0] fn; input [31:0] alu_a; input [4:0] alu_b; output [31:0] result; wire sin; reg [31:0] u,result; wire [31:0] v,w,x,y,z; assign sin = fn[1] & alu_a[31]; // assign u = fn[0] ? alu_a[0:31] : alu_a[31:0] always @(fn[0] or alu_a) begin: loopA integer i; for (i = 0; i < 32; i = i + 1) u[i] = fn[0] ? alu_a[31 - i] : alu_a[i]; end assign v = alu_b[4] ? {u[15:0],{16{sin}}} : u[31:0]; assign w = alu_b[3] ? {v[23:0],{8{sin}}} : v[31:0]; assign x = alu_b[2] ? {w[27:0],{4{sin}}} : w[31:0]; assign y = alu_b[1] ? {x[29:0],{2{sin}}} : x[31:0]; assign z = alu_b[0] ? {y[30:0],sin} : y[31:0]; // assign result = fn[0] ? z[0:31] : z[31:0] always @(fn[0] or z) begin: loopB integer i; for (i = 0; i < 32; i = i + 1) result[i] = fn[0] ? z[31 - i] : z[i]; end endmodule
// fn is truth table for boolean operation: // AND: fn = 4'b1000 // OR: fn = 4'b1110 // XOR: fn = 4'b0110 // A: fn = 4'b1010 module dp_boole(fn,alu_a,alu_b,result); input [3:0] fn; input [31:0] alu_a,alu_b; output [31:0] result; reg [31:0] result; always @(fn or alu_a or alu_b) begin: bits integer i; for (i = 0; i < 32; i = i + 1) begin result[i] = alu_b[i] ? (alu_a[i] ? fn[3] : fn[2]) : (alu_a[i] ? fn[1] : fn[0]); end end endmodule
module dp_mux(sel,addsub,boole,shift,cmp,alu); input [1:0] sel; input [31:0] addsub; input [31:0] boole; input [31:0] shift; input [31:0] cmp; output [31:0] alu; assign alu = (sel == 2'b00) ? addsub : (sel == 2'b01) ? boole : (sel == 2'b10) ? shift : (sel == 2'b11) ? cmp : 32'hXXXXXXXX; endmodule
module dp_misc(rd1zero,rd2zero,asel,bsel,inst,rd1,rd2,branch_addr, alu_a,alu_b,jump_addr,mem_wr_data,z); input rd1zero; // RA == R31, so treat RD1 as 0 input rd2zero; // RB/RC == R31, so treat RD2 as 0 input asel; // select A operand for ALU input bsel; // select B operand for ALU input [15:0] inst; // constant field from instruction input [31:0] rd1; // Reg[RA] from register file input [31:0] rd2; // Reg[RB] from register file (Reg[RC] for ST) input [31:0] branch_addr; // PC + 4 + 4*sxt(inst[15:0]) output [31:0] alu_a; // A input to ALU output [31:0] alu_b; // B input to ALU output [31:0] jump_addr; // jump address (from Reg[RA]) output [31:0] mem_wr_data; // data memory write data output z; // true if Reg[RA] is zero, used during branches // R31 reads as 0, so make that adjustment here assign jump_addr = rd1zero ? 32'b0 : rd1; assign mem_wr_data = rd2zero ? 32'b0 : rd2; assign z = ~|jump_addr; assign alu_a = asel ? branch_addr : jump_addr; assign alu_b = bsel[0] ? {{16{inst[15]}},inst[15:0]} : mem_wr_data; endmodule
module dp_alu(alufn,alu_a,alu_b,alu); input [5:0] alufn; input [31:0] alu_a; input [31:0] alu_b; output [31:0] alu; wire [31:0] addsub; wire [31:0] boole; wire [31:0] shift; wire [31:0] cmp; wire alu_n; wire alu_v; wire alu_z; // the four functional units dp_addsub alu1(alufn[0],alu_a,alu_b,addsub,alu_n,alu_v,alu_z); dp_boole alu4(alufn[3:0],alu_a,alu_b,boole); dp_shift alu3(alufn[1:0],alu_a,alu_b[4:0],shift); dp_cmp alu2(alufn[2:1],alu_n,alu_v,alu_z,cmp); // 4-to-1 mux selects alu output from functional units dp_mux alu5(alufn[5:4],addsub,boole,shift,cmp,alu); endmodule
module dp_wdata(wdsel,pc_plus_4,alu,mem_data,wdata); input [1:0] wdsel; input [31:0] pc_plus_4; input [31:0] alu; input [31:0] mem_data; output [31:0] wdata; assign wdata = wdsel[1] ? mem_data : wdsel[0] ? alu : pc_plus_4; endmodule
module datapath(inst,rd1,rd2,pc_plus_4,branch_addr,mem_rd_data, rd1zero,rd2zero,asel,bsel,wdsel,alufn, wdata,mem_addr,jump_addr,mem_wr_data,z); input [15:0] inst; // constant field from instruction input [31:0] rd1; // Reg[RA] from register file input [31:0] rd2; // Reg[RB] from register file (Reg[RC] for ST) input [31:0] pc_plus_4; // incremented PC input [31:0] branch_addr; // PC + 4 + 4*sxt(inst[15:0]) input [31:0] mem_rd_data; // memory read data (for LD) input rd1zero; // RA == R31, so treat RD1 as 0 input rd2zero; // RB/RC == R31, so treat RD2 as 0 input asel; // select A operand for ALU input bsel; // select B operand for ALU input [1:0] wdsel; // select regfile write data input [5:0] alufn; // operation to be performed by alu output [31:0] wdata; // regfile write data (output of WDSEL mux) output [31:0] mem_addr; // alu output, doubles as data memory address output [31:0] jump_addr; // jump address (from Reg[RA]) output [31:0] mem_wr_data; // data memory write data (from Reg[RC]) output z; // true if Reg[RA] is zero, used during branches wire [31:0] alu_a; // A input to ALU wire [31:0] alu_b; // B input to ALU // compute A and B inputs into alu, also Z bit for control logic dp_misc misc(rd1zero,rd2zero,asel,bsel,inst,rd1,rd2,branch_addr, alu_a,alu_b,jump_addr,mem_wr_data,z); // where all the heavy-lifting happens dp_alu alu(alufn,alu_a,alu_b,mem_addr); // select regfile write data from PC+4, alu output, and memory data dp_wdata wdata(wdsel,pc_plus_4,mem_addr,mem_rd_data,wdata); endmodule
module control(reset,irq,supervisor,z,inst, alufn,asel,bsel,pcsel,ra2,rd1zero,rd2zero,wa,wdsel,werf,mem_we); input reset; // active high input irq; // interrupt request, active high input supervisor; // 1 for kernel-mode, 0 for user-mode input z; // Reg[RA] == 0, used for branch tests input [31:0] inst; // current instruction output [5:0] alufn; // selects ALU function output asel; // selects ALU A operand output bsel; // selects ALU B operand output [2:0] pcsel; // selects next PC output [4:0] ra2; // RB or RC output rd1zero; // RA == 31 output rd2zero; // ra2 == 31 output [4:0] wa; // RC or XP output [1:0] wdsel; // selects regfile write data output werf; // regfile write enable output mem_we; // memory write enable, active high reg [15:0] ctl; // local wire interrupt; // interrupts enabled in user mode only assign interrupt = irq & ~supervisor; // control ROM always @(inst or interrupt) begin if (interrupt) ctl = 16'bx_100_1xx_xxxxxx_00_0; // interrupt else case (inst[31:26]) // ppp aaaaaa ww // b ccc w llllll dd // t sss aab uuuuuu ss // e eee sss ffffff ee x // s lll eee nnnnnn ll w // t 210 lll 543210 10 r default: ctl = 16'bx_011_1xx_xxxxxx_00_0; // illegal opcode 6'b011000: ctl = 16'bx_000_001_00xxx0_10_0; // LD 6'b011001: ctl = 16'bx_000_x01_00xxx0_10_1; // ST 6'b011011: ctl = 16'bx_010_0xx_xxxxxx_00_0; // JMP 6'b011101: ctl = 16'b1_001_0xx_xxxxxx_00_0; // BEQ 6'b011110: ctl = 16'b0_001_0xx_xxxxxx_00_0; // BNE 6'b011111: ctl = 16'bx_000_010_011010_10_0; // LDR 6'b100000: ctl = 16'bx_000_000_00xxx0_01_0; // ADD 6'b100001: ctl = 16'bx_000_000_00xxx1_01_0; // SUB 6'b100100: ctl = 16'bx_000_000_11x011_01_0; // CMPEQ 6'b100101: ctl = 16'bx_000_000_11x101_01_0; // CMPLT 6'b100110: ctl = 16'bx_000_000_11x111_01_0; // CMPLE 6'b101000: ctl = 16'bx_000_000_011000_01_0; // AND 6'b101001: ctl = 16'bx_000_000_011110_01_0; // OR 6'b101010: ctl = 16'bx_000_000_010110_01_0; // XOR 6'b101100: ctl = 16'bx_000_000_10xx00_01_0; // SHL 6'b101101: ctl = 16'bx_000_000_10xx01_01_0; // SHR 6'b101110: ctl = 16'bx_000_000_10xx11_01_0; // SRA 6'b110000: ctl = 16'bx_000_001_00xxx0_01_0; // ADDC 6'b110001: ctl = 16'bx_000_001_00xxx1_01_0; // SUBC 6'b110100: ctl = 16'bx_000_001_11x011_01_0; // CMPEQC 6'b110101: ctl = 16'bx_000_001_11x101_01_0; // CMPLTC 6'b110110: ctl = 16'bx_000_001_11x111_01_0; // CMPLEC 6'b111000: ctl = 16'bx_000_001_011000_01_0; // ANDC 6'b111001: ctl = 16'bx_000_001_011110_01_0; // ORC 6'b111010: ctl = 16'bx_000_001_010110_01_0; // XORC 6'b111100: ctl = 16'bx_000_001_10xx00_01_0; // SHLC 6'b111101: ctl = 16'bx_000_001_10xx01_01_0; // SHRC 6'b111110: ctl = 16'bx_000_001_10xx11_01_0; // SRAC endcase end assign werf = ~ctl[0]; assign mem_we = !reset & ctl[0]; assign wdsel = ctl[2:1]; assign alufn = ctl[8:3]; assign bsel = ctl[9]; assign asel = ctl[10]; assign wa = ctl[11] ? 5'b11110 : inst[25:21]; assign pcsel = ((ctl[14:12] == 3'b001) & (ctl[15] ^ z)) ? 3'b000 : ctl[14:12]; assign ra2 = ctl[0] ? inst[25:21] : inst[15:11]; assign rd1zero = (inst[20:16] == 5'b11111); assign rd2zero = (ra2 == 5'b11111); endmodule
module beta(clk,reset,irq,inst_addr,inst_data, mem_addr,mem_rd_data,mem_we,mem_wr_data); input clk; input reset; // active high input irq; // interrupt request, active high output [31:0] inst_addr; // address of instruction to be fetched input [31:0] inst_data; // instruction returning from memory output [31:0] mem_addr; // address of data word to be accessed input [31:0] mem_rd_data; // read data returning from memory output mem_we; // memory write enable, active high output [31:0] mem_wr_data; // memory write data // internal signals source description wire [5:0] alufn; // [control] selects ALU function wire asel; // [control] selects ALU A operand wire [31:0] branch_addr; // [pc] PC + 4 + 4*sxt(inst[15:0]) wire bsel; // [control] selects ALU B operand wire [31:0] jump_addr; // [datapath] Reg[RA] wire [31:0] pc_plus_4; // [pc] PC + 4 wire [2:0] pcsel; // [control] selects next PC wire [4:0] ra2; // [control] RB or RC wire [31:0] rd1; // [regfile] Reg[RA] wire rd1zero; // [control] RA == 31 wire [31:0] rd2; // [regfile] Reg[ra2] wire rd2zero; // [control] ra2 == 31 wire [4:0] wa; // [control] RC or XP wire [1:0] wdsel; // [control] selects regfile write data wire werf; // [control] regfile write enable wire [31:0] wdata; // [datapath] regfile write data wire z; // [datapath] Reg[RA] == 0 // control logic, reg file address generation control ctl(reset,irq,inst_addr[31],z,inst_data[31:0], alufn,asel,bsel,pcsel,ra2,rd1zero,rd2zero,wa,wdsel,werf,mem_we); // program counter logic pc pc(clk,reset,pcsel,inst_data[15:0],jump_addr,branch_addr, inst_addr,pc_plus_4); // register file regfile regfile(inst_data[20:16],rd1,ra2,rd2,clk,werf,wa,wdata); // datapath datapath dp(inst_data[15:0],rd1,rd2,pc_plus_4,branch_addr,mem_rd_data, rd1zero,rd2zero,asel,bsel,wdsel,alufn, wdata,mem_addr,jump_addr,mem_wr_data,z); endmodule
// Set time units to be nanoseconds. `timescale 1ns/1ns module main; reg clk,reset,irq; wire [31:0] inst_addr; wire [31:0] inst_data; wire [31:0] mem_addr; wire [31:0] mem_wr_data; wire [31:0] mem_rd_data; wire mem_we; reg [31:0] memory[1023:0]; beta beta(clk,reset,irq,inst_addr,inst_data, mem_addr,mem_rd_data,mem_we,mem_wr_data); // main memory (2 async read ports, 1 sync write port) assign inst_data = memory[inst_addr[13:2]]; assign mem_rd_data = memory[mem_addr[13:2]]; always @(posedge clk) if (mem_we) memory[mem_addr[13:2]] <= mem_wr_data; always #5 clk = ~clk; initial begin $dumpfile("beta_checkoff.vcd"); $dumpvars; $readmemh("beta_checkoff",memory); clk = 0; irq = 0; reset = 1; #10 reset = 0; #3000 // 300 cycles $finish; end endmodule