`define INS_LUI 7'b0110111 `define INS_AUIPC 7'b0010111 `define INS_JAL 7'b1101111 `define INS_JALR 7'b1100111 `define INS_BRANCH 7'b1100011 `define INS_LOAD 7'b0000011 `define INS_STORE 7'b0100011 `define INS_IMMOP 7'b0010011 `define INS_REGOP 7'b0110011 `define INS_FENCE 7'b0001111 `define INS_ENVOP 7'b1110011 `define INS_BRANCH_EQ 3'b000 `define INS_BRANCH_NE 3'b001 `define INS_BRANCH_LT 3'b100 `define INS_BRANCH_GE 3'b101 `define INS_BRANCH_LTU 3'b110 `define INS_BRANCH_GEU 3'b111 `define INS_LOAD_8 3'b000 `define INS_LOAD_16 3'b001 `define INS_LOAD_32 3'b010 `define INS_LOAD_U8 3'b100 `define INS_LOAD_U16 3'b101 `define INS_STORE_8 3'b000 `define INS_STORE_16 3'b001 `define INS_STORE_32 3'b010 `define INS_ALUOP_ADD 3'b000 `define INS_ALUOP_SLT 3'b010 `define INS_ALUOP_SLTU 3'b011 `define INS_ALUOP_XOR 3'b100 `define INS_ALUOP_OR 3'b110 `define INS_ALUOP_AND 3'b111 `define INS_ALUOP_SLL 3'b001 `define INS_ALUOP_SRL 3'b101 `define INS_ENVOP_CALL 12'b000000000000 `define INS_ENVOP_BREAK 12'b000000000001 `define F3_DONT_CARE 3'b??? `define F7_DONT_CARE 7'b??????? // RISC-V CPU Hart module hart( clk, pmmu_rd, pmmu_wr, pmmu_size, pmmu_addr, pmmu_datar, pmmu_dataw ); input clk; // the clock. pretty important. output reg pmmu_rd; output reg pmmu_wr; output reg[1:0] pmmu_size; output reg[31:0] pmmu_addr; input reg[31:0] pmmu_datar; output reg[31:0] pmmu_dataw; reg[31:0] reg_pc = 0; reg[31:0] reg_int[31:0]; // FETCH DATA reg[31:0] t_ins = 0; // temp for being processed instruction // DECODE DATA reg[6:0] t_d_op; reg[4:0] t_d_rd; reg[2:0] t_d_f3; reg[4:0] t_d_r1; reg[4:0] t_d_r2; reg[6:0] t_d_f7; reg[31:0] t_d_immi; reg[31:0] t_d_imms; reg[31:0] t_d_immb; reg[31:0] t_d_immu; reg[31:0] t_d_immj; reg[31:0] t_d_imm; // GET REGISTERS reg[31:0] t_r_a; reg[31:0] t_r_b; // EXECUTE INSTRUCTION reg[31:0] t_e_result; reg t_e_branch; reg[31:0] t_e_address; reg[31:0] t_e_tmp1; always begin // instruction fetch @(posedge clk); $display("== INSTRUCTION FETCH (0x%08h) ==", reg_pc); pmmu_addr = reg_pc; pmmu_rd = 1; pmmu_size = 2; // instruction decode @(posedge clk); $display("== INSTRUCTION DECODE (0x%08h) ==", pmmu_datar); t_ins = pmmu_datar; pmmu_addr = 0; pmmu_rd = 0; pmmu_size = 0; t_d_op = t_ins[6:0]; t_d_rd = t_ins[11:7]; t_d_f3 = t_ins[14:12]; t_d_r1 = t_ins[19:15]; t_d_r2 = t_ins[24:20]; t_d_f7 = t_ins[31:25]; t_d_immi = {{21{t_ins[31]}}, t_ins[30:20] }; t_d_imms = {{21{t_ins[31]}}, t_ins[30:25], t_ins[11:7] }; t_d_immb = {{20{t_ins[31]}}, t_ins[7], t_ins[30:25], t_ins[11:8], 1'b0 }; t_d_immu = { t_ins[31:12], 12'b0 }; t_d_immj = {{12{t_ins[31]}}, t_ins[19:12], t_ins[20], t_ins[30:21], 1'b0 }; case (t_d_op) `INS_JALR, `INS_LOAD, `INS_IMMOP: t_d_imm = t_d_immi; `INS_JAL: t_d_imm = t_d_immj; `INS_STORE: t_d_imm = t_d_imms; `INS_BRANCH: t_d_imm = t_d_immb; `INS_LUI, `INS_AUIPC: t_d_imm = t_d_immu; `INS_ENVOP, `INS_FENCE, `INS_REGOP: t_d_imm = 0; default: $error("INVALID INSTRUCTION %b", t_d_op); endcase $display("INS: %7b %3b %7b %d %d -> %d", t_d_op, t_d_f3, t_d_f7, t_d_r1, t_d_r2, t_d_rd); $display("IMM: U[%d] S[%d]", t_d_imm, $signed(t_d_imm)); // get registers @(posedge clk); $display("== GET REGISTERS =="); t_r_a = reg_int[t_d_r1]; t_r_b = reg_int[t_d_r2]; $display("REG: %d[%d] %d[%d]", t_d_r1, t_r_a, t_d_r2, t_r_b); // execute @(posedge clk); $display("== EXECUTE =="); t_e_address = 0; t_e_branch = 0; t_e_result = 0; casez ({ t_d_op, t_d_f3, t_d_f7 }) { `INS_LUI, `F3_DONT_CARE, `F7_DONT_CARE }: t_e_result = t_d_imm; { `INS_AUIPC, `F3_DONT_CARE, `F7_DONT_CARE }: t_e_result = t_d_imm + reg_pc; { `INS_JAL, `F3_DONT_CARE, `F7_DONT_CARE }: begin t_e_result = reg_pc + 4; t_e_address = reg_pc + t_d_imm; t_e_branch = 1; end { `INS_JALR, `F3_DONT_CARE, `F7_DONT_CARE }: begin t_e_result = reg_pc + 4; t_e_address = (t_r_a + t_d_imm) & ~1; t_e_branch = 1; end { `INS_BRANCH, `INS_BRANCH_EQ, `F7_DONT_CARE }: begin if (t_r_a == t_r_b) begin t_e_address = reg_pc + t_d_imm; t_e_branch = 1; end end { `INS_BRANCH, `INS_BRANCH_NE, `F7_DONT_CARE }: begin if (t_r_a != t_r_b) begin t_e_address = reg_pc + t_d_imm; t_e_branch = 1; end end { `INS_BRANCH, `INS_BRANCH_LT, `F7_DONT_CARE }: begin if ($signed(t_r_a) < $signed(t_r_b)) begin t_e_address = reg_pc + t_d_imm; t_e_branch = 1; end end { `INS_BRANCH, `INS_BRANCH_GE, `F7_DONT_CARE }: begin if ($signed(t_r_a) >= $signed(t_r_b)) begin t_e_address = reg_pc + t_d_imm; t_e_branch = 1; end end { `INS_BRANCH, `INS_BRANCH_LTU, `F7_DONT_CARE }: begin if (t_r_a < t_r_b) begin t_e_address = reg_pc + t_d_imm; t_e_branch = 1; end end { `INS_BRANCH, `INS_BRANCH_GEU, `F7_DONT_CARE }: begin if (t_r_a >= t_r_b) begin t_e_address = reg_pc + t_d_imm; t_e_branch = 1; end end { `INS_LOAD, `F3_DONT_CARE, `F7_DONT_CARE }: begin t_e_address = t_d_imm + t_r_a; pmmu_addr = t_e_address; pmmu_rd = 1; pmmu_size = 2; #2; pmmu_rd = 0; case (t_d_f3[1:0]) 0: t_e_result = (pmmu_datar >> (8 * (t_e_address & 3))) & 255; 1: begin if (t_e_address[0] != 0) $error("UNALIGNED READ %08x", t_e_address); t_e_result = (pmmu_datar >> (8 * (t_e_address & 2))) & 'hffff; end 2: begin if (t_e_address[1:0] != 0) $error("UNALIGNED READ %08x", t_e_address); t_e_result = pmmu_datar; end 3: $error("INVALID INSTRUCTION"); endcase case (t_d_f3) 1,2,3,4:; 4: t_e_result = { {24{t_e_result[7]}}, t_e_result[7:0] }; 5: t_e_result = { {16{t_e_result[16]}}, t_e_result[15:0] }; 6, 7: $error("INVALID INSTRUCTION"); endcase end { `INS_STORE, `F3_DONT_CARE, `F7_DONT_CARE }: begin pmmu_addr = t_d_imm + t_r_a; pmmu_dataw = t_r_b; pmmu_wr = 1; pmmu_size = t_d_f3[1:0]; #2; pmmu_wr = 0; end { `INS_IMMOP, `INS_ALUOP_ADD, `F7_DONT_CARE }: begin t_e_result = $signed(t_r_a) + $signed(t_d_imm); end { `INS_IMMOP, `INS_ALUOP_SLT, `F7_DONT_CARE }: begin t_e_result = $signed(t_r_a) < $signed(t_d_imm) ? 1 : 0; end { `INS_IMMOP, `INS_ALUOP_SLTU, `F7_DONT_CARE }: begin t_e_result = t_r_a < t_d_imm ? 1 : 0; end { `INS_IMMOP, `INS_ALUOP_XOR, `F7_DONT_CARE }: begin t_e_result = t_r_a ^ t_d_imm; end { `INS_IMMOP, `INS_ALUOP_OR, `F7_DONT_CARE }: begin t_e_result = t_r_a | t_d_imm; end { `INS_IMMOP, `INS_ALUOP_AND, `F7_DONT_CARE }: begin t_e_result = t_r_a & t_d_imm; end { `INS_IMMOP, `INS_ALUOP_SLL, 7'b0000000 }: begin t_e_result = t_r_a << (t_d_imm & 31); end { `INS_IMMOP, `INS_ALUOP_SRL, 7'b0000000 }: begin t_e_result = t_r_a >> (t_d_imm & 31); end { `INS_IMMOP, `INS_ALUOP_SRL, 7'b0100000 }: begin t_e_result = t_r_a >>> (t_d_imm & 31); end { `INS_REGOP, `INS_ALUOP_ADD, 7'b0000000 }: begin t_e_result = $signed(t_r_a) + $signed(t_r_b); end { `INS_REGOP, `INS_ALUOP_ADD, 7'b0100000 }: begin t_e_result = $signed(t_r_a) - $signed(t_r_b); end { `INS_REGOP, `INS_ALUOP_SLT, 7'b0000000 }: begin t_e_result = $signed(t_r_a) < $signed(t_r_b) ? 1 : 0; end { `INS_REGOP, `INS_ALUOP_SLTU, 7'b0000000 }: begin t_e_result = t_r_a < t_r_b ? 1 : 0; end { `INS_REGOP, `INS_ALUOP_XOR, 7'b0000000 }: begin t_e_result = t_r_a ^ t_r_b; end { `INS_REGOP, `INS_ALUOP_OR, 7'b0000000 }: begin t_e_result = t_r_a | t_r_b; end { `INS_REGOP, `INS_ALUOP_AND, 7'b0000000 }: begin t_e_result = t_r_a & t_r_b; end { `INS_REGOP, `INS_ALUOP_SLL, 7'b0000000 }: begin t_e_result = t_r_a << (t_r_b & 31); end { `INS_REGOP, `INS_ALUOP_SRL, 7'b0000000 }: begin t_e_result = t_r_a >> (t_r_b & 31); end { `INS_REGOP, `INS_ALUOP_SRL, 7'b0100000 }: begin t_e_result = t_r_a >>> (t_r_b & 31); end { `INS_FENCE, `F3_DONT_CARE, `F7_DONT_CARE }:; { `INS_ENVOP, `F3_DONT_CARE, `F7_DONT_CARE }:; default: $error("INVALID INSTRUCTION"); endcase // write back @(posedge clk); $display("== WRITE REGISTERS =="); case (t_d_op) `INS_BRANCH, `INS_STORE, `INS_FENCE, `INS_ENVOP:; default: begin if (t_d_rd != 0) reg_int[t_d_rd] = t_e_result; end endcase // finish up reg_pc = t_e_branch ? t_e_address : reg_pc + 4; end endmodule