commit 03ac91bf65fdb579e007ffd3fc51078a629f85fa Author: ANCCEND <3064429095@qq.com> Date: Sat Sep 12 13:18:24 2026 +0800 初版,Logisim转换Verilog diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d1a912 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +pipelined-cpu/* \ No newline at end of file diff --git a/cpu21_riscv_redirect_int_bpb.v b/cpu21_riscv_redirect_int_bpb.v new file mode 100644 index 0000000..1254e0b --- /dev/null +++ b/cpu21_riscv_redirect_int_bpb.v @@ -0,0 +1,1095 @@ +`timescale 1ns / 1ps +`default_nettype none + +// ----------------------------------------------------------------------------- +// cpu21-riscv-4.circ -> synthesizable Verilog +// +// This file keeps the instruction encodings used by the Logisim design. The +// processor is a five-stage IF/ID/EX/MEM/WB pipeline with: +// * EX-stage forwarding and load-use interlock; +// * EX-stage control-flow resolution and pipeline redirect; +// * an eight-entry fully-associative branch target buffer; +// * three sampled interrupt sources with priority and nested URET return; +// * the original ecall convention: a7==34 writes the LED value, otherwise +// the pipeline waits for go_i. +// +// Memory interface convention: +// instr_addr_o and data_addr_o are byte addresses. data_wstrb_o is a byte +// write mask, so an external word RAM can use data_addr_o[11:2] as its word +// index and merge bytes according to data_wstrb_o. +// ----------------------------------------------------------------------------- + +module cpu21_riscv_alu ( + input wire [ 3:0] op, + input wire [31:0] a, + input wire [31:0] b, + output reg [31:0] result, + output reg [31:0] result2 +); + localparam ALU_SLL = 4'd0; + localparam ALU_SRA = 4'd1; + localparam ALU_SRL = 4'd2; + localparam ALU_MUL = 4'd3; + localparam ALU_DIVU = 4'd4; + localparam ALU_ADD = 4'd5; + localparam ALU_SUB = 4'd6; + localparam ALU_AND = 4'd7; + localparam ALU_OR = 4'd8; + localparam ALU_XOR = 4'd9; + localparam ALU_NOR = 4'd10; + localparam ALU_SLT = 4'd11; + localparam ALU_SLTU = 4'd12; + reg [63:0] mult_result; + + always @* begin + mult_result = a * b; + result2 = 32'b0; + case (op) + ALU_SLL: result = a << b[4:0]; + ALU_SRA: result = $signed(a) >>> b[4:0]; + ALU_SRL: result = a >> b[4:0]; + ALU_MUL: begin + result = mult_result[31:0]; + result2 = mult_result[63:32]; + end + ALU_DIVU: begin + result = (b == 32'b0) ? 32'hffff_ffff : a / b; + result2 = (b == 32'b0) ? a : a % b; + end + ALU_ADD: result = a + b; + ALU_SUB: result = a - b; + ALU_AND: result = a & b; + ALU_OR: result = a | b; + ALU_XOR: result = a ^ b; + ALU_NOR: result = ~(a | b); + ALU_SLT: result = ($signed(a) < $signed(b)) ? 32'd1 : 32'd0; + ALU_SLTU: result = (a < b) ? 32'd1 : 32'd0; + default: result = 32'b0; + endcase + end +endmodule + + +// Eight-entry fully-associative branch target buffer. +// tag = PC[11:2], matching the ten-bit tag splitter in the Logisim BPB. +// count is a two-bit saturating predictor: 00/01 not-taken, 10/11 taken. +module cpu21_bpb_8 ( + input wire clk, + input wire reset, + input wire [31:0] predict_pc, + input wire predict_enable, + output reg predict_hit, + output reg predict_taken, + output reg [31:0] predict_target, + input wire update_enable, + input wire update_controlflow, + input wire [31:0] update_pc, + input wire [31:0] update_target, + input wire update_taken +); + reg valid [0:7]; + reg [ 9:0] tag [0:7]; + reg [31:0] target [0:7]; + reg [ 1:0] count [0:7]; + reg [ 2:0] age [0:7]; + + reg update_found; + reg [ 2:0] update_index; + reg replace_found; + reg [ 2:0] replace_index; + reg [ 2:0] max_age; + integer p; + integer u; + integer k; + integer selected_index; + + always @* begin + predict_hit = 1'b0; + predict_taken = 1'b0; + predict_target = 32'b0; + for (p = 0; p < 8; p = p + 1) begin + if (predict_enable && !predict_hit && valid[p] && (tag[p] == predict_pc[11:2])) begin + predict_hit = 1'b1; + predict_target = target[p]; + predict_taken = count[p][1]; + end + end + end + + // Find a matching entry, otherwise use the first invalid entry, otherwise + // the oldest entry. The age update below implements an LRU-like policy. + always @* begin + update_found = 1'b0; + update_index = 3'd0; + replace_found = 1'b0; + replace_index = 3'd0; + max_age = 3'd0; + + for (u = 0; u < 8; u = u + 1) begin + if (!update_found && valid[u] && (tag[u] == update_pc[11:2])) begin + update_found = 1'b1; + update_index = u; + end + end + for (u = 0; u < 8; u = u + 1) begin + if (!replace_found && !valid[u]) begin + replace_found = 1'b1; + replace_index = u; + end else if (!replace_found && (age[u] >= max_age)) begin + replace_index = u; + max_age = age[u]; + end + end + end + + always @(posedge clk or posedge reset) begin + if (reset) begin + for (k = 0; k < 8; k = k + 1) begin + valid[k] <= 1'b0; + tag[k] <= 10'b0; + target[k] <= 32'b0; + count[k] <= 2'b01; + age[k] <= 3'b0; + end + end else if (update_enable && update_controlflow) begin + selected_index = update_found ? update_index : replace_index; + + valid[selected_index] <= 1'b1; + tag[selected_index] <= update_pc[11:2]; + target[selected_index] <= update_target; + age[selected_index] <= 3'b0; + + if (!update_found) begin + // Weak initial state avoids an immediate false prediction. + count[selected_index] <= update_taken ? 2'b10 : 2'b01; + end else if (update_taken) begin + if (count[selected_index] != 2'b11) count[selected_index] <= count[selected_index] + 2'b01; + end else begin + if (count[selected_index] != 2'b00) count[selected_index] <= count[selected_index] - 2'b01; + end + + for (k = 0; k < 8; k = k + 1) begin + if (valid[k] && (k != selected_index) && (age[k] != 3'b111)) age[k] <= age[k] + 3'b001; + end + end + end +endmodule + + +module cpu21_riscv_redirect_int_bpb #( + parameter RESET_PC = 32'h0000_0000, + parameter IRQ1_VECTOR = 32'h0000_30ac, + parameter IRQ2_VECTOR = 32'h0000_31e4, + parameter IRQ3_VECTOR = 32'h0000_3310, + parameter IRQ_STACK_DEPTH = 4, + // ustatus reset value. Bit 0 is MIE. The course interrupt test program + // only sets MIE inside its interrupt handlers, so the mainline would never + // acknowledge the first interrupt unless MIE is enabled right after reset. + parameter [31:0] USTATUS_INIT = 32'h0000_0001 +) ( + input wire clk, + input wire reset, + input wire [31:0] instr_i, + input wire [31:0] data_rdata_i, + input wire [ 2:0] irq_i, + input wire go_i, + + output wire [31:0] instr_addr_o, + output wire [31:0] data_addr_o, + output wire [31:0] data_wdata_o, + output wire [ 3:0] data_wstrb_o, + output wire data_we_o, + + output wire [31:0] led_data_o, + output wire led_valid_o, + output wire [31:0] if_pc_o, + output wire [31:0] id_pc_o, + output wire [31:0] ex_pc_o, + output wire [31:0] mem_pc_o, + output wire [31:0] wb_pc_o, + output wire [31:0] if_ir_o, + output wire [31:0] id_ir_o, + output wire [31:0] ex_ir_o, + output wire [31:0] mem_ir_o, + output wire [31:0] wb_ir_o, + output wire [31:0] rdin_o, + output wire [31:0] mdin_o, + output wire reg_write_o, + output wire mem_write_o, + output wire halted_o, + + output wire [ 2:0] irq_pending_o, + output wire [ 1:0] irq_current_level_o, + output wire [31:0] uepc_o, + output wire [31:0] ustatus_o, + output wire bpb_predict_hit_o, + output wire bpb_predict_taken_o, + output wire bpb_mispredict_o, + + output wire [15:0] cycle_count_o, + output wire [15:0] stall_count_o, + output wire [15:0] bubble_count_o, + output wire [15:0] conditional_taken_count_o, + output wire [15:0] unconditional_branch_count_o, + output wire [15:0] prediction_success_count_o, + output wire [15:0] prediction_failure_count_o +); + // Custom CPU21 opcodes from the supplied truth table. + localparam OP_LOAD = 5'h00; + localparam OP_R = 5'h0c; + localparam OP_I = 5'h04; + localparam OP_STORE = 5'h08; + localparam OP_JALR = 5'h19; + localparam OP_BRANCH = 5'h18; + localparam OP_JAL = 5'h1b; + localparam OP_SYS = 5'h1c; + + localparam ALU_SLL = 4'd0; + localparam ALU_SRA = 4'd1; + localparam ALU_SRL = 4'd2; + localparam ALU_MUL = 4'd3; + localparam ALU_DIVU = 4'd4; + localparam ALU_ADD = 4'd5; + localparam ALU_SUB = 4'd6; + localparam ALU_AND = 4'd7; + localparam ALU_OR = 4'd8; + localparam ALU_XOR = 4'd9; + localparam ALU_SLT = 4'd11; + localparam ALU_SLTU = 4'd12; + + reg [31:0] pc_q; + + reg ifid_valid_q; + reg [31:0] ifid_pc_q; + reg [31:0] ifid_ir_q; + reg ifid_pred_taken_q; + reg [31:0] ifid_pred_target_q; + + reg idex_valid_q; + reg [31:0] idex_pc_q; + reg [31:0] idex_ir_q; + reg [31:0] idex_rs1_value_q; + reg [31:0] idex_rs2_value_q; + reg [ 4:0] idex_rs1_idx_q; + reg [ 4:0] idex_rs2_idx_q; + reg [ 4:0] idex_rd_q; + reg [31:0] idex_imm_q; + reg [ 3:0] idex_aluop_q; + reg idex_alu_src_q; + reg idex_reg_write_q; + reg idex_mem_to_reg_q; + reg idex_mem_write_q; + reg idex_mem_byte_q; + reg [ 2:0] idex_wb_sel_q; + reg [11:0] idex_csr_addr_q; + reg idex_branch_q; + reg idex_beq_q; + reg idex_bne_q; + reg idex_bltu_q; + reg idex_jal_q; + reg idex_jalr_q; + reg idex_ecall_q; + reg idex_uret_q; + reg idex_csr_set_q; + reg idex_csr_clear_q; + reg idex_csr_write_q; + reg idex_pred_taken_q; + reg [31:0] idex_pred_target_q; + + reg exmem_valid_q; + reg [31:0] exmem_pc_q; + reg [31:0] exmem_ir_q; + reg [31:0] exmem_alu_result_q; + reg [31:0] exmem_alu_result2_q; + reg [31:0] exmem_csr_old_q; + reg [31:0] exmem_write_data_q; + reg [ 4:0] exmem_rd_q; + reg exmem_reg_write_q; + reg exmem_mem_to_reg_q; + reg exmem_mem_write_q; + reg [ 3:0] exmem_wstrb_q; + reg [ 2:0] exmem_wb_sel_q; + + reg memwb_valid_q; + reg [31:0] memwb_pc_q; + reg [31:0] memwb_ir_q; + reg [31:0] memwb_alu_result_q; + reg [31:0] memwb_alu_result2_q; + reg [31:0] memwb_csr_old_q; + reg [31:0] memwb_mem_data_q; + reg [ 4:0] memwb_rd_q; + reg memwb_reg_write_q; + reg memwb_mem_to_reg_q; + reg [ 2:0] memwb_wb_sel_q; + + reg [31:0] regfile [ 0:31]; + reg [31:0] led_data_q; + reg led_valid_q; + reg halted_q; + + // Interrupt state. ustatus[0] is the circuit's MIE bit. A small stack + // keeps EPC/status/priority so nested interrupts can return correctly. + reg [31:0] ustatus_q; + reg [31:0] uepc_q; + reg [ 1:0] irq_current_q; + reg [ 2:0] irq_pending_q; + reg [ 2:0] irq_sync1_q; + reg [ 2:0] irq_sync2_q; + reg [ 2:0] irq_prev_q; + reg [31:0] epc_stack [0:IRQ_STACK_DEPTH-1]; + reg [31:0] status_stack [0:IRQ_STACK_DEPTH-1]; + reg [ 1:0] priority_stack [0:IRQ_STACK_DEPTH-1]; + reg [ 2:0] irq_depth_q; + + reg [15:0] cycle_count_q; + reg [15:0] stall_count_q; + reg [15:0] bubble_count_q; + reg [15:0] conditional_taken_count_q; + reg [15:0] unconditional_branch_count_q; + reg [15:0] prediction_success_count_q; + reg [15:0] prediction_failure_count_q; + + // ------------------------------------------------------------------------- + // Decode in ID. + // ------------------------------------------------------------------------- + // The Logisim controller receives the standard RISC-V opcode field + // IR[6:2] (the table stores this five-bit field in hexadecimal). + wire [ 4:0] d_opcode = ifid_ir_q[6:2]; + wire [ 2:0] d_funct3 = ifid_ir_q[14:12]; + wire [ 6:0] d_funct7 = ifid_ir_q[31:25]; + + reg d_reg_write; + reg d_mem_to_reg; + reg d_mem_write; + reg d_mem_byte; + reg d_alu_src; + reg [ 2:0] d_wb_sel; + reg [ 3:0] d_aluop; + reg d_branch; + reg d_beq; + reg d_bne; + reg d_bltu; + reg d_jal; + reg d_jalr; + reg d_ecall; + reg d_uret; + reg d_csr_set; + reg d_csr_clear; + reg d_csr_write; + reg d_uses_rs1; + reg d_uses_rs2; + reg [ 4:0] d_src1_idx; + reg [ 4:0] d_src2_idx; + reg [ 4:0] d_rd; + reg [31:0] d_imm; + wire [11:0] d_csr_addr = ifid_ir_q[31:20]; + + always @* begin + d_reg_write = 1'b0; + d_mem_to_reg = 1'b0; + d_mem_write = 1'b0; + d_mem_byte = 1'b0; + d_alu_src = 1'b0; + d_wb_sel = 3'd0; + d_aluop = ALU_ADD; + d_branch = 1'b0; + d_beq = 1'b0; + d_bne = 1'b0; + d_bltu = 1'b0; + d_jal = 1'b0; + d_jalr = 1'b0; + d_ecall = 1'b0; + d_uret = 1'b0; + d_csr_set = 1'b0; + d_csr_clear = 1'b0; + d_csr_write = 1'b0; + d_uses_rs1 = 1'b0; + d_uses_rs2 = 1'b0; + d_src1_idx = ifid_ir_q[19:15]; + d_src2_idx = ifid_ir_q[24:20]; + d_rd = ifid_ir_q[11:7]; + d_imm = {{20{ifid_ir_q[31]}}, ifid_ir_q[31:20]}; + + case (d_opcode) + OP_R: begin + d_uses_rs1 = 1'b1; + d_uses_rs2 = 1'b1; + d_reg_write = 1'b1; + // The circuit's extra REMU control is the standard R-type + // funct7=1/funct3=111 form; ALU result2 is the remainder. + if ((d_funct7 == 7'b0000001) && (d_funct3 == 3'b111)) begin + d_aluop = ALU_DIVU; + d_wb_sel = 3'd3; + end else if ((d_funct7 == 7'b0000001) && (d_funct3 == 3'b000)) begin + d_aluop = ALU_MUL; + end else begin + case (d_funct3) + 3'b000: d_aluop = (d_funct7[5] ? ALU_SUB : ALU_ADD); + 3'b001: d_aluop = ALU_SLL; + 3'b010: d_aluop = ALU_SLT; + 3'b011: d_aluop = ALU_SLTU; + 3'b100: d_aluop = ALU_XOR; + 3'b101: d_aluop = (d_funct7[5] ? ALU_SRA : ALU_SRL); + 3'b110: d_aluop = ALU_OR; + 3'b111: d_aluop = ALU_AND; + default: d_reg_write = 1'b0; + endcase + end + end + + OP_I: begin + d_uses_rs1 = 1'b1; + d_alu_src = 1'b1; + d_reg_write = 1'b1; + case (d_funct3) + 3'b000: d_aluop = ALU_ADD; // addi + 3'b001: d_aluop = ALU_SLL; // slli + 3'b010: d_aluop = ALU_SLT; // slti + 3'b100: d_aluop = ALU_XOR; // xori + 3'b101: d_aluop = (d_funct7[5] ? ALU_SRA : ALU_SRL); + 3'b110: d_aluop = ALU_OR; // ori + 3'b111: d_aluop = ALU_AND; // andi + default: d_reg_write = 1'b0; + endcase + end + + OP_LOAD: begin + if (d_funct3 == 3'b010) begin + d_uses_rs1 = 1'b1; + d_alu_src = 1'b1; + d_aluop = ALU_ADD; + d_mem_to_reg=1'b1; + d_reg_write= 1'b1; + d_wb_sel = 3'd1; + end + end + + OP_STORE: begin + if ((d_funct3 == 3'b010) || (d_funct3 == 3'b000)) begin + d_uses_rs1 = 1'b1; + d_uses_rs2 = 1'b1; + d_alu_src = 1'b1; + d_aluop = ALU_ADD; + d_mem_write = 1'b1; + d_mem_byte = (d_funct3 == 3'b000); // sb + d_imm = {{20{ifid_ir_q[31]}}, ifid_ir_q[31:25], ifid_ir_q[11:7]}; + end + end + + OP_BRANCH: begin + if ((d_funct3 == 3'b000) || (d_funct3 == 3'b001) || (d_funct3 == 3'b110)) begin + d_uses_rs1 = 1'b1; + d_uses_rs2 = 1'b1; + d_branch = 1'b1; + d_beq = (d_funct3 == 3'b000); + d_bne = (d_funct3 == 3'b001); + d_bltu = (d_funct3 == 3'b110); + d_aluop = d_bltu ? ALU_SLTU : ALU_SUB; + d_imm = { + {19{ifid_ir_q[31]}}, + ifid_ir_q[31], + ifid_ir_q[7], + ifid_ir_q[30:25], + ifid_ir_q[11:8], + 1'b0 + }; + end + end + + OP_JAL: begin + d_jal = 1'b1; + d_reg_write = 1'b1; + d_wb_sel = 3'd2; + d_imm = { + {11{ifid_ir_q[31]}}, + ifid_ir_q[31], + ifid_ir_q[19:12], + ifid_ir_q[20], + ifid_ir_q[30:21], + 1'b0 + }; + end + + OP_JALR: begin + if (d_funct3 == 3'b000) begin + d_jalr = 1'b1; + d_uses_rs1 = 1'b1; + d_alu_src = 1'b1; + d_aluop = ALU_ADD; + d_reg_write = 1'b1; + d_wb_sel = 3'd2; + end + end + + OP_SYS: begin + // The circuit uses IR[21] to distinguish URET from ecall. + if (d_funct3 == 3'b000) begin + if (ifid_ir_q[21]) begin + d_uret = 1'b1; + end else begin + d_ecall = 1'b1; + // ecall reads a7 and a0, as documented in the sheet. + d_uses_rs1 = 1'b1; + d_uses_rs2 = 1'b1; + d_src1_idx = 5'd17; + d_src2_idx = 5'd10; + end + end else if (d_funct3 == 3'b001) begin + d_csr_write = 1'b1; // CSRRW + d_uses_rs1 = 1'b1; + d_alu_src = 1'b1; + d_imm = {27'b0, ifid_ir_q[19:15]}; + d_reg_write = (d_rd != 5'd0); + d_wb_sel = 3'd4; + end else if (d_funct3 == 3'b110) begin + d_csr_set = 1'b1; // CSRRSI + d_imm = {27'b0, ifid_ir_q[19:15]}; + d_reg_write = (d_rd != 5'd0); + d_wb_sel = 3'd4; + end else if (d_funct3 == 3'b111) begin + d_csr_clear = 1'b1; // CSRRCI + d_imm = {27'b0, ifid_ir_q[19:15]}; + d_reg_write = (d_rd != 5'd0); + d_wb_sel = 3'd4; + end + end + + default: begin + end + endcase + end + + // Register-file read with a write-first bypass for the same cycle's WB. + wire [31:0] wb_value; + assign wb_value = (memwb_wb_sel_q == 3'd1) ? memwb_mem_data_q : + (memwb_wb_sel_q == 3'd2) ? (memwb_pc_q + 32'd4) : + (memwb_wb_sel_q == 3'd3) ? memwb_alu_result2_q : + (memwb_wb_sel_q == 3'd4) ? memwb_csr_old_q : + memwb_alu_result_q; + wire [31:0] d_rs1_value = (d_src1_idx == 5'd0) ? 32'b0 : + ((memwb_valid_q && memwb_reg_write_q && + (memwb_rd_q == d_src1_idx)) ? wb_value : + regfile[d_src1_idx]); + wire [31:0] d_rs2_value = (d_src2_idx == 5'd0) ? 32'b0 : + ((memwb_valid_q && memwb_reg_write_q && + (memwb_rd_q == d_src2_idx)) ? wb_value : + regfile[d_src2_idx]); + + // ------------------------------------------------------------------------- + // IF branch prediction. JAL/JALR are resolved in EX, while conditional + // branches may redirect the fetch PC immediately from a BPB target. + // ------------------------------------------------------------------------- + wire [4:0] f_opcode = instr_i[6:2]; + wire [2:0] f_funct3 = instr_i[14:12]; + wire f_is_branch = (f_opcode == OP_BRANCH) && + ((f_funct3 == 3'b000) || (f_funct3 == 3'b001) || + (f_funct3 == 3'b110)); + wire [31:0] bpb_predict_target; + wire bpb_predict_hit; + wire bpb_predict_taken; + wire ex_branch_taken; + wire [31:0] ex_target; + wire bpb_update_enable = idex_valid_q && idex_branch_q; + wire bpb_update_taken = ex_branch_taken; + wire [31:0] bpb_update_target = ex_target; + + cpu21_bpb_8 u_bpb ( + .clk (clk), + .reset (reset), + .predict_pc (pc_q), + .predict_enable (f_is_branch), + .predict_hit (bpb_predict_hit), + .predict_taken (bpb_predict_taken), + .predict_target (bpb_predict_target), + .update_enable (bpb_update_enable), + .update_controlflow(1'b1), + .update_pc (idex_pc_q), + .update_target (bpb_update_target), + .update_taken (bpb_update_taken) + ); + + wire [31:0] fetch_next_pc = + (f_is_branch && bpb_predict_hit && bpb_predict_taken) ? + bpb_predict_target : (pc_q + 32'd4); + + // ------------------------------------------------------------------------- + // EX forwarding, ALU, and control-flow resolution. + // ------------------------------------------------------------------------- + reg [31:0] ex_src1; + reg [31:0] ex_src2; + wire ex_mem_forward_valid = exmem_valid_q && exmem_reg_write_q && + !exmem_mem_to_reg_q && (exmem_rd_q != 0); + wire wb_forward_valid = memwb_valid_q && memwb_reg_write_q && (memwb_rd_q != 0); + wire [31:0] exmem_forward_value = + (exmem_wb_sel_q == 3'd2) ? (exmem_pc_q + 32'd4) : + (exmem_wb_sel_q == 3'd3) ? exmem_alu_result2_q : + (exmem_wb_sel_q == 3'd4) ? exmem_csr_old_q : + exmem_alu_result_q; + + always @* begin + ex_src1 = idex_rs1_value_q; + ex_src2 = idex_rs2_value_q; + if (ex_mem_forward_valid && (exmem_rd_q == idex_rs1_idx_q)) ex_src1 = exmem_forward_value; + else if (wb_forward_valid && (memwb_rd_q == idex_rs1_idx_q)) ex_src1 = wb_value; + + if (ex_mem_forward_valid && (exmem_rd_q == idex_rs2_idx_q)) ex_src2 = exmem_forward_value; + else if (wb_forward_valid && (memwb_rd_q == idex_rs2_idx_q)) ex_src2 = wb_value; + end + + wire [31:0] ex_alu_b = idex_alu_src_q ? idex_imm_q : ex_src2; + wire [31:0] ex_alu_result; + wire [31:0] ex_alu_result2; + wire [31:0] ex_csr_old_value = + (idex_csr_addr_q == 12'h004) ? ustatus_q : + (idex_csr_addr_q == 12'h041) ? uepc_q : 32'b0; + cpu21_riscv_alu u_ex_alu ( + .op (idex_aluop_q), + .a (ex_src1), + .b (ex_alu_b), + .result (ex_alu_result), + .result2(ex_alu_result2) + ); + + assign ex_branch_taken = idex_branch_q && + ((idex_beq_q && (ex_src1 == ex_src2)) || + (idex_bne_q && (ex_src1 != ex_src2)) || + (idex_bltu_q && (ex_src1 < ex_src2))); + assign ex_target = idex_jalr_q ? + ((ex_src1 + idex_imm_q) & 32'hffff_fffe) : + (idex_pc_q + idex_imm_q); + wire ex_controlflow = idex_valid_q && (idex_branch_q || idex_jal_q || idex_jalr_q); + wire ex_redirect_valid = ex_controlflow && + (idex_branch_q ? + ((idex_pred_taken_q != ex_branch_taken) || + (idex_pred_taken_q && ex_branch_taken && + (idex_pred_target_q != ex_target))) : 1'b1); + wire [31:0] ex_redirect_pc = + (idex_branch_q && !ex_branch_taken) ? (idex_pc_q + 32'd4) : ex_target; + + reg [31:0] ex_store_data; + reg [3:0] ex_store_wstrb; + always @* begin + ex_store_data = ex_src2; + ex_store_wstrb = 4'b0; + if (idex_mem_write_q) begin + if (idex_mem_byte_q) begin + case (ex_alu_result[1:0]) + 2'd0: begin + ex_store_data = {24'b0, ex_src2[7:0]}; + ex_store_wstrb = 4'b0001; + end + 2'd1: begin + ex_store_data = {16'b0, ex_src2[7:0], 8'b0}; + ex_store_wstrb = 4'b0010; + end + 2'd2: begin + ex_store_data = {8'b0, ex_src2[7:0], 16'b0}; + ex_store_wstrb = 4'b0100; + end + default: begin + ex_store_data = {ex_src2[7:0], 24'b0}; + ex_store_wstrb = 4'b1000; + end + endcase + end else begin + ex_store_data = ex_src2; + ex_store_wstrb = 4'b1111; + end + end + end + + wire ex_led_event = idex_valid_q && idex_ecall_q && (ex_src1 == 32'd34); + wire ex_halt_event = idex_valid_q && idex_ecall_q && (ex_src1 != 32'd34); + + // A load in EX followed by a consumer in ID needs one bubble. ALU and + // branch dependencies are handled by the forwarding network above. + wire load_use_hazard = ifid_valid_q && idex_valid_q && idex_mem_to_reg_q && + (idex_rd_q != 5'd0) && + ((d_uses_rs1 && (d_src1_idx == idex_rd_q)) || + (d_uses_rs2 && (d_src2_idx == idex_rd_q))); + + // ------------------------------------------------------------------------- + // Interrupt sampling and priority selection. + // IRQ3 has the highest priority, then IRQ2, then IRQ1. A nested request + // is admitted only when its priority is higher than the current level. + // ------------------------------------------------------------------------- + wire [2:0] irq_event = irq_sync2_q & ~irq_prev_q; + reg irq_selected_valid; + reg [1:0] irq_selected_level; + always @* begin + irq_selected_valid = 1'b0; + irq_selected_level = 2'd0; + if (irq_pending_q[2] && (irq_current_q < 2'd3)) begin + irq_selected_valid = 1'b1; + irq_selected_level = 2'd3; + end else if (irq_pending_q[1] && (irq_current_q < 2'd2)) begin + irq_selected_valid = 1'b1; + irq_selected_level = 2'd2; + end else if (irq_pending_q[0] && (irq_current_q < 2'd1)) begin + irq_selected_valid = 1'b1; + irq_selected_level = 2'd1; + end + end + + wire ex_uret_redirect = idex_valid_q && idex_uret_q; + // uepc_q is the active context's architectural return PC. The stack + // stores the interrupted outer contexts; using uepc_q here preserves the + // course-book sequence that saves/restores uepc with CSRRW. + wire [31:0] ex_return_pc = uepc_q; + wire take_irq = irq_selected_valid && ustatus_q[0] && + (irq_depth_q < IRQ_STACK_DEPTH) && !halted_q && + !ex_redirect_valid && !ex_uret_redirect && !ex_halt_event; + // The EX instruction is allowed to complete when an interrupt is taken. + // Resume at the next architectural PC; if EX is a correctly predicted + // taken branch, that PC is its target. If EX is empty, IF/ID is the + // oldest instruction that has not executed yet. + wire [31:0] irq_save_pc = idex_valid_q ? + ((idex_branch_q && ex_branch_taken) ? ex_target : (idex_pc_q + 32'd4)) : + (ifid_valid_q ? ifid_pc_q : pc_q); + reg [31:0] irq_vector; + always @* begin + case (irq_selected_level) + 2'd1: irq_vector = IRQ1_VECTOR; + 2'd2: irq_vector = IRQ2_VECTOR; + 2'd3: irq_vector = IRQ3_VECTOR; + default: irq_vector = IRQ1_VECTOR; + endcase + end + + reg [ 2:0] irq_pending_d; + reg [31:0] ustatus_d; + reg [31:0] uepc_d; + always @* begin + irq_pending_d = irq_pending_q | irq_event; + if (take_irq) begin + case (irq_selected_level) + 2'd1: irq_pending_d[0] = 1'b0; + 2'd2: irq_pending_d[1] = 1'b0; + 2'd3: irq_pending_d[2] = 1'b0; + default: irq_pending_d = irq_pending_d; + endcase + end + + ustatus_d = ustatus_q; + uepc_d = uepc_q; + if (idex_valid_q && idex_csr_set_q) begin + if (idex_csr_addr_q == 12'h004) ustatus_d = ustatus_q | idex_imm_q; + else if (idex_csr_addr_q == 12'h041) uepc_d = uepc_q | idex_imm_q; + end else if (idex_valid_q && idex_csr_clear_q) begin + if (idex_csr_addr_q == 12'h004) ustatus_d = ustatus_q & ~idex_imm_q; + else if (idex_csr_addr_q == 12'h041) uepc_d = uepc_q & ~idex_imm_q; + end else if (idex_valid_q && idex_csr_write_q) begin + if (idex_csr_addr_q == 12'h004) ustatus_d = ex_src1; + else if (idex_csr_addr_q == 12'h041) uepc_d = ex_src1; + end + + if (take_irq) ustatus_d[0] = 1'b0; + else if (ex_uret_redirect && (irq_depth_q != 0)) ustatus_d = status_stack[irq_depth_q-1'b1]; + + if (take_irq) uepc_d = irq_save_pc; + else if (ex_uret_redirect && (irq_depth_q > 1)) uepc_d = epc_stack[irq_depth_q-2]; + else if (ex_uret_redirect && (irq_depth_q == 1)) uepc_d = 32'b0; + end + + // ------------------------------------------------------------------------- + // External/debug outputs. + // ------------------------------------------------------------------------- + assign instr_addr_o = pc_q; + assign data_addr_o = exmem_alu_result_q; + assign data_wdata_o = exmem_write_data_q; + assign data_wstrb_o = (exmem_valid_q && exmem_mem_write_q) ? exmem_wstrb_q : 4'b0; + assign data_we_o = |data_wstrb_o; + + assign led_data_o = led_data_q; + assign led_valid_o = led_valid_q; + assign if_pc_o = pc_q; + assign id_pc_o = ifid_valid_q ? ifid_pc_q : 32'b0; + assign ex_pc_o = idex_valid_q ? idex_pc_q : 32'b0; + assign mem_pc_o = exmem_valid_q ? exmem_pc_q : 32'b0; + assign wb_pc_o = memwb_valid_q ? memwb_pc_q : 32'b0; + assign if_ir_o = instr_i; + assign id_ir_o = ifid_valid_q ? ifid_ir_q : 32'b0; + assign ex_ir_o = idex_valid_q ? idex_ir_q : 32'b0; + assign mem_ir_o = exmem_valid_q ? exmem_ir_q : 32'b0; + assign wb_ir_o = memwb_valid_q ? memwb_ir_q : 32'b0; + assign rdin_o = wb_value; + assign mdin_o = exmem_write_data_q; + assign reg_write_o = memwb_valid_q && memwb_reg_write_q; + assign mem_write_o = exmem_valid_q && exmem_mem_write_q; + assign halted_o = halted_q; + assign irq_pending_o = irq_pending_q; + assign irq_current_level_o = irq_current_q; + assign uepc_o = uepc_q; + assign ustatus_o = ustatus_q; + assign bpb_predict_hit_o = bpb_predict_hit; + assign bpb_predict_taken_o = bpb_predict_taken; + assign bpb_mispredict_o = ex_redirect_valid && idex_branch_q; + assign cycle_count_o = cycle_count_q; + assign stall_count_o = stall_count_q; + assign bubble_count_o = bubble_count_q; + assign conditional_taken_count_o = conditional_taken_count_q; + assign unconditional_branch_count_o = unconditional_branch_count_q; + assign prediction_success_count_o = prediction_success_count_q; + assign prediction_failure_count_o = prediction_failure_count_q; + + wire flush_younger = take_irq || ex_uret_redirect || ex_halt_event || ex_redirect_valid; + wire pipeline_stall = load_use_hazard && !flush_younger && !halted_q; + + // ------------------------------------------------------------------------- + // State update. Ordering follows the five-stage pipeline: WB commit, + // MEM/WB capture, EX/MEM capture, then ID/EX and IF/ID/PC control. + // ------------------------------------------------------------------------- + integer r; + integer s; + always @(posedge clk or posedge reset) begin + if (reset) begin + pc_q <= RESET_PC; + ifid_valid_q <= 1'b0; + ifid_pc_q <= 32'b0; + ifid_ir_q <= 32'b0; + ifid_pred_taken_q <= 1'b0; + ifid_pred_target_q <= 32'b0; + + idex_valid_q <= 1'b0; + idex_pc_q <= 32'b0; + idex_ir_q <= 32'b0; + idex_rs1_value_q <= 32'b0; + idex_rs2_value_q <= 32'b0; + idex_rs1_idx_q <= 5'b0; + idex_rs2_idx_q <= 5'b0; + idex_rd_q <= 5'b0; + idex_imm_q <= 32'b0; + idex_aluop_q <= ALU_ADD; + idex_alu_src_q <= 1'b0; + idex_reg_write_q <= 1'b0; + idex_mem_to_reg_q <= 1'b0; + idex_mem_write_q <= 1'b0; + idex_mem_byte_q <= 1'b0; + idex_wb_sel_q <= 3'b0; + idex_csr_addr_q <= 12'b0; + idex_branch_q <= 1'b0; + idex_beq_q <= 1'b0; + idex_bne_q <= 1'b0; + idex_bltu_q <= 1'b0; + idex_jal_q <= 1'b0; + idex_jalr_q <= 1'b0; + idex_ecall_q <= 1'b0; + idex_uret_q <= 1'b0; + idex_csr_set_q <= 1'b0; + idex_csr_clear_q <= 1'b0; + idex_csr_write_q <= 1'b0; + idex_pred_taken_q <= 1'b0; + idex_pred_target_q <= 32'b0; + + exmem_valid_q <= 1'b0; + exmem_pc_q <= 32'b0; + exmem_ir_q <= 32'b0; + exmem_alu_result_q <= 32'b0; + exmem_alu_result2_q <= 32'b0; + exmem_csr_old_q <= 32'b0; + exmem_write_data_q <= 32'b0; + exmem_rd_q <= 5'b0; + exmem_reg_write_q <= 1'b0; + exmem_mem_to_reg_q <= 1'b0; + exmem_mem_write_q <= 1'b0; + exmem_wstrb_q <= 4'b0; + exmem_wb_sel_q <= 3'b0; + + memwb_valid_q <= 1'b0; + memwb_pc_q <= 32'b0; + memwb_ir_q <= 32'b0; + memwb_alu_result_q <= 32'b0; + memwb_alu_result2_q <= 32'b0; + memwb_csr_old_q <= 32'b0; + memwb_mem_data_q <= 32'b0; + memwb_rd_q <= 5'b0; + memwb_reg_write_q <= 1'b0; + memwb_mem_to_reg_q <= 1'b0; + memwb_wb_sel_q <= 3'b0; + + led_data_q <= 32'b0; + led_valid_q <= 1'b0; + halted_q <= 1'b0; + ustatus_q <= USTATUS_INIT; + uepc_q <= 32'b0; + irq_current_q <= 2'b0; + irq_pending_q <= 3'b0; + irq_sync1_q <= 3'b0; + irq_sync2_q <= 3'b0; + irq_prev_q <= 3'b0; + irq_depth_q <= 3'b0; + cycle_count_q <= 16'b0; + stall_count_q <= 16'b0; + bubble_count_q <= 16'b0; + conditional_taken_count_q <= 16'b0; + unconditional_branch_count_q <= 16'b0; + prediction_success_count_q <= 16'b0; + prediction_failure_count_q <= 16'b0; + for (r = 0; r < 32; r = r + 1) regfile[r] <= 32'b0; + for (s = 0; s < IRQ_STACK_DEPTH; s = s + 1) begin + epc_stack[s] <= 32'b0; + status_stack[s] <= 32'b0; + priority_stack[s] <= 2'b0; + end + end else begin + if (!halted_q) cycle_count_q <= cycle_count_q + 16'd1; + led_valid_q <= 1'b0; + + // WB + if (memwb_valid_q && memwb_reg_write_q && (memwb_rd_q != 5'd0)) + regfile[memwb_rd_q] <= wb_value; + regfile[0] <= 32'b0; + + // Interrupt synchronizers and pending latches. + irq_sync1_q <= irq_i; + irq_sync2_q <= irq_sync1_q; + irq_prev_q <= irq_sync2_q; + irq_pending_q <= irq_pending_d; + ustatus_q <= ustatus_d; + uepc_q <= uepc_d; + + if (ex_led_event) begin + led_data_q <= ex_src2; + led_valid_q <= 1'b1; + end + if (go_i) halted_q <= 1'b0; + if (ex_halt_event) halted_q <= 1'b1; + + if (take_irq) begin + if (irq_depth_q < IRQ_STACK_DEPTH) begin + epc_stack[irq_depth_q] <= irq_save_pc; + status_stack[irq_depth_q] <= ustatus_q; + priority_stack[irq_depth_q] <= irq_current_q; + irq_depth_q <= irq_depth_q + 3'd1; + end + irq_current_q <= irq_selected_level; + end else if (ex_uret_redirect) begin + if (irq_depth_q != 0) begin + irq_depth_q <= irq_depth_q - 3'd1; + irq_current_q <= priority_stack[irq_depth_q-1'b1]; + end + end + + // MEM/WB + memwb_valid_q <= exmem_valid_q; + memwb_pc_q <= exmem_pc_q; + memwb_ir_q <= exmem_ir_q; + memwb_alu_result_q <= exmem_alu_result_q; + memwb_alu_result2_q <= exmem_alu_result2_q; + memwb_csr_old_q <= exmem_csr_old_q; + memwb_mem_data_q <= data_rdata_i; + memwb_rd_q <= exmem_rd_q; + memwb_reg_write_q <= exmem_reg_write_q; + memwb_mem_to_reg_q <= exmem_mem_to_reg_q; + memwb_wb_sel_q <= exmem_wb_sel_q; + + // EX/MEM + exmem_valid_q <= idex_valid_q; + exmem_pc_q <= idex_pc_q; + exmem_ir_q <= idex_ir_q; + exmem_alu_result_q <= ex_alu_result; + exmem_alu_result2_q <= ex_alu_result2; + exmem_csr_old_q <= ex_csr_old_value; + exmem_write_data_q <= ex_store_data; + exmem_rd_q <= idex_rd_q; + exmem_reg_write_q <= idex_reg_write_q; + exmem_mem_to_reg_q <= idex_mem_to_reg_q; + exmem_mem_write_q <= idex_mem_write_q; + exmem_wstrb_q <= ex_store_wstrb; + exmem_wb_sel_q <= idex_wb_sel_q; + + // Control priority: interrupt, URET, ecall halt, branch redirect, + // then load-use stall, then ordinary pipeline advance. + if (flush_younger || pipeline_stall || halted_q) begin + idex_valid_q <= 1'b0; + idex_pred_taken_q <= 1'b0; + idex_pred_target_q <= 32'b0; + end else begin + idex_valid_q <= ifid_valid_q; + idex_pc_q <= ifid_pc_q; + idex_ir_q <= ifid_ir_q; + idex_rs1_value_q <= d_rs1_value; + idex_rs2_value_q <= d_rs2_value; + idex_rs1_idx_q <= d_src1_idx; + idex_rs2_idx_q <= d_src2_idx; + idex_rd_q <= d_rd; + idex_imm_q <= d_imm; + idex_aluop_q <= d_aluop; + idex_alu_src_q <= d_alu_src; + idex_reg_write_q <= d_reg_write; + idex_mem_to_reg_q <= d_mem_to_reg; + idex_mem_write_q <= d_mem_write; + idex_mem_byte_q <= d_mem_byte; + idex_wb_sel_q <= d_wb_sel; + idex_csr_addr_q <= d_csr_addr; + idex_branch_q <= d_branch; + idex_beq_q <= d_beq; + idex_bne_q <= d_bne; + idex_bltu_q <= d_bltu; + idex_jal_q <= d_jal; + idex_jalr_q <= d_jalr; + idex_ecall_q <= d_ecall; + idex_uret_q <= d_uret; + idex_csr_set_q <= d_csr_set; + idex_csr_clear_q <= d_csr_clear; + idex_csr_write_q <= d_csr_write; + idex_pred_taken_q <= ifid_pred_taken_q; + idex_pred_target_q <= ifid_pred_target_q; + end + + if (take_irq) begin + pc_q <= irq_vector; + ifid_valid_q <= 1'b0; + ifid_pred_taken_q <= 1'b0; + ifid_pred_target_q <= 32'b0; + end else if (ex_uret_redirect) begin + pc_q <= ex_return_pc; + ifid_valid_q <= 1'b0; + ifid_pred_taken_q <= 1'b0; + ifid_pred_target_q <= 32'b0; + end else if (ex_halt_event) begin + pc_q <= idex_pc_q + 32'd4; + ifid_valid_q <= 1'b0; + ifid_pred_taken_q <= 1'b0; + ifid_pred_target_q <= 32'b0; + end else if (ex_redirect_valid) begin + pc_q <= ex_redirect_pc; + ifid_valid_q <= 1'b0; + ifid_pred_taken_q <= 1'b0; + ifid_pred_target_q <= 32'b0; + end else if (pipeline_stall) begin + pc_q <= pc_q; + ifid_valid_q <= ifid_valid_q; + ifid_pc_q <= ifid_pc_q; + ifid_ir_q <= ifid_ir_q; + ifid_pred_taken_q <= ifid_pred_taken_q; + ifid_pred_target_q <= ifid_pred_target_q; + end else if (halted_q) begin + pc_q <= pc_q; + ifid_valid_q <= 1'b0; + end else begin + pc_q <= fetch_next_pc; + ifid_valid_q <= 1'b1; + ifid_pc_q <= pc_q; + ifid_ir_q <= instr_i; + ifid_pred_taken_q <= f_is_branch && bpb_predict_hit && bpb_predict_taken; + ifid_pred_target_q <= bpb_predict_target; + end + + if (pipeline_stall) begin + stall_count_q <= stall_count_q + 16'd1; + bubble_count_q <= bubble_count_q + 16'd1; + end else if (flush_younger) bubble_count_q <= bubble_count_q + 16'd2; + if (idex_valid_q && idex_branch_q && ex_branch_taken) + conditional_taken_count_q <= conditional_taken_count_q + 16'd1; + if (idex_valid_q && (idex_jal_q || idex_jalr_q)) + unconditional_branch_count_q <= unconditional_branch_count_q + 16'd1; + if (idex_valid_q && idex_branch_q) begin + if (ex_redirect_valid) prediction_failure_count_q <= prediction_failure_count_q + 16'd1; + else prediction_success_count_q <= prediction_success_count_q + 16'd1; + end + end + end +endmodule + +`default_nettype wire diff --git a/cpu21_riscv_redirect_int_bpb说明.md b/cpu21_riscv_redirect_int_bpb说明.md new file mode 100644 index 0000000..ee69486 --- /dev/null +++ b/cpu21_riscv_redirect_int_bpb说明.md @@ -0,0 +1,60 @@ +# `cpu21-riscv-4.circ` 的 Verilog 实现 + +文件:`cpu21_riscv_redirect_int_bpb.v` + +该文件对应原电路中的“重定向流水线+中断+分支预测”部分,顶层模块为 +`cpu21_riscv_redirect_int_bpb`,并包含独立的 `cpu21_riscv_alu` 和 `cpu21_bpb_8` 模块。 + +## 已转换的功能 + +- 五级流水:IF、ID、EX、MEM、WB。 +- EX/MEM 与 MEM/WB 前递,以及 load-use 一拍停顿。 +- 条件分支 `beq`、`bne`、`bltu` 在 EX 段判定,错误预测时清空 IF/ID、ID/EX 并重定向 PC。 +- `jal`、`jalr` 在 EX 段重定向,返回地址写回 `rd`。 +- 8 项全相联 BPB:标签 `PC[11:2]`,每项包含 valid、目标 PC、2 位饱和计数器和 3 位年龄字段。BPB 只由条件分支更新,避免 `jal`、`jalr` 占用不会被查询的表项;未命中时优先使用无效项,否则替换年龄最大的项。 +- 三路中断输入 `irq_i[2:0]`:两级同步、上升沿挂起、IRQ3 > IRQ2 > IRQ1 优先级,仅在 `ustatus[0]`(MIE)允许时响应。 +- 支持嵌套中断:保存 EPC、`ustatus` 和当前优先级;`URET` 使用当前 `uepc` 返回,并从硬件栈恢复外层上下文。三个入口按照原电路的 `INTpc` 选择:IRQ1=`0x30ac`、IRQ2=`0x31e4`、IRQ3=`0x3310`。`0x22` 是 `ecall` 的服务号 34,不是中断向量。 +- CSRRSI、CSRRCI,以及 CSRRW:支持 CSR 地址 `0x004`(`ustatus`)和 `0x041`(`uepc`)。立即数为 `IR[19:15]` 的零扩展值,并按 RISC-V 规则把旧 CSR 值写回 `rd`;这覆盖任务书中的 `csrrsi s6,0x41,0` 和 `csrrw zero,0x41,s6` 用法。 +- `ecall`:`a7 == 34` 时把 `a0` 锁存到 `led_data_o`;否则置 `halted_o`,直到 `go_i` 为高。 +- `sw` 和 `sb`:输出 `data_wstrb_o`,支持按字节写入。 + +## 指令编码 + +表中的 `opcode` 是控制器使用的五位字段 `IR[6:2]`,不是包含最低两位的 7 位原始 opcode。 +因此它与标准 RISC-V 指令的低 7 位 opcode 相差右移两位。 + +| 指令类别 | opcode | 说明 | +| ---------------------- | -----: | --------------------------------------- | +| R 型 | `0x0c` | add/sub/and/or/xor/slt/sltu/sll/srl/sra | +| I 型 ALU | `0x04` | addi/andi/ori/xori/slti/slli/srli/srai | +| `lw` | `0x00` | `funct3=010` | +| `sw` / `sb` | `0x08` | `funct3=010` / `000` | +| `beq` / `bne` / `bltu` | `0x18` | `funct3=000` / `001` / `110` | +| `jal` | `0x1b` | J 型立即数 | +| `jalr` | `0x19` | `funct3=000` | +| 系统类 | `0x1c` | `ecall`、`URET`、CSRRSI、CSRRCI | + +`ecall` 和 `URET` 都是 `opcode=0x1c、funct3=0`,由 `IR[21]` 区分: +`IR[21]=0` 是 `ecall`,`IR[21]=1` 是 `URET`。这一点来自原控制器中的 `IR21` 分支逻辑。 + +## 顶层端口约定 + +- `instr_i`:当前 `instr_addr_o` 对应的 32 位指令。原 Logisim 中由 ROM 提供。 +- `data_rdata_i`:当前 `data_addr_o` 的读数据。原 Logisim 中由 MIPS RAM 提供。 +- `data_addr_o`、`data_wdata_o`、`data_wstrb_o`、`data_we_o`:数据存储器接口。地址按字节地址输出;若使用原 32 位 RAM,可用 `data_addr_o[11:2]` 作为字地址。 +- `irq_i[0]`、`irq_i[1]`、`irq_i[2]`:1、2、3 号中断源;输入应为脉冲或边沿信号,模块内部会同步并锁存挂起状态。 +- `go_i`:对应原电路的“继续运行”按键。 +- `if_pc_o`、`id_pc_o`、`ex_pc_o`、`mem_pc_o`、`wb_pc_o`:五级流水当前保存的 PC,气泡时输出 0,便于替代原电路的调试探针。 +- `rdin_o`、`mdin_o`、`reg_write_o`、`mem_write_o`:对应原电路的寄存器写回和存储器写调试输出。 +- `cycle_count_o`、`stall_count_o`、`bubble_count_o`、`conditional_taken_count_o`、`unconditional_branch_count_o`、`prediction_success_count_o`、`prediction_failure_count_o`:对应原电路和课程要求的周期、停顿/气泡、分支和动态预测统计量。 + +## 时序和使用说明 + +1. `reset` 为高时异步清空流水线、寄存器文件、BPB、挂起中断和中断栈,PC 置为 `RESET_PC`;`ustatus` 复位为参数 `USTATUS_INIT`,默认 `0x0000_0001`(MIE 上电即使能)。这样做的原因是课程的中断测试程序只在中断处理程序的“保护现场”之后写 MIE,主流程不会开中断;若不默认使能,第一次中断只会挂在 `irq_pending_o` 上而永不响应。参数可覆盖,例如把 `USTATUS_INIT` 设为 `32'h0` 即可恢复“上电关中断”的行为。 +2. 指令存储器按组合读处理;数据存储器读数据在一个 MEM 周期内返回。若 RAM 为同步读,需要在外部增加一级数据寄存或相应调整 MEM/WB。 +3. 响应中断时允许当前 EX 指令完成,并保存下一条尚未执行指令的 PC;若 EX 是已正确预测的跳转分支,则保存实际目标地址。EX 为空时优先保存 IF/ID 中尚未执行的 PC。这样 `URET` 不会重复执行已经提交的存储或寄存器写操作。 +4. 默认 BPB 新表项采用弱不跳转/弱跳转状态:首次更新为不跳转时计数器为 `01`,首次更新为跳转时为 `10`;命中后按 2 位饱和规则增减。 +5. `bpb_predict_hit_o`、`bpb_predict_taken_o`、`bpb_mispredict_o` 和 `irq_*_o` 是观测端口,不参与外部控制,可以直接接逻辑分析仪或测试平台。 +6. `cycle_count_o` 在 `halted_o` 为高时暂停;load-use 每次计入一个气泡,重定向或中断清空 IF/ID 与当前 IF 时计入两个气泡。 + +原 `.circ` 文件中的 ROM、MIPS RAM、按钮、LED 和调试显示器属于 Logisim 外围,不直接搬入 RTL;其功能分别由指令接口、数据接口、`go_i`、`led_*_o` 和调试端口替代。 diff --git a/testbench/cpu21_riscv_redirect_int_bpb_rom.hex b/testbench/cpu21_riscv_redirect_int_bpb_rom.hex new file mode 100644 index 0000000..69eb8e7 --- /dev/null +++ b/testbench/cpu21_riscv_redirect_int_bpb_rom.hex @@ -0,0 +1,267 @@ +7d000113 +00100413 +01f41993 +41f9d993 +00000433 +00c00913 +00800b13 +00140413 +00f47413 +00800293 +00100313 +00499993 +0089e9b3 +01300533 +02200893 +00000073 +406282b3 +fe0294e3 +00140413 +00f00f93 +01f47433 +01c41413 +00800293 +00100313 +0049d993 +0089e9b3 +01300533 +02200893 +00000073 +406282b3 +fe0294e3 +01c45413 +406b0b33 +000b0463 +f95ff06f +000002b3 +fff2c293 +01029293 +fff2e293 +00500533 +02200893 +00000073 +f5dff06f +00512023 +00410113 +01c12023 +00410113 +00912023 +00410113 +01112023 +00410113 +00a12023 +00410113 +00812023 +00410113 +01312023 +00410113 +01412023 +00410113 +01512023 +00410113 +01612023 +00410113 +04106b73 +01612023 +00410113 +0040e073 +00200293 +01100493 +00849493 +01148493 +01049493 +00900533 +02200893 +00000073 +01c00e13 +0254d4b3 +00900533 +02200893 +00000073 +fffe0e13 +fe0e16e3 +00a00893 +00100b13 +00300a13 +00100a93 +01600433 +00441413 +014469b3 +01300533 +02200893 +00000073 +fe0416e3 +415a0a33 +fe0a10e3 +0040f073 +ffc10113 +00012b03 +041b1073 +ffc10113 +00012b03 +ffc10113 +00012a83 +ffc10113 +00012a03 +ffc10113 +00012983 +ffc10113 +00012403 +ffc10113 +00012503 +ffc10113 +00012883 +ffc10113 +00012483 +ffc10113 +00012e03 +ffc10113 +00012283 +00200073 +00612023 +00410113 +01c12023 +00410113 +00912023 +00410113 +01212023 +00410113 +01112023 +00410113 +00a12023 +00410113 +00812023 +00410113 +01312023 +00410113 +01412023 +00410113 +01512023 +00410113 +01612023 +00410113 +04106b73 +01612023 +00410113 +0040e073 +ff100493 +00900533 +02200893 +00000073 +00148493 +fe04c8e3 +00a00893 +00200b13 +00300a13 +00100a93 +01600433 +00441413 +014469b3 +01300533 +02200893 +00000073 +fe0416e3 +415a0a33 +fe0a10e3 +0040f073 +ffc10113 +00012b03 +041b1073 +ffc10113 +00012b03 +ffc10113 +00012a83 +ffc10113 +00012a03 +ffc10113 +00012983 +ffc10113 +00012403 +ffc10113 +00012503 +ffc10113 +00012883 +ffc10113 +00012903 +ffc10113 +00012483 +ffc10113 +00012e03 +ffc10113 +00012303 +00200073 +00912023 +00410113 +01112023 +00410113 +00a12023 +00410113 +00812023 +00410113 +01312023 +00410113 +01412023 +00410113 +01512023 +00410113 +01612023 +00410113 +04106b73 +01612023 +00410113 +0040e073 +00000313 +02000e13 +00000493 +00100913 +00930023 +00900533 +02200893 +00000073 +012484b3 +00130313 +fffe0e13 +fe0e12e3 +00800e13 +00000313 +00032483 +00900533 +02200893 +00000073 +00430313 +fffe0e13 +fe0e14e3 +00a00893 +00300b13 +00300a13 +00100a93 +01600433 +00441413 +014469b3 +01300533 +02200893 +00000073 +fe0416e3 +415a0a33 +fe0a10e3 +0040f073 +ffc10113 +00012b03 +041b1073 +ffc10113 +00012b03 +ffc10113 +00012a83 +ffc10113 +00012a03 +ffc10113 +00012983 +ffc10113 +00012403 +ffc10113 +00012503 +ffc10113 +00012883 +ffc10113 +00012483 +00200073 diff --git a/testbench/programs/rars_f0c874c.jar b/testbench/programs/rars_f0c874c.jar new file mode 100644 index 0000000..bdd858a Binary files /dev/null and b/testbench/programs/rars_f0c874c.jar differ diff --git a/testbench/programs/risc-v-benchmark_ccab.asm b/testbench/programs/risc-v-benchmark_ccab.asm new file mode 100644 index 0000000..06b835c --- /dev/null +++ b/testbench/programs/risc-v-benchmark_ccab.asm @@ -0,0 +1,428 @@ +############################################################# +#测试jal,jalr指令, +############################################################# +.text + addi s1,zero, 1 #测试jal,jalrr指令 + j jmp_next1 #jal x0,8 + addi s1,zero, 1 + addi s2,zero, 2 + addi s3,zero, 3 +jmp_next1: + j jmp_next2 + addi s1,zero, 1 + addi s2,zero, 2 + addi s3,zero, 3 +jmp_next2: + j jmp_next3 + addi s1,zero, 1 + addi s2,zero, 2 + addi s3,zero, 3 +jmp_next3: + j jmp_next4 + addi s1,zero, 1 + addi s2,zero, 2 + addi s3,zero, 3 +jmp_next4:jal jmp_count + +#移位测试 需要支持超addi,sll,add,ecall,srl,sll,sra,beq,j,ecall revise date:2015/12/16 tiger + +.text +addi s0,zero,1 #简单移位,循环测试,0号区域显示的是初始值1左移1位重复15次的值,1号区域是累加值 +addi s1,zero,1 +slli s1, s1, 31 #逻辑左移31位 s1=0x80000000 + + +################################################################### +# 逻辑左移测试 +# 显示区域依次显示0x80000000 0x20000000 0x08000000 0x02000000 0x00800000 0x00200000 0x00080000 0x00020000 0x00008000 0x00002000 0x00000800 0x00000200 0x00000080 0x00000020 0x00000008 0x00000002 0x00000000 +################################################################### +LogicalRightShift: #逻辑右移测试,将最高位1逐位向右右移直至结果为零 + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + +srli s1, s1, 2 +beq s1, zero, shift_next1 +j LogicalRightShift + +shift_next1: + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + + +################################################################### +# 逻辑右移测试 +# 显示区域依次显示0x00000004 0x00000010 0x00000040 0x00000100 0x00000400 0x00001000 0x00004000 0x00010000 0x00040000 0x00100000 0x00400000 0x01000000 0x04000000 0x10000000 0x40000000 0x00000000 +################################################################### + +addi s1,zero, 1 +LogicalLeftShift: #逻辑左移测试,将最低位1逐位向左移直至结果为零 +slli s1, s1, 2 + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + +beq s1, zero, ArithRightShift +j LogicalLeftShift + + +################################################################### +# 算术右移测试 +# 显示区域依次显示0x80000000 0xf0000000 0xff000000 0xfff00000 0xffff0000 0xfffff000 0xffffff00 0xfffffff0 0xffffffff +################################################################### +ArithRightShift: #算术右移测试,#算术移位测试,80000000算术右移,依次显示为F0000000,FF000000,FFF00000,FFFF0000直至FFFFFFFF + +addi s1,zero,1 +slli s1, s1, 31 #逻辑左移31位 s1=0x80000000 + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + +srai s1, s1, 3 #s1=0X80000000-->0XF0000000 + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + + +srai s1, s1, 4 #0XF0000000-->0XFF000000 + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + + +srai s1, s1, 4 #0XFF000000-->0XFFF00000 + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + +srai s1, s1, 4 + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + +srai s1, s1, 4 + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + + +srai s1, s1, 4 + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + + +srai s1, s1, 4 + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + + +srai s1, s1, 4 + + +add a0,zero,s1 #display s1 +addi a7,zero,34 # display hex +ecall # we are out of here. + +############################################################# +#走马灯测试,测试addi,andi,sll,srl,sra,or,ori,nor,ecall LED按走马灯方式来回显示数据 +############################################################# + +.text +addi s0,zero,1 +slli s3, s0, 31 # s3=0x80000000 +srai s3, s3, 31 # s3=0xFFFFFFFF +add s0,zero,zero # s0=0 +addi s2,zero,12 + +addi s6,zero,3 #走马灯计数 +zmd_loop: + +addi s0, s0, 1 #计算下一个走马灯的数据 +andi s0, s0, 15 + +####################################### +addi t0,zero,8 +addi t1,zero,1 +left: + +slli s3, s3, 4 #走马灯左移 +or s3, s3, s0 + +add a0,zero,s3 # display s3 +addi a7,zero,34 # system call for LED display +ecall # display + +sub t0,t0,t1 +bne t0,zero,left +####################################### + +addi s0, s0, 1 #计算下一个走马灯的数据 +addi t6,zero,15 +and s0, s0, t6 +slli s0, s0, 28 + +addi t0,zero,8 +addi t1,zero,1 + +zmd_right: + +srli s3, s3, 4 #走马灯右移 +or s3, s3, s0 + +add a0,zero,s3 # display s3 +addi a7,zero,34 # system call for LED display +ecall # display + +sub t0,t0,t1 +bne t0,zero,zmd_right +srli s0, s0, 28 +####################################### + +sub s6,s6,t1 +beq s6,zero, exit +j zmd_loop + +exit: + +add t0,zero,zero +xori t0,t0,-1 #test r xori +slli t0,t0,8 +ori t0,t0,255 + +add a0,zero,t0 # display t0 +addi a7,zero,34 # system call for LED display +ecall # display + +################################################################################# +#本程序实现0-15号字单元的降序排序,此程序可在rars 仿真器中运行 +#运行时请将rars Setting中的Memory Configuration设置为Compact,data at address 0 +################################################################################# + .text +sort_init: + addi s0,zero,-1 + addi s1,zero,0 + + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + sw s0,0(s1) + addi s0,s0,1 + addi s1,s1,4 + + addi s0,s0,1 + + add s0,zero,zero + addi s1,zero,60 #排序区间 +sort_loop: + lw s3,0(s0) + lw s4,0(s1) + slt t0,s3,s4 + beq t0,zero,sort_next #降序排序 + sw s3, 0(s1) + sw s4, 0(s0) +sort_next: + addi s1, s1, -4 + bne s0, s1, sort_loop + + add a0,zero,s0 #display s0 + addi a7,zero,34 # display hex + ecall # we are out of here. DISP: disp r0, 0 + + addi s0,s0,4 + addi s1,zero,60 + bne s0, s1, sort_loop + + addi a7,zero,10 # benchmark 测试完成,ecall 停止 # 停止后点击继续运行,将进入下方 CCAB 附加指令测试区 + +############################################# +# insert your ccmb benchmark program here!!! +############################################# + +#===== [1] 运算类C:DIVU 无符号除法(divu/mflo)===== +#依次输出0x11110000 0x08888000 0x04444000 0x02222000 0x01111000 0x00888800 0x00444400 0x00222200 0x00111100 0x00088880 0x00044440 0x00022220 0x00011110 0x00008888 0x00004444 0x00002222 0x00001111 0x00000888 0x00000444 0x00000222 0x00000111 0x00000088 0x00000044 0x00000022 0x00000011 0x00000008 0x00000004 0x00000002 0x00000001 +addi t0,zero,2 # /2 +addi s1,zero, 0x11 +slli s1,s1,8 +addi s1,s1,0x11 +slli s1,s1,16 + +add a0,zero,s1 +addi a7,zero,34 +ecall +addi t3,zero,28 #循环次数 + +divu_branch: + divu s1,s1,t0 #测试指令 + add a0,zero,s1 + addi a7,zero,34 + ecall #输出当前值 + addi t3,t3, -1 + bne t3,zero,divu_branch #循环 + +addi a7,zero,10 # 暂停或退出 + +#===== [2] 运算类C:REMU 无符号取余 ===== +#输出 0x87540110 +addi s1,x0,0x88 +slli s1,s1,8 +addi s1,s1,0x48 +addi s2,x0,0x87 +slli s2,s2,8 +addi s2,s2,0x54 +remu s2,s2,s1 +slli s2,s2,16 +ori s2,s2,0x110 + +add a0,zero,s2 +addi a7,zero,34 # system call for print +ecall + +addi a7,zero,10 # system call for exit + +#===== [3] 存储类A:SB 存字节 ===== +#依次输出 0x00000000 0x00000001 0x00000002 0x00000003 0x00000004 0x00000005 0x00000006 0x00000007 0x00000008 0x00000009 0x0000000a 0x0000000b 0x0000000c 0x0000000d 0x0000000e 0x0000000f 0x00000010 0x00000011 0x00000012 0x00000013 0x00000014 0x00000015 0x00000016 0x00000017 0x00000018 0x00000019 0x0000001a 0x0000001b 0x0000001c 0x0000001d 0x0000001e 0x0000001f 0x03020100 0x07060504 0x0b0a0908 0x0f0e0d0c 0x13121110 0x17161514 0x1b1a1918 0x1f1e1d1c +addi t1,zero,0 #init_addr +addi t3,zero,32 #counter + +#sb写入 01,02,03,04 +addi s1,zero, 0x00 # +addi s2,zero, 0x01 # + +sb_store: +sb s1,(t1) +add a0,zero,s1 +addi a7,zero,34 # system call for print +ecall # print + +add s1,s1,s2 #data +1 +addi t1,t1,1 # addr ++ +addi t3,t3,-1 #counter +bne t3,zero,sb_store + +addi t3,zero,8 +addi t1,zero,0 # addr +sb_branch: +lw s1,(t1) #读出数据 +add a0,zero,s1 +addi a7,zero,34 # system call for print +ecall # print +addi t1,t1,4 +addi t3,t3, -1 +bne t3,zero,sb_branch + +addi a7,zero,10 # system call for exit + +#===== [4] 跳转类B:BLT 小于则跳转(从负数向零累加)===== +#依次输出0xfffffff1 0xfffffff2 0xfffffff3 0xfffffff4 0xfffffff5 0xfffffff6 0xfffffff7 0xfffffff8 0xfffffff9 0xfffffffa 0xfffffffb 0xfffffffc 0xfffffffd 0xfffffffe 0xffffffff +addi s1,zero,-15 #初始值 +blt_branch: +add a0,zero,s1 +addi a7,zero,34 +ecall #输出当前值 +addi s1,s1,1 +blt s1,zero,blt_branch #测试指令 + +ccab_end: +addi a7,zero,10 + + #处理器实现中请用停机指令实现ecall + +jmp_count: addi s0,zero, 0 + addi s0,s0, 1 + add a0,zero,s0 + addi a7,zero,34 # display hex + ecall # we are out of here. + + addi s0,s0, 2 + add a0,zero,s0 + addi a7,zero,34 # display hex + ecall # we are out of here. + + addi s0,s0, 3 + add a0,zero,s0 + addi a7,zero,34 # display hex + ecall # we are out of here. + + addi s0,s0, 4 + add a0,zero,s0 + addi a7,zero,34 # display hex + ecall # we are out of here. + + addi s0,s0, 5 + add a0,zero,s0 + addi a7,zero,34 # display hex + ecall # we are out of here. + + addi s0,s0, 6 + add a0,zero,s0 + addi a7,zero,34 # display hex + ecall # we are out of here. + + addi s0,s0, 7 + add a0,zero,s0 + addi a7,zero,34 # display hex + ecall # we are out of here. + + addi s0,s0, 8 + add a0,zero,s0 + addi a7,zero,34 # display hex + addi a7,zero,34 # display hex + ecall # we are out of here. + + ret #persudo instruction jalr x0,x1,0 diff --git a/testbench/programs/risc-v-benchmark_ccab.hex b/testbench/programs/risc-v-benchmark_ccab.hex new file mode 100644 index 0000000..2131925 --- /dev/null +++ b/testbench/programs/risc-v-benchmark_ccab.hex @@ -0,0 +1,282 @@ +v2.0 raw +00100493 +0100006f +00100493 +00200913 +00300993 +0100006f +00100493 +00200913 +00300993 +0100006f +00100493 +00200913 +00300993 +0100006f +00100493 +00200913 +00300993 +394000ef +00100413 +00100493 +01f49493 +00900533 +02200893 +00000073 +0024d493 +00048463 +fedff06f +00900533 +02200893 +00000073 +00100493 +00249493 +00900533 +02200893 +00000073 +00048463 +fedff06f +00100493 +01f49493 +00900533 +02200893 +00000073 +4034d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +00100413 +01f41993 +41f9d993 +00000433 +00c00913 +00300b13 +00140413 +00f47413 +00800293 +00100313 +00499993 +0089e9b3 +01300533 +02200893 +00000073 +406282b3 +fe0294e3 +00140413 +00f00f93 +01f47433 +01c41413 +00800293 +00100313 +0049d993 +0089e9b3 +01300533 +02200893 +00000073 +406282b3 +fe0294e3 +01c45413 +406b0b33 +000b0463 +f95ff06f +000002b3 +fff2c293 +00829293 +0ff2e293 +00500533 +02200893 +00000073 +fff00413 +00000493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +00140413 +00000433 +03c00493 +00042983 +0004aa03 +0149a2b3 +00028663 +0134a023 +01442023 +ffc48493 +fe9412e3 +00800533 +02200893 +00000073 +00440413 +03c00493 +fc9416e3 +00a00893 +00000073 +00200293 +01100493 +00849493 +01148493 +01049493 +00900533 +02200893 +00000073 +01c00e13 +0254d4b3 +00900533 +02200893 +00000073 +fffe0e13 +fe0e16e3 +00a00893 +00000073 +08800493 +00849493 +04848493 +08700913 +00891913 +05490913 +02997933 +01091913 +11096913 +01200533 +02200893 +00000073 +00a00893 +00000073 +00000313 +02000e13 +00000493 +00100913 +00930023 +00900533 +02200893 +00000073 +012484b3 +00130313 +fffe0e13 +fe0e12e3 +00800e13 +00000313 +00032483 +00900533 +02200893 +00000073 +00430313 +fffe0e13 +fe0e14e3 +00a00893 +00000073 +ff100493 +00900533 +02200893 +00000073 +00148493 +fe04c8e3 +00a00893 +00000073 +00000413 +00140413 +00800533 +02200893 +00000073 +00240413 +00800533 +02200893 +00000073 +00340413 +00800533 +02200893 +00000073 +00440413 +00800533 +02200893 +00000073 +00540413 +00800533 +02200893 +00000073 +00640413 +00800533 +02200893 +00000073 +00740413 +00800533 +02200893 +00000073 +00840413 +00800533 +02200893 +02200893 +00000073 +00008067 diff --git a/testbench/programs/risc-v多级中断测试(EPC内存堆栈保护) - ccab.asm b/testbench/programs/risc-v多级中断测试(EPC内存堆栈保护) - ccab.asm new file mode 100644 index 0000000..8c8476a --- /dev/null +++ b/testbench/programs/risc-v多级中断测试(EPC内存堆栈保护) - ccab.asm @@ -0,0 +1,421 @@ +.text +addi sp, zero, 2000 #内存堆栈初始化 + +############################################################# +#走马灯测?,测试addi,andi,slli,srli,srai,or,ori,nor,ecall LED按走马灯方式来回显示数据 +############################################################# + +.text +START: + +addi s0,zero,1 +slli s3, s0, 31 # s3=0x80000000 +srai s3, s3, 31 # s3=0xFFFFFFFF +add s0,zero,zero # s0=0 +addi s2,zero,12 + +addi s6,zero,8 #走马灯计数 +zmd_loop: + +addi s0, s0, 1 #计算下一个走马灯的数 +andi s0, s0, 15 + +####################################### +addi t0,zero,8 +addi t1,zero,1 +left: + +slli s3, s3, 4 #走马灯左移 +or s3, s3, s0 + +add a0,zero,s3 # display s3 +addi a7,zero,34 # system call for LED display +ecall # display + +sub t0,t0,t1 +bne t0,zero,left +####################################### + +addi s0, s0, 1 #计算下一个走马灯的数 +addi t6,zero,15 +and s0, s0, t6 +slli s0, s0, 28 + +addi t0,zero,8 +addi t1,zero,1 + +zmd_right: + +srli s3, s3, 4 #走马灯右移 +or s3, s3, s0 + +add a0,zero,s3 # display s3 +addi a7,zero,34 # system call for LED display +ecall # display + +sub t0,t0,t1 +bne t0,zero,zmd_right +srli s0, s0, 28 +####################################### + +sub s6,s6,t1 +beq s6,zero, exit +j zmd_loop + +exit: + +add t0,zero,zero +xori t0,t0,-1 #test nor ori +slli t0,t0,16 +ori t0,t0,-1 + +add a0,zero,t0 # display t0 +addi a7,zero,34 # system call for LED display +ecall # display + +j START # loop forever + + +#InteruptProgram1: +############################################################################################# +# exceptoin 1 +# 使用?s6? s5?s4?s3?s0?a0?a7 +############################################################################################# + +################# 保护现场 +InteruptProgram1: +sw t0, 0(sp) #保护t0现场 +addi sp, sp, 4 +sw t3, 0(sp) #保护t3现场 +addi sp, sp, 4 +sw s1, 0(sp) #保护s1现场 +addi sp, sp, 4 +sw a7, 0(sp) +addi sp, sp, 4 +sw a0, 0(sp) +addi sp, sp, 4 +sw s0, 0(sp) +addi sp, sp, 4 +sw s3, 0(sp) +addi sp, sp, 4 +sw s4, 0(sp) +addi sp, sp, 4 +sw s5, 0(sp) +addi sp, sp, 4 +sw s6, 0(sp) +addi sp, sp, 4 + +csrrsi s6,0x41,0 #s6=uepc +sw s6, 0(sp) #uepc压栈保护 +addi sp, sp, 4 + + +################# 开中断 +csrrsi zero,0x4,1 #ustatus.MIE=1 + +################# DIVU测试 +addi t0,zero,2 # /2 +addi s1,zero, 0x11 +slli s1,s1,8 +addi s1,s1,0x11 +slli s1,s1,16 + +add a0,zero,s1 +addi a7,zero,34 +ecall +addi t3,zero,28 #ѭ������ + +divu_branch: + divu s1,s1,t0 #����ָ�� + add a0,zero,s1 + addi a7,zero,34 + ecall #�����ǰֵ + addi t3,t3, -1 + bne t3,zero,divu_branch #ѭ�� + + +addi a7,zero,10 + +################# 中断服务 +addi s6,zero,1 #中断?1,2,3 不同中断号显示值不一样 +addi s4,zero,3 #循环次数初始值 +addi s5,zero,1 #计数器累加值 + +IntLoop1: +add s0,zero,s6 + +IntLeftShift1: + +slli s0, s0, 4 +or s3,s0,s4 +add a0,zero,s3 #display s0 +addi a7,zero,34 # display hex +ecall + +bne s0, zero, IntLeftShift1 +sub s4,s4,s5 #循环次数递减 +bne s4, zero, IntLoop1 + +################# 关中断 +csrrci zero,0x4,1 #ustatus.MIE=0 + +################# 恢复现场 +addi sp, sp, -4 +lw s6, 0(sp) + +csrrw zero,0x41,s6 #uepc=s6 + +addi sp, sp, -4 +lw s6, 0(sp) +addi sp, sp, -4 +lw s5, 0(sp) +addi sp, sp, -4 +lw s4, 0(sp) +addi sp, sp, -4 +lw s3, 0(sp) +addi sp, sp, -4 +lw s0, 0(sp) +addi sp, sp, -4 +lw a0, 0(sp) +addi sp, sp, -4 +lw a7, 0(sp) +addi sp, sp, -4 +lw s1, 0(sp) #恢复s1现场 +addi sp, sp, -4 +lw t3, 0(sp) #恢复t3现场 +addi sp, sp, -4 +lw t0, 0(sp) #恢复t0现场 + +################ 中断返回 +uret #同步开中断,uepc-->pc + + +#InteruptProgram2: +############################################################################################# +# exceptoin 2 +# 使用?s6? s5?s4?s3?s0?a0?a7 +############################################################################################# + +################# 保护现场 +InteruptProgram2: +sw t1, 0(sp) #保护t1现场 +addi sp, sp, 4 +sw t3, 0(sp) #保护t3现场 +addi sp, sp, 4 +sw s1, 0(sp) #保护s1现场 +addi sp, sp, 4 +sw s2, 0(sp) #保护s2现场 +addi sp, sp, 4 +sw a7, 0(sp) +addi sp, sp, 4 +sw a0, 0(sp) +addi sp, sp, 4 +sw s0, 0(sp) +addi sp, sp, 4 +sw s3, 0(sp) +addi sp, sp, 4 +sw s4, 0(sp) +addi sp, sp, 4 +sw s5, 0(sp) +addi sp, sp, 4 +sw s6, 0(sp) +addi sp, sp, 4 + +csrrsi s6,0x41,0 #s6=uepc +sw s6, 0(sp) #uepc压栈保护 +addi sp, sp, 4 + + +################# 开中断 +csrrsi zero,0x4,1 #ustatus.MIE=1 + +################# BLT测试 +addi s1,zero,-15 #初始值 +blt_branch: +add a0,zero,s1 +addi a7,zero,34 +ecall #输出当前值 +addi s1,s1,1 +blt s1,zero,blt_branch #当前指令 + + +addi a7,zero,10 + +################# 中断服务 + +addi s6,zero,2 #中断?1,2,3 不同中断号显示值不一样 + +addi s4,zero,3 #循环次数初始值 +addi s5,zero,1 #计数器累加值 + +IntLoop2: +add s0,zero,s6 + +IntLeftShift2: + + +slli s0, s0, 4 +or s3,s0,s4 +add a0,zero,s3 #display s0 +addi a7,zero,34 # display hex +ecall + +bne s0, zero, IntLeftShift2 +sub s4,s4,s5 #循环次数递减 +bne s4, zero, IntLoop2 + +################# 关中断 +csrrci zero,0x4,1 #ustatus.MIE=0 + +################# 恢复现场 + + +addi sp, sp, -4 +lw s6, 0(sp) +csrrw zero,0x41,s6 #uepc=s6 + +addi sp, sp, -4 +lw s6, 0(sp) +addi sp, sp, -4 +lw s5, 0(sp) +addi sp, sp, -4 +lw s4, 0(sp) +addi sp, sp, -4 +lw s3, 0(sp) +addi sp, sp, -4 +lw s0, 0(sp) #恢复s0现场 +addi sp, sp, -4 +lw a0, 0(sp) +addi sp, sp, -4 +lw a7, 0(sp) +addi sp, sp, -4 +lw s2, 0(sp) #恢复s2现场 +addi sp, sp, -4 +lw s1, 0(sp) #恢复s1现场 +addi sp, sp, -4 +lw t3, 0(sp) #恢复t3现场 +addi sp, sp, -4 +lw t1, 0(sp) #恢复t1现场 + +################ 中断返回 +uret #同步开中断,uepc-->pc + + +#InteruptProgram3: +############################################################################################# +# exceptoin 3 +# 使用?s6? s5?s4?s3?s0?a0?a7 +############################################################################################# + + +################# 保护现场 +InteruptProgram3: +sw s1, 0(sp) #保护s1现场 +addi sp, sp, 4 +sw a7, 0(sp) +addi sp, sp, 4 +sw a0, 0(sp) +addi sp, sp, 4 +sw s0, 0(sp) +addi sp, sp, 4 +sw s3, 0(sp) +addi sp, sp, 4 +sw s4, 0(sp) +addi sp, sp, 4 +sw s5, 0(sp) +addi sp, sp, 4 +sw s6, 0(sp) +addi sp, sp, 4 + +csrrsi s6,0x41,0 #s6=uepc +sw s6, 0(sp) #uepc压栈保护 +addi sp, sp, 4 + + +################# 开中断 +csrrsi zero,0x4,1 #ustatus.MIE=1 + +################# SB测试 +addi t1,zero,0 #init_addr +addi t3,zero,32 #counter + +#sb写入 01,02,03,04 +addi s1,zero, 0x00 # +addi s2,zero, 0x01 # + +sb_store: +sb s1,(t1) +add a0,zero,s1 +addi a7,zero,34 # system call for print +ecall # print + +add s1,s1,s2 #data +1 +addi t1,t1,1 # addr ++ +addi t3,t3,-1 #counter +bne t3,zero,sb_store + +addi t3,zero,8 +addi t1,zero,0 # addr +sb_branch: +lw s1,(t1) #读出数据 +add a0,zero,s1 +addi a7,zero,34 # system call for print +ecall # print +addi t1,t1,4 +addi t3,t3, -1 +bne t3,zero,sb_branch + +addi a7,zero,10 # system call for exit + + +################# 中断服务 +addi s6,zero,3 #中断?1,2,3 不同中断号显示值不一样 +addi s4,zero,3 #循环次数初始值 +addi s5,zero,1 #计数器累加值 + +IntLoop3: +add s0,zero,s6 + +IntLeftShift3: + +slli s0, s0, 4 +or s3,s0,s4 +add a0,zero,s3 #display s0 +addi a7,zero,34 # display hex +ecall + +bne s0, zero, IntLeftShift3 +sub s4,s4,s5 #循环次数递减 +bne s4, zero, IntLoop3 + +################# 关中断 +csrrci zero,0x4,1 #ustatus.MIE=0 + + + +################# 恢复现场 + +addi sp, sp, -4 +lw s6, 0(sp) +csrrw zero,0x41,s6 #uepc=s6 + +addi sp, sp, -4 +lw s6, 0(sp) +addi sp, sp, -4 +lw s5, 0(sp) +addi sp, sp, -4 +lw s4, 0(sp) +addi sp, sp, -4 +lw s3, 0(sp) +addi sp, sp, -4 +lw s0, 0(sp) +addi sp, sp, -4 +lw a0, 0(sp) +addi sp, sp, -4 +lw a7, 0(sp) +addi sp, sp, -4 +lw s1, 0(sp) #恢复s1现场 + +################ 中断返回 +uret #同步开中断,uepc-->pc + diff --git a/testbench/programs/risc-v多级中断测试(EPC内存堆栈保护) - ccab.hex b/testbench/programs/risc-v多级中断测试(EPC内存堆栈保护) - ccab.hex new file mode 100644 index 0000000..493d05e --- /dev/null +++ b/testbench/programs/risc-v多级中断测试(EPC内存堆栈保护) - ccab.hex @@ -0,0 +1,270 @@ +7d000113 +00100413 +01f41993 +41f9d993 +00000433 +00c00913 +00800b13 +00140413 +00f47413 +00800293 +00100313 +00499993 +0089e9b3 +01300533 +02200893 +00000073 +406282b3 +fe0294e3 +00140413 +00f00f93 +01f47433 +01c41413 +00800293 +00100313 +0049d993 +0089e9b3 +01300533 +02200893 +00000073 +406282b3 +fe0294e3 +01c45413 +406b0b33 +000b0463 +f95ff06f +000002b3 +fff2c293 +01029293 +fff2e293 +00500533 +02200893 +00000073 +f5dff06f +00512023 +00410113 +01c12023 +00410113 +00912023 +00410113 +01112023 +00410113 +00a12023 +00410113 +00812023 +00410113 +01312023 +00410113 +01412023 +00410113 +01512023 +00410113 +01612023 +00410113 +04106b73 +01612023 +00410113 +0040e073 +00200293 +01100493 +00849493 +01148493 +01049493 +00900533 +02200893 +00000073 +01c00e13 +0254d4b3 +00900533 +02200893 +00000073 +fffe0e13 +fe0e16e3 +00a00893 +00000073 +00100b13 +00300a13 +00100a93 +01600433 +00441413 +014469b3 +01300533 +02200893 +00000073 +fe0416e3 +415a0a33 +fe0a10e3 +0040f073 +ffc10113 +00012b03 +041b1073 +ffc10113 +00012b03 +ffc10113 +00012a83 +ffc10113 +00012a03 +ffc10113 +00012983 +ffc10113 +00012403 +ffc10113 +00012503 +ffc10113 +00012883 +ffc10113 +00012483 +ffc10113 +00012e03 +ffc10113 +00012283 +00200073 +00612023 +00410113 +01c12023 +00410113 +00912023 +00410113 +01212023 +00410113 +01112023 +00410113 +00a12023 +00410113 +00812023 +00410113 +01312023 +00410113 +01412023 +00410113 +01512023 +00410113 +01612023 +00410113 +04106b73 +01612023 +00410113 +0040e073 +ff100493 +00900533 +02200893 +00000073 +00148493 +fe04c8e3 +00a00893 +00000073 +00200b13 +00300a13 +00100a93 +01600433 +00441413 +014469b3 +01300533 +02200893 +00000073 +fe0416e3 +415a0a33 +fe0a10e3 +0040f073 +ffc10113 +00012b03 +041b1073 +ffc10113 +00012b03 +ffc10113 +00012a83 +ffc10113 +00012a03 +ffc10113 +00012983 +ffc10113 +00012903 +ffc10113 +00012483 +ffc10113 +00012e03 +ffc10113 +00012303 +ffc10113 +00012403 +ffc10113 +00012503 +ffc10113 +00012883 +00200073 +00912023 +00410113 +01112023 +00410113 +00a12023 +00410113 +00812023 +00410113 +01312023 +00410113 +01412023 +00410113 +01512023 +00410113 +01612023 +00410113 +04106b73 +01612023 +00410113 +0040e073 +00000313 +02000e13 +00000493 +00100913 +00930023 +00900533 +02200893 +00000073 +012484b3 +00130313 +fffe0e13 +fe0e12e3 +00800e13 +00000313 +00032483 +00900533 +02200893 +00000073 +00430313 +fffe0e13 +fe0e14e3 +00a00893 +00000073 +00300b13 +00300a13 +00100a93 +01600433 +00441413 +014469b3 +01300533 +02200893 +00000073 +fe0416e3 +415a0a33 +fe0a10e3 +0040f073 +ffc10113 +00012b03 +041b1073 +ffc10113 +00012b03 +ffc10113 +00012a83 +ffc10113 +00012a03 +ffc10113 +00012483 +ffc10113 +00012983 +ffc10113 +00012403 +ffc10113 +00012503 +ffc10113 +00012883 +00200073 diff --git a/testbench/risc-v-benchmark_ccab.hex b/testbench/risc-v-benchmark_ccab.hex new file mode 100644 index 0000000..7e8eaaa --- /dev/null +++ b/testbench/risc-v-benchmark_ccab.hex @@ -0,0 +1,276 @@ +00100493 +0100006f +00100493 +00200913 +00300993 +0100006f +00100493 +00200913 +00300993 +0100006f +00100493 +00200913 +00300993 +0100006f +00100493 +00200913 +00300993 +380000ef +00100413 +00100493 +01f49493 +00900533 +02200893 +00000073 +0024d493 +00048463 +fedff06f +00900533 +02200893 +00000073 +00100493 +00249493 +00900533 +02200893 +00000073 +00048463 +fedff06f +00100493 +01f49493 +00900533 +02200893 +00000073 +4034d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +4044d493 +00900533 +02200893 +00000073 +00100413 +01f41993 +41f9d993 +00000433 +00c00913 +00300b13 +00140413 +00f47413 +00800293 +00100313 +00499993 +0089e9b3 +01300533 +02200893 +00000073 +406282b3 +fe0294e3 +00140413 +00f00f93 +01f47433 +01c41413 +00800293 +00100313 +0049d993 +0089e9b3 +01300533 +02200893 +00000073 +406282b3 +fe0294e3 +01c45413 +406b0b33 +000b0463 +f95ff06f +000002b3 +fff2c293 +00829293 +0ff2e293 +00500533 +02200893 +00000073 +fff00413 +00000493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +0084a023 +00140413 +00448493 +00140413 +00000433 +03c00493 +00042983 +0004aa03 +0149a2b3 +00028663 +0134a023 +01442023 +ffc48493 +fe9412e3 +00800533 +02200893 +00000073 +00440413 +03c00493 +fc9416e3 +00a00893 +00200293 +01100493 +00849493 +01148493 +01049493 +00900533 +02200893 +00000073 +01c00e13 +0254d4b3 +00900533 +02200893 +00000073 +fffe0e13 +fe0e16e3 +00a00893 +08800493 +00849493 +04848493 +08700913 +00891913 +05490913 +02997933 +01091913 +11096913 +01200533 +02200893 +00000073 +00a00893 +00000313 +02000e13 +00000493 +00100913 +00930023 +00900533 +02200893 +00000073 +012484b3 +00130313 +fffe0e13 +fe0e12e3 +00800e13 +00000313 +00032483 +00900533 +02200893 +00000073 +00430313 +fffe0e13 +fe0e14e3 +00a00893 +ff100493 +00900533 +02200893 +00000073 +00148493 +fe04c8e3 +00a00893 +00000413 +00140413 +00800533 +02200893 +00000073 +00240413 +00800533 +02200893 +00000073 +00340413 +00800533 +02200893 +00000073 +00440413 +00800533 +02200893 +00000073 +00540413 +00800533 +02200893 +00000073 +00640413 +00800533 +02200893 +00000073 +00740413 +00800533 +02200893 +00000073 +00840413 +00800533 +02200893 +02200893 +00000073 +00008067 diff --git a/testbench/tb_cpu21_riscv_redirect_int_bpb.v b/testbench/tb_cpu21_riscv_redirect_int_bpb.v new file mode 100644 index 0000000..c1940bf --- /dev/null +++ b/testbench/tb_cpu21_riscv_redirect_int_bpb.v @@ -0,0 +1,273 @@ +// Compile together with cpu21_riscv_redirect_int_bpb.v. +// Example: +// iverilog -g2012 -o cpu21_sim cpu21_riscv_redirect_int_bpb.v tb_cpu21_riscv_redirect_int_bpb.v +// vvp cpu21_sim +// The simulation writes cpu21_riscv_redirect_int_bpb.vcd for GTKWave. +// +// The program image is loaded into the ROM model with $readmemh from +// ROM_FILE. A failed $readmemh only prints a warning and leaves the memory +// untouched, so a wrong path used to make the ROM look like all-NOP +// (0x00000013). The simulator working directory is not fixed (Vivado xsim +// runs in .sim/sim_1/behav/xsim), so this testbench probes a list of +// candidate paths and reports which one worked. An explicit path always +// wins: +// vvp cpu21_sim +ROM_FILE= (iverilog/vvp) +// xelab ... -generic_top "ROM_FILE=" (Vivado xsim) +module tb_cpu21_riscv_redirect_int_bpb #( + parameter ROM_FILE = "risc-v-benchmark_ccab.hex" + // risc-v-benchmark_ccab.hex + // cpu21_riscv_redirect_int_bpb_rom.hex +); + localparam integer ROM_WORDS = 1024; + localparam integer RAM_WORDS = 1024; + localparam integer PATH_LEN = 512; + // Sentinel that cannot appear as the first ROM word of a RISC-V image. + localparam [31:0] EMPTY_ROM_WORD = 32'hFFFF_FFFF; + + // Interrupt entry addresses, shared by the DUT instance and the trace + // condition below. The DUT addresses the ROM with PC[11:2], so these three + // entries live at words 43 / 120 / 192 of the program image (the first + // "sw ..., 0(sp)" of each handler prologue). + localparam [31:0] IRQ1_VECTOR = 32'h0000_30ac; + localparam [31:0] IRQ2_VECTOR = 32'h0000_31e0; + localparam [31:0] IRQ3_VECTOR = 32'h0000_3300; + + reg clk; + reg reset; + reg [ 31:0] instr_i; + reg [ 31:0] data_rdata_i; + reg [ 2:0] irq_i; + reg go_i; + + reg [ 31:0] rom [0:ROM_WORDS-1]; + reg [ 31:0] ram [0:RAM_WORDS-1]; + + wire [ 31:0] instr_addr_o; + wire [ 31:0] data_addr_o; + wire [ 31:0] data_wdata_o; + wire [ 3:0] data_wstrb_o; + wire data_we_o; + wire [ 31:0] led_data_o; + wire led_valid_o; + wire [ 31:0] if_pc_o; + wire [ 31:0] id_pc_o; + wire [ 31:0] ex_pc_o; + wire [ 31:0] mem_pc_o; + wire [ 31:0] wb_pc_o; + wire [ 31:0] if_ir_o; + wire [ 31:0] id_ir_o; + wire [ 31:0] ex_ir_o; + wire [ 31:0] mem_ir_o; + wire [ 31:0] wb_ir_o; + wire [ 31:0] rdin_o; + wire [ 31:0] mdin_o; + wire reg_write_o; + wire mem_write_o; + wire halted_o; + + wire [ 2:0] irq_pending_o; + wire [ 1:0] irq_current_level_o; + wire [ 31:0] uepc_o; + wire [ 31:0] ustatus_o; + wire bpb_predict_hit_o; + wire bpb_predict_taken_o; + wire bpb_mispredict_o; + + wire [ 15:0] cycle_count_o; + wire [ 15:0] stall_count_o; + wire [ 15:0] bubble_count_o; + wire [ 15:0] conditional_taken_count_o; + wire [ 15:0] unconditional_branch_count_o; + wire [ 15:0] prediction_success_count_o; + wire [ 15:0] prediction_failure_count_o; + + integer i; + integer tb_cycle; + reg rom_loaded; + reg [8*PATH_LEN-1:0] rom_file_path; + + cpu21_riscv_redirect_int_bpb #( + .RESET_PC (32'h0000_0000), + .IRQ1_VECTOR(IRQ1_VECTOR), + .IRQ2_VECTOR(IRQ2_VECTOR), + .IRQ3_VECTOR(IRQ3_VECTOR), + // The interrupt test program writes MIE only inside its handlers, so + // the testbench starts with MIE already set (ustatus[0] = 1). + .USTATUS_INIT(32'h0000_0001) + ) dut ( + .clk (clk), + .reset (reset), + .instr_i (instr_i), + .data_rdata_i (data_rdata_i), + .irq_i (irq_i), + .go_i (go_i), + .instr_addr_o (instr_addr_o), + .data_addr_o (data_addr_o), + .data_wdata_o (data_wdata_o), + .data_wstrb_o (data_wstrb_o), + .data_we_o (data_we_o), + .led_data_o (led_data_o), + .led_valid_o (led_valid_o), + .if_pc_o (if_pc_o), + .id_pc_o (id_pc_o), + .ex_pc_o (ex_pc_o), + .mem_pc_o (mem_pc_o), + .wb_pc_o (wb_pc_o), + .if_ir_o (if_ir_o), + .id_ir_o (id_ir_o), + .ex_ir_o (ex_ir_o), + .mem_ir_o (mem_ir_o), + .wb_ir_o (wb_ir_o), + .rdin_o (rdin_o), + .mdin_o (mdin_o), + .reg_write_o (reg_write_o), + .mem_write_o (mem_write_o), + .halted_o (halted_o), + .irq_pending_o (irq_pending_o), + .irq_current_level_o (irq_current_level_o), + .uepc_o (uepc_o), + .ustatus_o (ustatus_o), + .bpb_predict_hit_o (bpb_predict_hit_o), + .bpb_predict_taken_o (bpb_predict_taken_o), + .bpb_mispredict_o (bpb_mispredict_o), + .cycle_count_o (cycle_count_o), + .stall_count_o (stall_count_o), + .bubble_count_o (bubble_count_o), + .conditional_taken_count_o (conditional_taken_count_o), + .unconditional_branch_count_o(unconditional_branch_count_o), + .prediction_success_count_o (prediction_success_count_o), + .prediction_failure_count_o (prediction_failure_count_o) + ); + + always #5 clk = ~clk; + + // The Logisim ROM uses PC[11:2] as its word address. This also maps + // the three original interrupt vectors to the same ROM entries. + always @* begin + instr_i = 32'h00000013; + if (instr_addr_o[11:2] < ROM_WORDS) instr_i = rom[instr_addr_o[11:2]]; + end + + // Asynchronous read model for data memory. + always @* begin + data_rdata_i = 32'b0; + if (data_addr_o[11:2] < RAM_WORDS) data_rdata_i = ram[data_addr_o[11:2]]; + end + + // Synchronous write model with byte enables. + always @(posedge clk) begin + if (!reset && data_we_o && (data_addr_o[11:2] < RAM_WORDS)) begin + if (data_wstrb_o[0]) ram[data_addr_o[11:2]][7:0] <= data_wdata_o[7:0]; + if (data_wstrb_o[1]) ram[data_addr_o[11:2]][15:8] <= data_wdata_o[15:8]; + if (data_wstrb_o[2]) ram[data_addr_o[11:2]][23:16] <= data_wdata_o[23:16]; + if (data_wstrb_o[3]) ram[data_addr_o[11:2]][31:24] <= data_wdata_o[31:24]; + end + end + + always @(posedge clk) begin + if (reset) begin + tb_cycle = 0; + end else begin + tb_cycle = tb_cycle + 1; + if ((tb_cycle <= 20) || data_we_o || led_valid_o || + (irq_pending_o != 3'b000) || + (instr_addr_o == IRQ1_VECTOR) || + (instr_addr_o == IRQ2_VECTOR) || + (instr_addr_o == IRQ3_VECTOR)) begin + $display( + "t=%0t cyc=%0d PC=%08h ID=%08h EX=%08h MEM=%08h WB=%08h irq=%b pend=%b uepc=%08h LEDv=%b LED=%08h memwe=%b addr=%08h wdata=%08h wstrb=%b", + $time, tb_cycle, if_pc_o, id_ir_o, ex_ir_o, mem_ir_o, wb_ir_o, irq_i, irq_pending_o, + uepc_o, led_valid_o, led_data_o, data_we_o, data_addr_o, data_wdata_o, data_wstrb_o); + end + end + end + + // Try to load the ROM image from "fname". "loaded" is 1 when the file + // existed and contained at least one word. A sentinel in rom[0] makes a + // silent load failure impossible to miss. + task load_rom_image; + input [8*PATH_LEN-1:0] fname; + output loaded; + integer fh; + begin + loaded = 1'b0; + fh = $fopen(fname, "r"); + if (fh != 0) begin + $fclose(fh); + rom[0] = EMPTY_ROM_WORD; + $readmemh(fname, rom); + if (rom[0] === EMPTY_ROM_WORD) begin + // Opened but empty: restore the default NOP fill. + rom[0] = 32'h0000_0013; + end else begin + rom_file_path = fname; + loaded = 1'b1; + $display("NOTE: ROM image loaded from \"%0s\".", fname); + end + end + end + endtask + + initial begin + clk = 1'b0; + reset = 1'b1; + irq_i = 3'b000; + go_i = 1'b0; + tb_cycle = 0; + + for (i = 0; i < ROM_WORDS; i = i + 1) rom[i] = 32'h00000013; + for (i = 0; i < RAM_WORDS; i = i + 1) ram[i] = 32'b0; + + // Seed a few words so memory activity is visible in the waveform. + ram[1] = 32'h1234_5678; + ram[2] = 32'h89ab_cdef; + + // Probe the usual locations for the ROM image. The first readable + // candidate wins; failed probes below are harmless (the ROM keeps its + // 0x00000013 fill until a real image is found). + $display("NOTE: searching for ROM image \"%0s\" ...", ROM_FILE); + + rom_loaded = 1'b0; + if ($value$plusargs("ROM_FILE=%s", rom_file_path)) load_rom_image(rom_file_path, rom_loaded); + if (!rom_loaded) load_rom_image(ROM_FILE, rom_loaded); + if (!rom_loaded) load_rom_image({"testbench/", ROM_FILE}, rom_loaded); + if (!rom_loaded) load_rom_image({"../testbench/", ROM_FILE}, rom_loaded); + if (!rom_loaded) load_rom_image({"../../../testbench/", ROM_FILE}, rom_loaded); + if (!rom_loaded) load_rom_image({"../../../../../testbench/", ROM_FILE}, rom_loaded); + if (!rom_loaded) + $display( + "WARNING: no ROM image found for \"%0s\" - the ROM stays filled with NOPs (0x00000013).", + ROM_FILE + ); + + $dumpfile("cpu21_riscv_redirect_int_bpb.vcd"); + $dumpvars(0, tb_cpu21_riscv_redirect_int_bpb); + + #22 reset = 1'b0; + + // Button-like interrupt pulses. The DUT synchronizes and latches them. + repeat (80) @(negedge clk); + irq_i[0] = 1'b1; + @(negedge clk); + irq_i[0] = 1'b0; + + repeat (180) @(negedge clk); + irq_i[1] = 1'b1; + @(negedge clk); + irq_i[1] = 1'b0; + + repeat (180) @(negedge clk); + irq_i[2] = 1'b1; + @(negedge clk); + irq_i[2] = 1'b0; + + repeat (5560) @(negedge clk); + $display( + "FINAL: tb_cycles=%0d dut_cycles=%0d stalls=%0d bubbles=%0d cond_taken=%0d uncond=%0d pred_ok=%0d pred_fail=%0d halted=%b", + tb_cycle, cycle_count_o, stall_count_o, bubble_count_o, conditional_taken_count_o, + unconditional_branch_count_o, prediction_success_count_o, prediction_failure_count_o, + halted_o); + $finish; + end +endmodule + diff --git a/真值表.txt b/真值表.txt new file mode 100644 index 0000000..c721a2f --- /dev/null +++ b/真值表.txt @@ -0,0 +1,33 @@ +# 指令 "Funct7 +(十进制)" "Funct3 +(十进制)" "OpCode +(十六进制)" ALU_OP MemtoReg MemWrite ALU_Src RegWrite ecall S_Type BEQ BNE Jal jalr REMU sel BLT r1u r2u CSR CSRW CSRRCI +1 add 0 0 C 5 1 1 1 +2 sub 32 0 C 6 1 1 1 +3 and 0 7 C 7 1 1 1 +4 or 0 6 C 8 1 1 1 +5 slt 0 2 C 11 1 1 1 +6 sltu 0 3 C 12 1 1 1 +7 addi 0 4 5 1 1 1 +8 andi 7 4 7 1 1 1 +9 ori 6 4 8 1 1 1 +10 xori 4 4 9 1 1 1 +11 slti 2 4 11 1 1 1 +12 slli 0 1 4 0 1 1 1 +13 srli 0 5 4 2 1 1 1 +14 srai 32 5 4 1 1 1 1 +15 lw 2 0 5 1 1 1 1 +16 sw 2 8 5 1 1 1 1 1 +17 ecall 0 0 1C 1 1 1 +18 beq 0 18 6 1 1 1 +19 bne 1 18 6 1 1 1 +20 jal 1B 1 1 +21 jalr 0 19 6 1 1 1 1 +22 CSRRSI 6 1C 8 1 1 1 +23 CSRRCI 7 1C 7 1 1 1 1 +24 URET 2 0 1C 1 +25 DIVU 1 5 C 4 1 1 1 +26 REMU 1 7 C 4 1 1 1 1 +27 SB 0 8 5 1 1 1 1 1 1 +28 BLT 4 18 11 1 1 1 +29 CSRRW 1 1C 1 1 1