module core( input clk, input reset, output reg [31:0] mem_inst_addr, input [31:0] mem_inst_data, // output reg [31:0] mem_data_addr, // output reg [31:0] mem_data_wdata, // input [31:0] mem_data_rdata, // output reg mem_data_en, // output reg mem_data_we, // input mem_data_valid, // input mem_data_done // // instruction memory // output axi_inst_ACLK, // output axi_inst_ARESETn, // output axi_inst_AWVALID, // output axi_inst_AWADDR, // output [2:0] axi_inst_AWPROT, // input axi_inst_AWREADY, // output axi_inst_WVALID, // output [DATA_WIDTH-1:0] axi_inst_WDATA, // output [(DATA_WIDTH/8)-1:0] axi_inst_WSTRB, // input axi_inst_WREADY, // input axi_inst_BVALID, // output axi_inst_BREADY, // input [1:0] axi_inst_BRESP, // output axi_inst_ARVALID, // output axi_inst_ARADDR, // output [2:0] axi_inst_ARPROT, // input axi_inst_ARREADY, // input axi_inst_RVALID, // input [DATA_WIDTH-1:0] axi_inst_RDATA, // input [1:0] axi_inst_RRESP, // output axi_inst_RREADY, // data memory // output axi_data_ACLK, // output axi_data_ARESETn, // output axi_data_AWVALID, // output axi_data_AWADDR, // output [2:0] axi_data_AWPROT, // input axi_data_AWREADY, // output axi_data_WVALID, // output [DATA_WIDTH-1:0] axi_data_WDATA, // output [(DATA_WIDTH/8)-1:0] axi_data_WSTRB, // input axi_data_WREADY, // input axi_data_BVALID, // output axi_data_BREADY, // input [1:0] axi_data_BRESP, // output axi_data_ARVALID, // output axi_data_ARADDR, // output [2:0] axi_data_ARPROT, // input axi_data_ARREADY, // input axi_data_RVALID, // input [DATA_WIDTH-1:0] axi_data_RDATA, // input [1:0] axi_data_RRESP, // output axi_data_RREADY, output dummy_out ); // Register File reg [31:0] regfile [0:31]; initial begin : init_regfile integer i; for (i=0; i<32; i=i+1) begin regfile[i] = 32'h00000000; end end // Registers reg [31:0] r_if_pc = 0, r_id_pc, r_ex_pc, r_mem_pc, r_wb_pc; reg r_id_stall, r_ex_stall, r_mem_stall, r_wb_stall; reg [31:0] r_id_inst, r_ex_inst, r_mem_inst, r_wb_inst; reg [4:0] r_ex_rd, r_mem_rd, r_wb_rd; reg r_ex_alu_seed; reg [3:0] r_ex_aluop; reg [31:0] r_ex_s1, r_ex_s2, r_mem_s1, r_mem_s2; reg [31:0] r_mem_alu_out, r_wb_alu_out; reg r_mem_alu_zero; reg r_ex_jump; reg r_ex_store, r_mem_store; reg r_ex_load, r_mem_load; // IF reg s_if_halt; reg [31:0] s_if_next_pc; reg [31:0] s_if_inst; reg s_if_stall; always @(*) begin s_if_halt = 0; if (r_ex_jump) begin s_if_next_pc = s_ex_alu_out; s_if_stall = 1'b1; end else begin s_if_next_pc = r_if_pc + 4; s_if_stall = 1'b0; end mem_inst_addr = r_if_pc; s_if_inst = mem_inst_data; end // ID reg s_id_halt; reg [6:0] s_id_opcode; reg [2:0] s_id_funct3; reg [6:0] s_id_funct7; reg [4:0] s_id_rd, s_id_rs1, s_id_rs2; reg [31:0] s_id_immed_itype, s_id_immed_stype, s_id_immed_utype, s_id_immed_btype, s_id_immed_jtype; reg [31:0] s_id_s1, s_id_s2; reg [3:0] s_id_aluop; reg s_id_alu_seed; reg s_id_invalid; reg s_id_jump, s_id_branch; reg s_id_store, s_id_load; // RV32I / RV64I / RV32M localparam OP_LUI = 7'b0110111, OP_AUIPC = 7'b0010111, OP_JAL = 7'b1101111, OP_JALR = 7'b1100111, OP_BRANCH = 7'b1100011, OP_LOAD = 7'b0000011, OP_STORE = 7'b0100011, OP_IMM = 7'b0010011, OP_ALU = 7'b0110011, OP_FENCE = 7'b0001111, OP_SYSTEM = 7'b1110011; // RV64M // localparam OP_???????? = 7'b0111011; // RV32A / RV64A // localparam OP_ATOMIC = 7'b0101111; // TODO: add opcodes for other extensions // ALU OPCODES localparam ALUOP_ADD = 4'b0000, ALUOP_SUB = 4'b0001, ALUOP_XOR = 4'b0010, ALUOP_OR = 4'b0011, ALUOP_AND = 4'b0100, ALUOP_SL = 4'b0101, ALUOP_SRL = 4'b0110, ALUOP_SRA = 4'b0111, ALUOP_SLT = 4'b1000, ALUOP_SLTU = 4'b1001; always @(*) begin s_id_halt = 0; s_id_invalid = 0; s_id_store = 0; s_id_load = 0; s_id_opcode = r_id_inst[6:0]; s_id_rd = r_id_inst[11:7]; s_id_rs1 = r_id_inst[19:15]; s_id_rs2 = r_id_inst[24:20]; s_id_funct3 = r_id_inst[14:12]; s_id_funct7 = r_id_inst[31:25]; s_id_immed_itype = {{20{r_id_inst[31]}}, r_id_inst[31:20]}; s_id_immed_stype = {{20{r_id_inst[31]}}, r_id_inst[31:25], r_id_inst[11:7]}; s_id_immed_utype = {r_id_inst[31:12], 12'b0}; s_id_immed_btype = {{19{r_id_inst[31]}}, r_id_inst[31], r_id_inst[7], r_id_inst[30:25], r_id_inst[11:8], 1'b0}; s_id_immed_jtype = {{11{r_id_inst[31]}}, r_id_inst[31], r_id_inst[19:12], r_id_inst[20], r_id_inst[30:21], 1'b0}; case (s_id_opcode) OP_LUI: begin // LUI s_id_s1 = 32'h00000000; s_id_s2 = s_id_immed_utype; s_id_aluop = ALUOP_ADD; s_id_jump = 0; s_id_branch = 0; end OP_AUIPC: begin // AUIPC s_id_s1 = r_id_pc; s_id_s2 = s_id_immed_utype; s_id_aluop = ALUOP_ADD; s_id_jump = 0; s_id_branch = 0; end OP_JAL: begin // JAL s_id_s1 = r_id_pc; s_id_s2 = s_id_immed_jtype; s_id_aluop = ALUOP_ADD; s_id_jump = 1; s_id_branch = 0; end OP_JALR: begin // JALR s_id_s1 = regfile[s_id_rs1]; s_id_s2 = s_id_immed_itype; s_id_aluop = ALUOP_ADD; s_id_jump = 1; s_id_branch = 0; end // OP_BRANCH: begin // end // OP_LOAD: begin // end // OP_STORE: begin // end OP_IMM: begin s_id_s1 = regfile[s_id_rs1]; s_id_s2 = s_id_immed_itype; s_id_jump = 0; s_id_branch = 0; casex ({s_id_funct3, s_id_funct7}) 10'b000xxxxxxx: s_id_aluop = ALUOP_ADD; // ADDI 10'b010xxxxxxx: s_id_aluop = ALUOP_SLT; // SLTI 10'b011xxxxxxx: s_id_aluop = ALUOP_SLTU; // SLTIU 10'b100xxxxxxx: s_id_aluop = ALUOP_XOR; // XORI 10'b110xxxxxxx: s_id_aluop = ALUOP_OR; // ORI 10'b111xxxxxxx: s_id_aluop = ALUOP_AND; // ANDI 10'b001000000x: s_id_aluop = ALUOP_SL; // SLLI // NOTE: technically s_id_funct7[0] must be 0 however GCC allows shifts of up to 63b despite assembling for 32b. I can tolerate this deviation from ISA spec at essentially no cost 10'b101000000x: s_id_aluop = ALUOP_SRL; // SRLI // NOTE: technically s_id_funct7[0] must be 0 however GCC allows shifts of up to 63b despite assembling for 32b. I can tolerate this deviation from ISA spec at essentially no cost 10'b101010000x: s_id_aluop = ALUOP_SRA; // SRAI // NOTE: technically s_id_funct7[0] must be 0 however GCC allows shifts of up to 63b despite assembling for 32b. I can tolerate this deviation from ISA spec at essentially no cost default: begin s_id_s1 = 32'hxxxxxxxx; s_id_s2 = 32'hxxxxxxxx; s_id_invalid = 1; end endcase end OP_ALU: begin s_id_s1 = regfile[s_id_rs1]; s_id_s2 = regfile[s_id_rs2]; s_id_jump = 0; s_id_branch = 0; case ({s_id_funct3, s_id_funct7}) 10'b0000000000: s_id_aluop = ALUOP_ADD; // ADD 10'b0000100000: s_id_aluop = ALUOP_SUB; // SUB 10'b0010000000: s_id_aluop = ALUOP_SL; // SLL 10'b0100000000: s_id_aluop = ALUOP_SLT; // SLT 10'b0110000000: s_id_aluop = ALUOP_SLTU; // SLTU 10'b1000000000: s_id_aluop = ALUOP_XOR; // XOR 10'b1100000000: s_id_aluop = ALUOP_OR; // OR 10'b1110000000: s_id_aluop = ALUOP_AND; // AND 10'b1010000000: s_id_aluop = ALUOP_SRL; // SRL 10'b1010100000: s_id_aluop = ALUOP_SRA; // SRA default: begin s_id_s1 = 32'hxxxxxxxx; s_id_s2 = 32'hxxxxxxxx; s_id_invalid = 1; end endcase end // OP_FENCE: begin // end // OP_SYSTEM: begin // end default: begin s_id_jump = 0; s_id_branch = 0; s_id_s1 = 32'hxxxxxxxx; s_id_s2 = 32'hxxxxxxxx; s_id_invalid = 1; end endcase if (s_id_invalid) begin $display("%0t:\tInvalid instruction at PC=0x%h", $time, r_id_pc); s_id_halt = 1'b1; s_id_aluop = 3'hx; s_id_alu_seed = 1'bx; end end // EX reg s_ex_halt; reg [31:0] s_ex_data1, s_ex_data2; reg [31:0] s_ex_alu_out; reg s_ex_alu_zero; reg [31:0] s_ex_ra; always @(*) begin s_ex_halt = 0; // NOTE: s_ex_data* exist for adding data paths bypassing regfile in the future s_ex_data1 = r_ex_s1; s_ex_data2 = r_ex_s2; case (r_ex_aluop) ALUOP_ADD: begin s_ex_alu_out = s_ex_data1 + s_ex_data2; end ALUOP_SUB: begin s_ex_alu_out = s_ex_data1 - s_ex_data2; end ALUOP_XOR: begin s_ex_alu_out = s_ex_data1 ^ s_ex_data2; end ALUOP_OR: begin s_ex_alu_out = s_ex_data1 | s_ex_data2; end ALUOP_AND: begin s_ex_alu_out = s_ex_data1 & s_ex_data2; end ALUOP_SL: begin s_ex_alu_out = s_ex_data1 << s_ex_data2[4:0]; end ALUOP_SRL: begin s_ex_alu_out = s_ex_data1 >> s_ex_data2[4:0]; end ALUOP_SRA: begin s_ex_alu_out = $signed(s_ex_data1) >>> s_ex_data2[4:0]; end ALUOP_SLT: begin s_ex_alu_out = $signed(s_ex_data1) < $signed(s_ex_data2); end ALUOP_SLTU: begin s_ex_alu_out = s_ex_data1 < s_ex_data2; end default: begin s_ex_halt = 1; s_ex_alu_out = 32'hxxxxxxxx; end endcase s_ex_alu_zero = (s_ex_alu_out == 0); s_ex_ra = r_ex_pc + 4; end // MEM reg s_mem_halt; reg s_mem_bp; always @(*) begin s_mem_halt = 0; s_mem_bp = 0; // if (r_mem_store) begin // mem_data_en = 1; // mem_data_we = 1; // s_mem_bp = !mem_data_done; // end else if (r_mem_load) begin // mem_data_en = 1; // mem_data_we = 0; // s_mem_bp = !mem_data_valid; // end else begin // mem_data_en = 0; // mem_data_we = 0; // s_mem_bp = 0; // end end // WB reg s_wb_halt; reg [31:0] s_wb_data; reg s_wb_write; always @(*) begin s_wb_halt = 0; // load instructions do not use output of alu in wb s_wb_data = r_wb_alu_out; // FIXME: always writes!!! s_wb_write = !r_wb_stall; end // SYS reg s_sys_halt; always @(*) begin s_sys_halt = s_if_halt || s_id_halt || s_ex_halt || s_mem_halt || s_wb_halt; end // Register update always @(posedge clk) begin if (reset) begin r_if_pc <= 32'h00000000; // rather than resetting all flip-flops just stall the pipeline so values are ignored. r_id_stall <= 1; r_ex_stall <= 1; r_mem_stall <= 1; r_wb_stall <= 1; end else begin // NOTE: halt disabled because startup causes hault // if (s_sys_halt && 0) begin // // stay halted forever // end else begin // IF // if (!s_mem_bp) begin r_if_pc <= s_if_next_pc; // end // ID // if (!s_mem_bp) begin r_id_stall <= s_if_stall; r_id_pc <= r_if_pc; r_id_inst <= s_if_inst; // end // EX // if (!s_mem_bp) begin // TODO: also stall EX if taking branch r_ex_stall <= r_id_stall; r_ex_pc <= r_id_pc; r_ex_inst <= r_id_inst; r_ex_rd <= s_id_rd; r_ex_s1 <= s_id_s1; r_ex_s2 <= s_id_s2; r_ex_aluop <= s_id_aluop; r_ex_alu_seed <= s_id_alu_seed; r_ex_jump <= s_id_jump; r_ex_store <= s_id_store; r_ex_load <= s_id_load; // end // MEM // if (!s_mem_bp) begin r_mem_stall <= r_ex_stall; r_mem_pc <= r_ex_pc; r_mem_inst <= r_ex_inst; r_mem_rd <= r_ex_rd; r_mem_s1 <= r_ex_s1; r_mem_s2 <= r_ex_s2; r_mem_alu_out <= s_ex_alu_out; r_mem_alu_zero <= s_ex_alu_zero; r_mem_store <= r_ex_store; r_mem_load <= r_ex_load; // end // WB // if (!s_mem_bp) begin r_wb_stall <= r_mem_stall; r_wb_pc <= r_mem_pc; r_wb_rd <= r_mem_rd; r_wb_alu_out <= r_mem_alu_out; // end // Register File if (r_wb_rd != 0 && s_wb_write) begin regfile[r_wb_rd] <= s_wb_data; end // end end end assign dummy_out = s_wb_data[0]; endmodule