Merge stages D and X, and bring all branch resolution into X. Passes RV32I compliance
This commit is contained in:
parent
844fa8f97f
commit
692abbad8b
|
@ -1,3 +1,9 @@
|
||||||
[submodule "test/riscv-compliance/riscv-compliance"]
|
[submodule "test/riscv-compliance/riscv-compliance"]
|
||||||
path = test/riscv-compliance/riscv-compliance
|
path = test/riscv-compliance/riscv-compliance
|
||||||
url = https://github.com/riscv/riscv-compliance.git
|
url = https://github.com/riscv/riscv-compliance.git
|
||||||
|
[submodule "test/riscv-compliance/riscv-arch-test"]
|
||||||
|
path = test/riscv-compliance/riscv-arch-test
|
||||||
|
url = https://github.com/riscv/riscv-arch-test.git
|
||||||
|
[submodule "scripts"]
|
||||||
|
path = scripts
|
||||||
|
url = https://github.com/Wren6991/fpgascripts
|
||||||
|
|
|
@ -84,20 +84,20 @@ localparam HSIZE_BYTE = 3'd0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
// ----------------------------------------------------------------------------
|
||||||
// Pipe Stage F
|
// Pipe Stage F
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
wire m_jump_req;
|
|
||||||
wire [W_ADDR-1:0] m_jump_target;
|
|
||||||
wire d_jump_req;
|
|
||||||
wire [W_ADDR-1:0] d_jump_target;
|
|
||||||
|
|
||||||
wire f_jump_req = d_jump_req || m_jump_req;
|
wire f_jump_req;
|
||||||
wire [W_ADDR-1:0] f_jump_target = m_jump_req ? m_jump_target : d_jump_target;
|
wire [W_ADDR-1:0] f_jump_target;
|
||||||
wire f_jump_rdy;
|
wire f_jump_rdy;
|
||||||
wire f_jump_now = f_jump_req && f_jump_rdy;
|
wire f_jump_now = f_jump_req && f_jump_rdy;
|
||||||
|
|
||||||
|
// Predecoded register numbers, for register file access
|
||||||
|
wire f_regnum_vld;
|
||||||
|
wire [W_REGADDR-1:0] f_rs1;
|
||||||
|
wire [W_REGADDR-1:0] f_rs2;
|
||||||
|
|
||||||
wire [31:0] fd_cir;
|
wire [31:0] fd_cir;
|
||||||
wire [1:0] fd_cir_vld;
|
wire [1:0] fd_cir_vld;
|
||||||
wire [1:0] df_cir_use;
|
wire [1:0] df_cir_use;
|
||||||
|
@ -130,14 +130,17 @@ hazard3_frontend #(
|
||||||
.cir (fd_cir),
|
.cir (fd_cir),
|
||||||
.cir_vld (fd_cir_vld),
|
.cir_vld (fd_cir_vld),
|
||||||
.cir_use (df_cir_use),
|
.cir_use (df_cir_use),
|
||||||
.cir_lock (df_cir_lock)
|
.cir_lock (df_cir_lock),
|
||||||
|
|
||||||
|
.next_regs_rs1 (f_rs1),
|
||||||
|
.next_regs_rs2 (f_rs2),
|
||||||
|
.next_regs_vld (f_regnum_vld)
|
||||||
);
|
);
|
||||||
|
|
||||||
assign flush_d_x = m_jump_req && f_jump_rdy;
|
assign flush_d_x = m_jump_req && f_jump_rdy;
|
||||||
|
|
||||||
// ============================================================================
|
// ----------------------------------------------------------------------------
|
||||||
// Pipe Stage D
|
// Pipe Stage X (Decode Logic)
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// X-check on pieces of instruction which frontend claims are valid
|
// X-check on pieces of instruction which frontend claims are valid
|
||||||
//synthesis translate_off
|
//synthesis translate_off
|
||||||
|
@ -155,33 +158,29 @@ always @ (posedge clk) begin
|
||||||
end
|
end
|
||||||
//synthesis translate_on
|
//synthesis translate_on
|
||||||
|
|
||||||
wire [W_ADDR-1:0] d_pc; // FIXME only used for riscv-formal
|
// To X
|
||||||
|
wire d_jump_req;
|
||||||
// To register file
|
wire [W_ADDR-1:0] d_jump_target;
|
||||||
|
wire [W_DATA-1:0] d_imm;
|
||||||
wire [W_REGADDR-1:0] d_rs1;
|
wire [W_REGADDR-1:0] d_rs1;
|
||||||
wire [W_REGADDR-1:0] d_rs2;
|
wire [W_REGADDR-1:0] d_rs2;
|
||||||
|
wire [W_REGADDR-1:0] d_rd;
|
||||||
|
wire [W_ALUSRC-1:0] d_alusrc_a;
|
||||||
|
wire [W_ALUSRC-1:0] d_alusrc_b;
|
||||||
|
wire [W_ALUOP-1:0] d_aluop;
|
||||||
|
wire [W_MEMOP-1:0] d_memop;
|
||||||
|
wire [W_MULOP-1:0] d_mulop;
|
||||||
|
wire [W_BCOND-1:0] d_branchcond;
|
||||||
|
wire d_jump_is_regoffs;
|
||||||
|
wire d_result_is_linkaddr;
|
||||||
|
wire [W_ADDR-1:0] d_pc;
|
||||||
|
wire [W_ADDR-1:0] d_mispredict_addr;
|
||||||
|
wire [W_EXCEPT-1:0] d_except;
|
||||||
|
wire d_csr_ren;
|
||||||
|
wire d_csr_wen;
|
||||||
|
wire [1:0] d_csr_wtype;
|
||||||
|
wire d_csr_w_imm;
|
||||||
|
|
||||||
// To X
|
|
||||||
wire [W_DATA-1:0] dx_imm;
|
|
||||||
wire [W_REGADDR-1:0] dx_rs1;
|
|
||||||
wire [W_REGADDR-1:0] dx_rs2;
|
|
||||||
wire [W_REGADDR-1:0] dx_rd;
|
|
||||||
wire [W_ALUSRC-1:0] dx_alusrc_a;
|
|
||||||
wire [W_ALUSRC-1:0] dx_alusrc_b;
|
|
||||||
wire [W_ALUOP-1:0] dx_aluop;
|
|
||||||
wire [W_MEMOP-1:0] dx_memop;
|
|
||||||
wire [W_MULOP-1:0] dx_mulop;
|
|
||||||
wire [W_BCOND-1:0] dx_branchcond;
|
|
||||||
wire [W_ADDR-1:0] dx_jump_target;
|
|
||||||
wire dx_jump_is_regoffs;
|
|
||||||
wire dx_result_is_linkaddr;
|
|
||||||
wire [W_ADDR-1:0] dx_pc;
|
|
||||||
wire [W_ADDR-1:0] dx_mispredict_addr;
|
|
||||||
wire [W_EXCEPT-1:0] dx_except;
|
|
||||||
wire dx_csr_ren;
|
|
||||||
wire dx_csr_wen;
|
|
||||||
wire [1:0] dx_csr_wtype;
|
|
||||||
wire dx_csr_w_imm;
|
|
||||||
|
|
||||||
hazard3_decode #(
|
hazard3_decode #(
|
||||||
`include "hazard3_config_inst.vh"
|
`include "hazard3_config_inst.vh"
|
||||||
|
@ -204,33 +203,30 @@ hazard3_decode #(
|
||||||
.f_jump_now (f_jump_now),
|
.f_jump_now (f_jump_now),
|
||||||
.f_jump_target (f_jump_target),
|
.f_jump_target (f_jump_target),
|
||||||
|
|
||||||
|
.d_imm (d_imm),
|
||||||
.d_rs1 (d_rs1),
|
.d_rs1 (d_rs1),
|
||||||
.d_rs2 (d_rs2),
|
.d_rs2 (d_rs2),
|
||||||
.dx_imm (dx_imm),
|
.d_rd (d_rd),
|
||||||
.dx_rs1 (dx_rs1),
|
.d_alusrc_a (d_alusrc_a),
|
||||||
.dx_rs2 (dx_rs2),
|
.d_alusrc_b (d_alusrc_b),
|
||||||
.dx_rd (dx_rd),
|
.d_aluop (d_aluop),
|
||||||
.dx_alusrc_a (dx_alusrc_a),
|
.d_memop (d_memop),
|
||||||
.dx_alusrc_b (dx_alusrc_b),
|
.d_mulop (d_mulop),
|
||||||
.dx_aluop (dx_aluop),
|
.d_csr_ren (d_csr_ren),
|
||||||
.dx_memop (dx_memop),
|
.d_csr_wen (d_csr_wen),
|
||||||
.dx_mulop (dx_mulop),
|
.d_csr_wtype (d_csr_wtype),
|
||||||
.dx_csr_ren (dx_csr_ren),
|
.d_csr_w_imm (d_csr_w_imm),
|
||||||
.dx_csr_wen (dx_csr_wen),
|
.d_branchcond (d_branchcond),
|
||||||
.dx_csr_wtype (dx_csr_wtype),
|
.d_jump_target (d_jump_target),
|
||||||
.dx_csr_w_imm (dx_csr_w_imm),
|
.d_jump_is_regoffs (d_jump_is_regoffs),
|
||||||
.dx_branchcond (dx_branchcond),
|
.d_result_is_linkaddr (d_result_is_linkaddr),
|
||||||
.dx_jump_target (dx_jump_target),
|
.d_pc (d_pc),
|
||||||
.dx_jump_is_regoffs (dx_jump_is_regoffs),
|
.d_mispredict_addr (d_mispredict_addr),
|
||||||
.dx_result_is_linkaddr (dx_result_is_linkaddr),
|
.d_except (d_except)
|
||||||
.dx_pc (dx_pc),
|
|
||||||
.dx_mispredict_addr (dx_mispredict_addr),
|
|
||||||
.dx_except (dx_except)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// ============================================================================
|
// ----------------------------------------------------------------------------
|
||||||
// Pipe Stage X
|
// Pipe Stage X (Execution Logic)
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// Register the write which took place to the regfile on previous cycle, and bypass.
|
// Register the write which took place to the regfile on previous cycle, and bypass.
|
||||||
// This is an alternative to a write -> read bypass in the regfile,
|
// This is an alternative to a write -> read bypass in the regfile,
|
||||||
|
@ -239,8 +235,8 @@ reg [W_REGADDR-1:0] mw_rd;
|
||||||
reg [W_DATA-1:0] mw_result;
|
reg [W_DATA-1:0] mw_result;
|
||||||
|
|
||||||
// From register file:
|
// From register file:
|
||||||
wire [W_DATA-1:0] dx_rdata1;
|
wire [W_DATA-1:0] x_rdata1;
|
||||||
wire [W_DATA-1:0] dx_rdata2;
|
wire [W_DATA-1:0] x_rdata2;
|
||||||
|
|
||||||
// Combinational regs for muxing
|
// Combinational regs for muxing
|
||||||
reg [W_DATA-1:0] x_rs1_bypass;
|
reg [W_DATA-1:0] x_rs1_bypass;
|
||||||
|
@ -260,19 +256,9 @@ reg [W_REGADDR-1:0] xm_rs1;
|
||||||
reg [W_REGADDR-1:0] xm_rs2;
|
reg [W_REGADDR-1:0] xm_rs2;
|
||||||
reg [W_REGADDR-1:0] xm_rd;
|
reg [W_REGADDR-1:0] xm_rd;
|
||||||
reg [W_DATA-1:0] xm_result;
|
reg [W_DATA-1:0] xm_result;
|
||||||
reg [W_ADDR-1:0] xm_jump_target;
|
|
||||||
reg [W_DATA-1:0] xm_store_data;
|
reg [W_DATA-1:0] xm_store_data;
|
||||||
reg xm_jump;
|
|
||||||
reg [W_MEMOP-1:0] xm_memop;
|
reg [W_MEMOP-1:0] xm_memop;
|
||||||
|
|
||||||
// For JALR, the LSB of the result must be cleared by hardware
|
|
||||||
wire [W_ADDR-1:0] x_taken_jump_target = dx_jump_is_regoffs ? x_alu_add & ~32'h1 : dx_jump_target;
|
|
||||||
wire [W_ADDR-1:0] x_jump_target =
|
|
||||||
x_trap_exit ? x_mepc : // Note precedence -- it's possible to have enter && exit, but in this case enter_rdy is false.
|
|
||||||
x_trap_enter ? x_trap_addr :
|
|
||||||
dx_imm[31] && dx_branchcond != BCOND_ALWAYS ? dx_mispredict_addr :
|
|
||||||
x_taken_jump_target;
|
|
||||||
|
|
||||||
reg x_stall_raw;
|
reg x_stall_raw;
|
||||||
wire x_stall_muldiv;
|
wire x_stall_muldiv;
|
||||||
|
|
||||||
|
@ -287,24 +273,24 @@ always @ (*) begin
|
||||||
x_stall_raw = 1'b0;
|
x_stall_raw = 1'b0;
|
||||||
if (REDUCED_BYPASS) begin
|
if (REDUCED_BYPASS) begin
|
||||||
x_stall_raw =
|
x_stall_raw =
|
||||||
|xm_rd && (xm_rd == dx_rs1 || xm_rd == dx_rs2) ||
|
|xm_rd && (xm_rd == d_rs1 || xm_rd == d_rs2) ||
|
||||||
|mw_rd && (mw_rd == dx_rs1 || mw_rd == dx_rs2);
|
|mw_rd && (mw_rd == d_rs1 || mw_rd == d_rs2);
|
||||||
end else if (m_generating_result) begin
|
end else if (m_generating_result) begin
|
||||||
// With the full bypass network, load-use (or fast multiply-use) is the only RAW stall
|
// With the full bypass network, load-use (or fast multiply-use) is the only RAW stall
|
||||||
if (|xm_rd && xm_rd == dx_rs1) begin
|
if (|xm_rd && xm_rd == d_rs1) begin
|
||||||
// Store addresses cannot be bypassed later, so there is no exception here.
|
// Store addresses cannot be bypassed later, so there is no exception here.
|
||||||
x_stall_raw = 1'b1;
|
x_stall_raw = 1'b1;
|
||||||
end else if (|xm_rd && xm_rd == dx_rs2) begin
|
end else if (|xm_rd && xm_rd == d_rs2) begin
|
||||||
// Store data can be bypassed in M. Any other instructions must stall.
|
// Store data can be bypassed in M. Any other instructions must stall.
|
||||||
x_stall_raw = !(dx_memop == MEMOP_SW || dx_memop == MEMOP_SH || dx_memop == MEMOP_SB);
|
x_stall_raw = !(d_memop == MEMOP_SW || d_memop == MEMOP_SH || d_memop == MEMOP_SB);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// AHB transaction request
|
// AHB transaction request
|
||||||
|
|
||||||
wire x_memop_vld = !dx_memop[3];
|
wire x_memop_vld = !d_memop[3];
|
||||||
wire x_memop_write = dx_memop == MEMOP_SW || dx_memop == MEMOP_SH || dx_memop == MEMOP_SB;
|
wire x_memop_write = d_memop == MEMOP_SW || d_memop == MEMOP_SH || d_memop == MEMOP_SB;
|
||||||
wire x_unaligned_addr =
|
wire x_unaligned_addr =
|
||||||
bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] ||
|
bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] ||
|
||||||
bus_hsize_d == HSIZE_HWORD && bus_haddr_d[0];
|
bus_hsize_d == HSIZE_HWORD && bus_haddr_d[0];
|
||||||
|
@ -316,7 +302,7 @@ always @ (*) begin
|
||||||
// Need to be careful not to use anything hready-sourced to gate htrans!
|
// Need to be careful not to use anything hready-sourced to gate htrans!
|
||||||
bus_haddr_d = x_alu_add;
|
bus_haddr_d = x_alu_add;
|
||||||
bus_hwrite_d = x_memop_write;
|
bus_hwrite_d = x_memop_write;
|
||||||
case (dx_memop)
|
case (d_memop)
|
||||||
MEMOP_LW: bus_hsize_d = HSIZE_WORD;
|
MEMOP_LW: bus_hsize_d = HSIZE_WORD;
|
||||||
MEMOP_SW: bus_hsize_d = HSIZE_WORD;
|
MEMOP_SW: bus_hsize_d = HSIZE_WORD;
|
||||||
MEMOP_LH: bus_hsize_d = HSIZE_HWORD;
|
MEMOP_LH: bus_hsize_d = HSIZE_HWORD;
|
||||||
|
@ -332,42 +318,42 @@ end
|
||||||
|
|
||||||
// ALU operand muxes and bypass
|
// ALU operand muxes and bypass
|
||||||
always @ (*) begin
|
always @ (*) begin
|
||||||
if (~|dx_rs1) begin
|
if (~|d_rs1) begin
|
||||||
x_rs1_bypass = {W_DATA{1'b0}};
|
x_rs1_bypass = {W_DATA{1'b0}};
|
||||||
end else if (xm_rd == dx_rs1) begin
|
end else if (xm_rd == d_rs1) begin
|
||||||
x_rs1_bypass = xm_result;
|
x_rs1_bypass = xm_result;
|
||||||
end else if (mw_rd == dx_rs1 && !REDUCED_BYPASS) begin
|
end else if (mw_rd == d_rs1 && !REDUCED_BYPASS) begin
|
||||||
x_rs1_bypass = mw_result;
|
x_rs1_bypass = mw_result;
|
||||||
end else begin
|
end else begin
|
||||||
x_rs1_bypass = dx_rdata1;
|
x_rs1_bypass = x_rdata1;
|
||||||
end
|
end
|
||||||
if (~|dx_rs2) begin
|
if (~|d_rs2) begin
|
||||||
x_rs2_bypass = {W_DATA{1'b0}};
|
x_rs2_bypass = {W_DATA{1'b0}};
|
||||||
end else if (xm_rd == dx_rs2) begin
|
end else if (xm_rd == d_rs2) begin
|
||||||
x_rs2_bypass = xm_result;
|
x_rs2_bypass = xm_result;
|
||||||
end else if (mw_rd == dx_rs2 && !REDUCED_BYPASS) begin
|
end else if (mw_rd == d_rs2 && !REDUCED_BYPASS) begin
|
||||||
x_rs2_bypass = mw_result;
|
x_rs2_bypass = mw_result;
|
||||||
end else begin
|
end else begin
|
||||||
x_rs2_bypass = dx_rdata2;
|
x_rs2_bypass = x_rdata2;
|
||||||
end
|
end
|
||||||
|
|
||||||
if (|dx_alusrc_a)
|
if (|d_alusrc_a)
|
||||||
x_op_a = dx_pc;
|
x_op_a = d_pc;
|
||||||
else
|
else
|
||||||
x_op_a = x_rs1_bypass;
|
x_op_a = x_rs1_bypass;
|
||||||
|
|
||||||
if (|dx_alusrc_b)
|
if (|d_alusrc_b)
|
||||||
x_op_b = dx_imm;
|
x_op_b = d_imm;
|
||||||
else
|
else
|
||||||
x_op_b = x_rs2_bypass;
|
x_op_b = x_rs2_bypass;
|
||||||
end
|
end
|
||||||
|
|
||||||
// CSRs and Trap Handling
|
// CSRs and Trap Handling
|
||||||
|
|
||||||
wire x_except_ecall = dx_except == EXCEPT_ECALL;
|
wire x_except_ecall = d_except == EXCEPT_ECALL;
|
||||||
wire x_except_breakpoint = dx_except == EXCEPT_EBREAK;
|
wire x_except_breakpoint = d_except == EXCEPT_EBREAK;
|
||||||
wire x_except_invalid_instr = dx_except == EXCEPT_INSTR_ILLEGAL;
|
wire x_except_invalid_instr = d_except == EXCEPT_INSTR_ILLEGAL;
|
||||||
assign x_trap_exit = dx_except == EXCEPT_MRET && !(x_stall || m_jump_req);
|
assign x_trap_exit = d_except == EXCEPT_MRET && !(x_stall || m_jump_req);
|
||||||
wire x_trap_enter_rdy = !(x_stall || m_jump_req || x_trap_exit);
|
wire x_trap_enter_rdy = !(x_stall || m_jump_req || x_trap_exit);
|
||||||
wire x_trap_is_exception; // diagnostic
|
wire x_trap_is_exception; // diagnostic
|
||||||
|
|
||||||
|
@ -380,8 +366,8 @@ always @ (posedge clk) begin
|
||||||
end
|
end
|
||||||
`endif
|
`endif
|
||||||
|
|
||||||
wire [W_DATA-1:0] x_csr_wdata = dx_csr_w_imm ?
|
wire [W_DATA-1:0] x_csr_wdata = d_csr_w_imm ?
|
||||||
{{W_DATA-5{1'b0}}, dx_rs1} : x_rs1_bypass;
|
{{W_DATA-5{1'b0}}, d_rs1} : x_rs1_bypass;
|
||||||
|
|
||||||
wire [W_DATA-1:0] x_csr_rdata;
|
wire [W_DATA-1:0] x_csr_rdata;
|
||||||
|
|
||||||
|
@ -394,21 +380,21 @@ hazard3_csr #(
|
||||||
// CSR access port
|
// CSR access port
|
||||||
// *en_soon are early access strobes which are not a function of bus stall.
|
// *en_soon are early access strobes which are not a function of bus stall.
|
||||||
// Can generate access faults (hence traps), but do not actually perform access.
|
// Can generate access faults (hence traps), but do not actually perform access.
|
||||||
.addr (dx_imm[11:0]),
|
.addr (d_imm[11:0]), // todo could just connect this to the instruction bits
|
||||||
.wdata (x_csr_wdata),
|
.wdata (x_csr_wdata),
|
||||||
.wen_soon (dx_csr_wen),
|
.wen_soon (d_csr_wen),
|
||||||
.wen (dx_csr_wen && !(x_stall || flush_d_x)),
|
.wen (d_csr_wen && !(x_stall || flush_d_x)),
|
||||||
.wtype (dx_csr_wtype),
|
.wtype (d_csr_wtype),
|
||||||
.rdata (x_csr_rdata),
|
.rdata (x_csr_rdata),
|
||||||
.ren_soon (dx_csr_ren),
|
.ren_soon (d_csr_ren),
|
||||||
.ren (dx_csr_ren && !(x_stall || flush_d_x)),
|
.ren (d_csr_ren && !(x_stall || flush_d_x)),
|
||||||
// Trap signalling
|
// Trap signalling
|
||||||
.trap_addr (x_trap_addr),
|
.trap_addr (x_trap_addr),
|
||||||
.trap_enter_vld (x_trap_enter),
|
.trap_enter_vld (x_trap_enter),
|
||||||
.trap_enter_rdy (x_trap_enter_rdy),
|
.trap_enter_rdy (x_trap_enter_rdy),
|
||||||
.trap_exit (x_trap_exit),
|
.trap_exit (x_trap_exit),
|
||||||
.trap_is_exception (x_trap_is_exception),
|
.trap_is_exception (x_trap_is_exception),
|
||||||
.mepc_in (dx_pc),
|
.mepc_in (d_pc),
|
||||||
.mepc_out (x_mepc),
|
.mepc_out (x_mepc),
|
||||||
// IRQ and exception requests
|
// IRQ and exception requests
|
||||||
.irq (irq),
|
.irq (irq),
|
||||||
|
@ -447,9 +433,9 @@ if (EXTENSION_M) begin: has_muldiv
|
||||||
|
|
||||||
wire x_muldiv_kill = flush_d_x || x_trap_enter; // TODO this takes an extra cycle to kill muldiv before trap entry
|
wire x_muldiv_kill = flush_d_x || x_trap_enter; // TODO this takes an extra cycle to kill muldiv before trap entry
|
||||||
|
|
||||||
wire x_use_fast_mul = MUL_FAST && dx_aluop == ALUOP_MULDIV && dx_mulop == M_OP_MUL;
|
wire x_use_fast_mul = MUL_FAST && d_aluop == ALUOP_MULDIV && d_mulop == M_OP_MUL;
|
||||||
|
|
||||||
assign x_muldiv_op_vld = (dx_aluop == ALUOP_MULDIV && !x_use_fast_mul)
|
assign x_muldiv_op_vld = (d_aluop == ALUOP_MULDIV && !x_use_fast_mul)
|
||||||
&& !(x_muldiv_posted || x_stall_raw || x_muldiv_kill);
|
&& !(x_muldiv_posted || x_stall_raw || x_muldiv_kill);
|
||||||
|
|
||||||
hazard3_muldiv_seq #(
|
hazard3_muldiv_seq #(
|
||||||
|
@ -458,7 +444,7 @@ if (EXTENSION_M) begin: has_muldiv
|
||||||
) muldiv (
|
) muldiv (
|
||||||
.clk (clk),
|
.clk (clk),
|
||||||
.rst_n (rst_n),
|
.rst_n (rst_n),
|
||||||
.op (dx_mulop),
|
.op (d_mulop),
|
||||||
.op_vld (x_muldiv_op_vld),
|
.op_vld (x_muldiv_op_vld),
|
||||||
.op_rdy (x_muldiv_op_rdy),
|
.op_rdy (x_muldiv_op_rdy),
|
||||||
.op_kill (x_muldiv_kill),
|
.op_kill (x_muldiv_kill),
|
||||||
|
@ -472,17 +458,17 @@ if (EXTENSION_M) begin: has_muldiv
|
||||||
|
|
||||||
// TODO fusion of MULHx->MUL and DIVy->REMy sequences
|
// TODO fusion of MULHx->MUL and DIVy->REMy sequences
|
||||||
wire x_muldiv_result_is_high =
|
wire x_muldiv_result_is_high =
|
||||||
dx_mulop == M_OP_MULH ||
|
d_mulop == M_OP_MULH ||
|
||||||
dx_mulop == M_OP_MULHSU ||
|
d_mulop == M_OP_MULHSU ||
|
||||||
dx_mulop == M_OP_MULHU ||
|
d_mulop == M_OP_MULHU ||
|
||||||
dx_mulop == M_OP_REM ||
|
d_mulop == M_OP_REM ||
|
||||||
dx_mulop == M_OP_REMU;
|
d_mulop == M_OP_REMU;
|
||||||
assign x_muldiv_result = x_muldiv_result_is_high ? x_muldiv_result_h : x_muldiv_result_l;
|
assign x_muldiv_result = x_muldiv_result_is_high ? x_muldiv_result_h : x_muldiv_result_l;
|
||||||
assign x_stall_muldiv = x_muldiv_op_vld || !x_muldiv_result_vld;
|
assign x_stall_muldiv = x_muldiv_op_vld || !x_muldiv_result_vld;
|
||||||
|
|
||||||
if (MUL_FAST) begin: has_fast_mul
|
if (MUL_FAST) begin: has_fast_mul
|
||||||
|
|
||||||
wire x_issue_fast_mul = x_use_fast_mul && |dx_rd && !(x_stall || flush_d_x);
|
wire x_issue_fast_mul = x_use_fast_mul && |d_rd && !(x_stall || flush_d_x);
|
||||||
|
|
||||||
hazard3_mul_fast #(
|
hazard3_mul_fast #(
|
||||||
.XLEN(W_DATA)
|
.XLEN(W_DATA)
|
||||||
|
@ -506,7 +492,7 @@ if (EXTENSION_M) begin: has_muldiv
|
||||||
end
|
end
|
||||||
|
|
||||||
`ifdef FORMAL
|
`ifdef FORMAL
|
||||||
always @ (posedge clk) if (dx_aluop != ALUOP_MULDIV) assert(!x_stall_muldiv);
|
always @ (posedge clk) if (d_aluop != ALUOP_MULDIV) assert(!x_stall_muldiv);
|
||||||
`endif
|
`endif
|
||||||
|
|
||||||
end else begin: no_muldiv
|
end else begin: no_muldiv
|
||||||
|
@ -519,37 +505,23 @@ end else begin: no_muldiv
|
||||||
end
|
end
|
||||||
endgenerate
|
endgenerate
|
||||||
|
|
||||||
// State machine and branch detection
|
// State machine
|
||||||
always @ (posedge clk or negedge rst_n) begin
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
if (!rst_n) begin
|
if (!rst_n) begin
|
||||||
xm_jump <= 1'b0;
|
|
||||||
xm_memop <= MEMOP_NONE;
|
xm_memop <= MEMOP_NONE;
|
||||||
{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
|
{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
|
||||||
end else begin
|
end else begin
|
||||||
// TODO: this assertion may become untrue depending on how we handle exceptions/IRQs when stalled?
|
// TODO: this assertion may become untrue depending on how we handle exceptions/IRQs when stalled?
|
||||||
//`ASSERT(!(m_stall && flush_d_x));// bubble insertion logic below is broken otherwise
|
//`ASSERT(!(m_stall && flush_d_x));// bubble insertion logic below is broken otherwise
|
||||||
if (!m_stall) begin
|
if (!m_stall) begin
|
||||||
{xm_rs1, xm_rs2, xm_rd} <= {dx_rs1, dx_rs2, dx_rd};
|
{xm_rs1, xm_rs2, xm_rd} <= {d_rs1, d_rs2, d_rd};
|
||||||
// If the transfer is unaligned, make sure it is completely NOP'd on the bus
|
// If the transfer is unaligned, make sure it is completely NOP'd on the bus
|
||||||
xm_memop <= dx_memop | {x_unaligned_addr, 3'h0};
|
xm_memop <= d_memop | {x_unaligned_addr, 3'h0};
|
||||||
if (x_stall || flush_d_x || x_trap_enter) begin
|
if (x_stall || flush_d_x || x_trap_enter) begin
|
||||||
// Insert bubble
|
// Insert bubble
|
||||||
xm_rd <= {W_REGADDR{1'b0}};
|
xm_rd <= {W_REGADDR{1'b0}};
|
||||||
xm_jump <= 1'b0;
|
|
||||||
xm_memop <= MEMOP_NONE;
|
xm_memop <= MEMOP_NONE;
|
||||||
end
|
end
|
||||||
if (!(x_stall || flush_d_x)) begin
|
|
||||||
case (dx_branchcond)
|
|
||||||
BCOND_ALWAYS: xm_jump <= 1'b1;
|
|
||||||
// For branches, we are either taking a branch late, or recovering from
|
|
||||||
// an incorrectly taken branch, depending on sign of branch offset.
|
|
||||||
BCOND_ZERO: xm_jump <= !x_alu_cmp ^ dx_imm[31];
|
|
||||||
BCOND_NZERO: xm_jump <= x_alu_cmp ^ dx_imm[31];
|
|
||||||
default xm_jump <= 1'b0;
|
|
||||||
endcase
|
|
||||||
if (x_trap_enter || x_trap_exit)
|
|
||||||
xm_jump <= 1'b1;
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -558,16 +530,34 @@ end
|
||||||
always @ (posedge clk)
|
always @ (posedge clk)
|
||||||
if (!m_stall) begin
|
if (!m_stall) begin
|
||||||
xm_result <=
|
xm_result <=
|
||||||
dx_result_is_linkaddr ? dx_mispredict_addr :
|
d_result_is_linkaddr ? d_mispredict_addr :
|
||||||
dx_csr_ren ? x_csr_rdata :
|
d_csr_ren ? x_csr_rdata :
|
||||||
EXTENSION_M && dx_aluop == ALUOP_MULDIV ? x_muldiv_result :
|
EXTENSION_M && d_aluop == ALUOP_MULDIV ? x_muldiv_result :
|
||||||
x_alu_result;
|
x_alu_result;
|
||||||
xm_store_data <= x_rs2_bypass;
|
xm_store_data <= x_rs2_bypass;
|
||||||
xm_jump_target <= x_jump_target;
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
// Branch handling
|
||||||
|
|
||||||
|
// For JALR, the LSB of the result must be cleared by hardware
|
||||||
|
wire [W_ADDR-1:0] x_taken_jump_target = d_jump_is_regoffs ? x_alu_add & ~32'h1 : d_jump_target;
|
||||||
|
wire [W_ADDR-1:0] x_jump_target =
|
||||||
|
x_trap_exit ? x_mepc : // Note precedence -- it's possible to have enter && exit, but in this case enter_rdy is false.
|
||||||
|
x_trap_enter ? x_trap_addr :
|
||||||
|
x_taken_jump_target;
|
||||||
|
|
||||||
|
wire x_jump_req =
|
||||||
|
x_trap_enter || x_trap_exit ||
|
||||||
|
d_branchcond == BCOND_ALWAYS ||
|
||||||
|
d_branchcond == BCOND_ZERO && !x_alu_cmp ||
|
||||||
|
d_branchcond == BCOND_NZERO && x_alu_cmp;
|
||||||
|
|
||||||
|
assign f_jump_req = d_jump_req || x_jump_req;
|
||||||
|
assign f_jump_target = x_jump_target;
|
||||||
|
|
||||||
|
|
||||||
hazard3_alu alu (
|
hazard3_alu alu (
|
||||||
.aluop (dx_aluop),
|
.aluop (d_aluop),
|
||||||
.op_a (x_op_a),
|
.op_a (x_op_a),
|
||||||
.op_b (x_op_b),
|
.op_b (x_op_b),
|
||||||
.result (x_alu_result),
|
.result (x_alu_result),
|
||||||
|
@ -575,15 +565,12 @@ hazard3_alu alu (
|
||||||
.cmp (x_alu_cmp)
|
.cmp (x_alu_cmp)
|
||||||
);
|
);
|
||||||
|
|
||||||
// ============================================================================
|
// ----------------------------------------------------------------------------
|
||||||
// Pipe Stage M
|
// Pipe Stage M
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
reg [W_DATA-1:0] m_rdata_shift;
|
reg [W_DATA-1:0] m_rdata_shift;
|
||||||
reg [W_DATA-1:0] m_wdata;
|
reg [W_DATA-1:0] m_wdata;
|
||||||
reg [W_DATA-1:0] m_result;
|
reg [W_DATA-1:0] m_result;
|
||||||
assign m_jump_req = xm_jump;
|
|
||||||
assign m_jump_target = xm_jump_target;
|
|
||||||
|
|
||||||
assign m_stall = (!xm_memop[3] && !bus_dph_ready_d) || (m_jump_req && !f_jump_rdy);
|
assign m_stall = (!xm_memop[3] && !bus_dph_ready_d) || (m_jump_req && !f_jump_rdy);
|
||||||
|
|
||||||
|
@ -652,9 +639,8 @@ always @ (posedge clk)
|
||||||
if (!m_stall)
|
if (!m_stall)
|
||||||
mw_result <= m_result;
|
mw_result <= m_result;
|
||||||
|
|
||||||
// ============================================================================
|
// ----------------------------------------------------------------------------
|
||||||
// Pipe Stage W
|
// Pipe Stage W
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// mw_result and mw_rd register the most recent write to the register file,
|
// mw_result and mw_rd register the most recent write to the register file,
|
||||||
// so that X can bypass them in.
|
// so that X can bypass them in.
|
||||||
|
@ -686,12 +672,12 @@ hazard3_regfile_1w2r #(
|
||||||
) inst_regfile_1w2r (
|
) inst_regfile_1w2r (
|
||||||
.clk (clk),
|
.clk (clk),
|
||||||
.rst_n (rst_n),
|
.rst_n (rst_n),
|
||||||
// On stall, we feed X's addresses back into regfile
|
// On downstream stall, we feed D's addresses back into regfile
|
||||||
// so that output does not change.
|
// so that output does not change.
|
||||||
.raddr1 (x_stall ? dx_rs1 : d_rs1),
|
.raddr1 (x_stall ? d_rs1 : f_rs1),
|
||||||
.rdata1 (dx_rdata1),
|
.rdata1 (x_rdata1),
|
||||||
.raddr2 (x_stall ? dx_rs2 : d_rs2),
|
.raddr2 (x_stall ? d_rs2 : f_rs2),
|
||||||
.rdata2 (dx_rdata2),
|
.rdata2 (x_rdata2),
|
||||||
|
|
||||||
.waddr (xm_rd),
|
.waddr (xm_rd),
|
||||||
.wdata (m_result),
|
.wdata (m_result),
|
||||||
|
|
|
@ -29,7 +29,7 @@ module hazard3_decode #(
|
||||||
output wire df_cir_lock,
|
output wire df_cir_lock,
|
||||||
output reg d_jump_req,
|
output reg d_jump_req,
|
||||||
output reg [W_ADDR-1:0] d_jump_target,
|
output reg [W_ADDR-1:0] d_jump_target,
|
||||||
output wire [W_ADDR-1:0] d_pc, // FIXME only added for riscv-formal
|
output wire [W_ADDR-1:0] d_pc,
|
||||||
|
|
||||||
output wire d_stall,
|
output wire d_stall,
|
||||||
input wire x_stall,
|
input wire x_stall,
|
||||||
|
@ -38,29 +38,26 @@ module hazard3_decode #(
|
||||||
input wire f_jump_now,
|
input wire f_jump_now,
|
||||||
input wire [W_ADDR-1:0] f_jump_target,
|
input wire [W_ADDR-1:0] f_jump_target,
|
||||||
|
|
||||||
output reg [W_REGADDR-1:0] d_rs1, // combinatorial
|
output reg [W_DATA-1:0] d_imm,
|
||||||
output reg [W_REGADDR-1:0] d_rs2, // combinatorial
|
output reg [W_REGADDR-1:0] d_rs1,
|
||||||
|
output reg [W_REGADDR-1:0] d_rs2,
|
||||||
output reg [W_DATA-1:0] dx_imm,
|
output reg [W_REGADDR-1:0] d_rd,
|
||||||
output reg [W_REGADDR-1:0] dx_rs1,
|
output reg [W_ALUSRC-1:0] d_alusrc_a,
|
||||||
output reg [W_REGADDR-1:0] dx_rs2,
|
output reg [W_ALUSRC-1:0] d_alusrc_b,
|
||||||
output reg [W_REGADDR-1:0] dx_rd,
|
output reg [W_ALUOP-1:0] d_aluop,
|
||||||
output reg [W_ALUSRC-1:0] dx_alusrc_a,
|
output reg [W_MEMOP-1:0] d_memop,
|
||||||
output reg [W_ALUSRC-1:0] dx_alusrc_b,
|
output reg [W_MULOP-1:0] d_mulop,
|
||||||
output reg [W_ALUOP-1:0] dx_aluop,
|
output reg d_csr_ren,
|
||||||
output reg [W_MEMOP-1:0] dx_memop,
|
output reg d_csr_wen,
|
||||||
output reg [W_MULOP-1:0] dx_mulop,
|
output reg [1:0] d_csr_wtype,
|
||||||
output reg dx_csr_ren,
|
output reg d_csr_w_imm,
|
||||||
output reg dx_csr_wen,
|
output reg [W_BCOND-1:0] d_branchcond,
|
||||||
output reg [1:0] dx_csr_wtype,
|
output reg [W_ADDR-1:0] d_jump_target,
|
||||||
output reg dx_csr_w_imm,
|
output reg d_jump_is_regoffs,
|
||||||
output reg [W_BCOND-1:0] dx_branchcond,
|
output reg d_result_is_linkaddr,
|
||||||
output reg [W_ADDR-1:0] dx_jump_target,
|
output reg [W_ADDR-1:0] d_pc,
|
||||||
output reg dx_jump_is_regoffs,
|
output reg [W_ADDR-1:0] d_mispredict_addr,
|
||||||
output reg dx_result_is_linkaddr,
|
output reg [2:0] d_except
|
||||||
output reg [W_ADDR-1:0] dx_pc,
|
|
||||||
output reg [W_ADDR-1:0] dx_mispredict_addr,
|
|
||||||
output reg [2:0] dx_except
|
|
||||||
);
|
);
|
||||||
|
|
||||||
`include "rv_opcodes.vh"
|
`include "rv_opcodes.vh"
|
||||||
|
@ -165,39 +162,17 @@ always @ (*) begin
|
||||||
|
|
||||||
d_jump_target = pc + d_jump_offs;
|
d_jump_target = pc + d_jump_offs;
|
||||||
|
|
||||||
casez ({d_instr[31], d_instr})
|
casez (d_instr)
|
||||||
{1'b1, RV_BEQ }: d_jump_req = jump_enable;
|
RV_JAL: d_jump_req = jump_enable;
|
||||||
{1'b1, RV_BNE }: d_jump_req = jump_enable;
|
|
||||||
{1'b1, RV_BLT }: d_jump_req = jump_enable;
|
|
||||||
{1'b1, RV_BGE }: d_jump_req = jump_enable;
|
|
||||||
{1'b1, RV_BLTU}: d_jump_req = jump_enable;
|
|
||||||
{1'b1, RV_BGEU}: d_jump_req = jump_enable;
|
|
||||||
{1'bz, RV_JAL }: d_jump_req = jump_enable;
|
|
||||||
default: d_jump_req = 1'b0;
|
default: d_jump_req = 1'b0;
|
||||||
endcase
|
endcase
|
||||||
|
|
||||||
|
d_mispredict_addr = pc_next;
|
||||||
end
|
end
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Decode X controls
|
// Decode X controls
|
||||||
|
|
||||||
// Combinatorials:
|
|
||||||
reg [W_REGADDR-1:0] d_rd;
|
|
||||||
reg [W_DATA-1:0] d_imm;
|
|
||||||
reg [W_DATA-1:0] d_branchoffs;
|
|
||||||
reg [W_ALUSRC-1:0] d_alusrc_a;
|
|
||||||
reg [W_ALUSRC-1:0] d_alusrc_b;
|
|
||||||
reg [W_ALUOP-1:0] d_aluop;
|
|
||||||
reg [W_MEMOP-1:0] d_memop;
|
|
||||||
reg [W_MULOP-1:0] d_mulop;
|
|
||||||
reg [W_BCOND-1:0] d_branchcond;
|
|
||||||
reg d_jump_is_regoffs;
|
|
||||||
reg d_result_is_linkaddr;
|
|
||||||
reg d_csr_ren;
|
|
||||||
reg d_csr_wen;
|
|
||||||
reg [1:0] d_csr_wtype;
|
|
||||||
reg d_csr_w_imm;
|
|
||||||
reg [W_EXCEPT-1:0] d_except;
|
|
||||||
|
|
||||||
localparam X0 = {W_REGADDR{1'b0}};
|
localparam X0 = {W_REGADDR{1'b0}};
|
||||||
|
|
||||||
always @ (*) begin
|
always @ (*) begin
|
||||||
|
@ -206,7 +181,6 @@ always @ (*) begin
|
||||||
d_rs2 = d_instr[24:20];
|
d_rs2 = d_instr[24:20];
|
||||||
d_rd = d_instr[11: 7];
|
d_rd = d_instr[11: 7];
|
||||||
d_imm = d_imm_i;
|
d_imm = d_imm_i;
|
||||||
d_branchoffs = d_imm_i;
|
|
||||||
d_alusrc_a = ALUSRCA_RS1;
|
d_alusrc_a = ALUSRCA_RS1;
|
||||||
d_alusrc_b = ALUSRCB_RS2;
|
d_alusrc_b = ALUSRCB_RS2;
|
||||||
d_aluop = ALUOP_ADD;
|
d_aluop = ALUOP_ADD;
|
||||||
|
@ -281,87 +255,20 @@ always @ (*) begin
|
||||||
RV_MRET: if (HAVE_CSR) begin d_except = EXCEPT_MRET; d_rs2 = X0; d_rs1 = X0; d_rd = X0; end else begin d_invalid_32bit = 1'b1; end
|
RV_MRET: if (HAVE_CSR) begin d_except = EXCEPT_MRET; d_rs2 = X0; d_rs1 = X0; d_rd = X0; end else begin d_invalid_32bit = 1'b1; end
|
||||||
default: begin d_invalid_32bit = 1'b1; end
|
default: begin d_invalid_32bit = 1'b1; end
|
||||||
endcase
|
endcase
|
||||||
end
|
|
||||||
|
|
||||||
|
if (d_invalid || d_starved) begin
|
||||||
always @ (posedge clk or negedge rst_n) begin
|
d_rs1 = {W_REGADDR{1'b0}};
|
||||||
if (!rst_n) begin
|
d_rs2 = {W_REGADDR{1'b0}};
|
||||||
{dx_rs1, dx_rs2, dx_rd} <= {(3 * W_REGADDR){1'b0}};
|
d_rd = {W_REGADDR{1'b0}};
|
||||||
dx_alusrc_a <= ALUSRCA_RS1;
|
d_memop = MEMOP_NONE;
|
||||||
dx_alusrc_b <= ALUSRCB_RS2;
|
d_branchcond = BCOND_NEVER;
|
||||||
dx_aluop <= ALUOP_ADD;
|
d_csr_ren = 1'b0;
|
||||||
dx_memop <= MEMOP_NONE;
|
d_csr_wen = 1'b0;
|
||||||
dx_mulop <= M_OP_MUL;
|
|
||||||
dx_csr_ren <= 1'b0;
|
|
||||||
dx_csr_wen <= 1'b0;
|
|
||||||
dx_csr_wtype <= CSR_WTYPE_W;
|
|
||||||
dx_csr_w_imm <= 1'b0;
|
|
||||||
dx_branchcond <= BCOND_NEVER;
|
|
||||||
dx_jump_is_regoffs <= 1'b0;
|
|
||||||
dx_result_is_linkaddr <= 1'b0;
|
|
||||||
dx_except <= EXCEPT_NONE;
|
|
||||||
end else if (flush_d_x || (d_stall && !x_stall)) begin
|
|
||||||
// Bubble insertion
|
|
||||||
dx_branchcond <= BCOND_NEVER;
|
|
||||||
dx_memop <= MEMOP_NONE;
|
|
||||||
dx_rd <= 5'h0;
|
|
||||||
dx_except <= EXCEPT_NONE;
|
|
||||||
dx_csr_ren <= 1'b0;
|
|
||||||
dx_csr_wen <= 1'b0;
|
|
||||||
// Don't start a multiply in a pipe bubble
|
|
||||||
if (EXTENSION_M)
|
if (EXTENSION_M)
|
||||||
dx_aluop <= ALUOP_ADD;
|
d_aluop = ALUOP_ADD;
|
||||||
// Also need to clear rs1, rs2, due to a nasty sequence of events:
|
|
||||||
// Suppose we have a load, followed by a dependent branch, which is predicted taken
|
|
||||||
// - branch will stall in D until AHB master becomes free
|
|
||||||
// - on next cycle, prediction causes jump, and bubble is in X
|
|
||||||
// - if X gets branch's rs1, rs2, it will cause spurious RAW stall
|
|
||||||
// - on next cycle, branch will not progress into X due to RAW stall, but *will* be replaced in D due to jump
|
|
||||||
// - branch mispredict now cannot be corrected
|
|
||||||
dx_rs1 <= 5'h0;
|
|
||||||
dx_rs2 <= 5'h0;
|
|
||||||
end else if (!x_stall) begin
|
|
||||||
// These ones can have side effects
|
|
||||||
dx_rs1 <= d_invalid ? {W_REGADDR{1'b0}} : d_rs1;
|
|
||||||
dx_rs2 <= d_invalid ? {W_REGADDR{1'b0}} : d_rs2;
|
|
||||||
dx_rd <= d_invalid ? {W_REGADDR{1'b0}} : d_rd;
|
|
||||||
dx_memop <= d_invalid ? MEMOP_NONE : d_memop;
|
|
||||||
dx_branchcond <= d_invalid ? BCOND_NEVER : d_branchcond;
|
|
||||||
dx_csr_ren <= d_invalid ? 1'b0 : d_csr_ren;
|
|
||||||
dx_csr_wen <= d_invalid ? 1'b0 : d_csr_wen;
|
|
||||||
dx_except <= d_invalid ? EXCEPT_INSTR_ILLEGAL : d_except;
|
|
||||||
dx_aluop <= d_invalid && EXTENSION_M ? ALUOP_ADD : d_aluop;
|
|
||||||
|
|
||||||
// These can't
|
if (d_invalid && !d_starved)
|
||||||
dx_alusrc_a <= d_alusrc_a;
|
d_except = EXCEPT_INSTR_ILLEGAL;
|
||||||
dx_alusrc_b <= d_alusrc_b;
|
|
||||||
dx_mulop <= d_mulop;
|
|
||||||
dx_jump_is_regoffs <= d_jump_is_regoffs;
|
|
||||||
dx_result_is_linkaddr <= d_result_is_linkaddr;
|
|
||||||
dx_csr_wtype <= d_csr_wtype;
|
|
||||||
dx_csr_w_imm <= d_csr_w_imm;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
// No reset required on these; will be masked by the resettable pipeline controls until they're valid
|
|
||||||
always @ (posedge clk) begin
|
|
||||||
if (!x_stall) begin
|
|
||||||
dx_imm <= d_imm;
|
|
||||||
dx_jump_target <= d_jump_target;
|
|
||||||
dx_mispredict_addr <= pc_next;
|
|
||||||
dx_pc <= pc;
|
|
||||||
end
|
|
||||||
if (flush_d_x) begin
|
|
||||||
// The target of a late jump must be propagated *immediately* to X PC, as
|
|
||||||
// mepc may sample X PC at any time due to IRQ, and must not capture
|
|
||||||
// misprediction.
|
|
||||||
// Also required for flush while X stalled (e.g. if a muldiv enters X while
|
|
||||||
// a 1 cycle bus stall holds off the jump request in M)
|
|
||||||
dx_pc <= f_jump_target;
|
|
||||||
`ifdef FORMAL
|
|
||||||
// This should only be caused by late jumps
|
|
||||||
assert(f_jump_now);
|
|
||||||
`endif
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,17 @@ module hazard3_frontend #(
|
||||||
output reg [1:0] cir_vld, // number of valid halfwords in CIR
|
output reg [1:0] cir_vld, // number of valid halfwords in CIR
|
||||||
input wire [1:0] cir_use, // number of halfwords D intends to consume
|
input wire [1:0] cir_use, // number of halfwords D intends to consume
|
||||||
// *may* be a function of hready
|
// *may* be a function of hready
|
||||||
input wire cir_lock // Lock-in current contents and level of CIR.
|
input wire cir_lock,// Lock-in current contents and level of CIR.
|
||||||
// Assert simultaneously with a jump request,
|
// Assert simultaneously with a jump request,
|
||||||
// if decode is going to stall. This stops the CIR
|
// if decode is going to stall. This stops the CIR
|
||||||
// from being trashed by incoming fetch data;
|
// from being trashed by incoming fetch data;
|
||||||
// jump instructions have other side effects besides jumping!
|
// jump instructions have other side effects besides jumping!
|
||||||
|
|
||||||
|
// Provide the rs1/rs2 register numbers which will be in CIR on the next
|
||||||
|
// cycle. These go straight to the register file read ports.
|
||||||
|
output wire [4:0] next_regs_rs1,
|
||||||
|
output wire [4:0] next_regs_rs2,
|
||||||
|
output wire next_regs_vld
|
||||||
);
|
);
|
||||||
|
|
||||||
`undef ASSERT
|
`undef ASSERT
|
||||||
|
@ -50,19 +56,12 @@ module hazard3_frontend #(
|
||||||
`define ASSERT(x)
|
`define ASSERT(x)
|
||||||
`endif
|
`endif
|
||||||
|
|
||||||
// ISIM doesn't support some of this:
|
|
||||||
// //synthesis translate_off
|
|
||||||
// initial if (W_DATA != 32) begin $error("Frontend requires 32-bit databus"); end
|
|
||||||
// initial if ((1 << $clog2(FIFO_DEPTH)) != FIFO_DEPTH) begin $error("Frontend FIFO depth must be power of 2"); end
|
|
||||||
// initial if (~|FIFO_DEPTH) begin $error("Frontend FIFO depth must be > 0"); end
|
|
||||||
// //synthesis translate_on
|
|
||||||
|
|
||||||
localparam W_BUNDLE = W_DATA / 2;
|
localparam W_BUNDLE = W_DATA / 2;
|
||||||
parameter W_FIFO_LEVEL = $clog2(FIFO_DEPTH + 1);
|
parameter W_FIFO_LEVEL = $clog2(FIFO_DEPTH + 1);
|
||||||
|
|
||||||
// ============================================================================
|
// ----------------------------------------------------------------------------
|
||||||
// Fetch Queue (FIFO)
|
// Fetch Queue (FIFO)
|
||||||
// ============================================================================
|
//
|
||||||
// This is a little different from either a normal sync fifo or sync fwft fifo
|
// This is a little different from either a normal sync fifo or sync fwft fifo
|
||||||
// so it's worth implementing from scratch
|
// so it's worth implementing from scratch
|
||||||
|
|
||||||
|
@ -105,9 +104,8 @@ always @ (posedge clk) begin: fifo_data_shift
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
// ============================================================================
|
// ----------------------------------------------------------------------------
|
||||||
// Fetch Request + State Logic
|
// Fetch Request + State Logic
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// Keep track of some useful state of the memory interface
|
// Keep track of some useful state of the memory interface
|
||||||
|
|
||||||
|
@ -230,10 +228,8 @@ end
|
||||||
|
|
||||||
assign jump_target_rdy = !mem_addr_hold;
|
assign jump_target_rdy = !mem_addr_hold;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
// ============================================================================
|
|
||||||
// Instruction assembly yard
|
// Instruction assembly yard
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
// buf_level is the number of valid halfwords in {hwbuf, cir}.
|
// buf_level is the number of valid halfwords in {hwbuf, cir}.
|
||||||
// cir_vld and hwbuf_vld are functions of this.
|
// cir_vld and hwbuf_vld are functions of this.
|
||||||
|
@ -298,4 +294,21 @@ end
|
||||||
always @ (posedge clk)
|
always @ (posedge clk)
|
||||||
{hwbuf, cir} <= instr_data_plus_fetch;
|
{hwbuf, cir} <= instr_data_plus_fetch;
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Register number predecode
|
||||||
|
|
||||||
|
wire [31:0] next_instr = instr_data_plus_fetch[31:0];
|
||||||
|
wire next_instr_is_32bit = next_instr[1:0] == 2'b11;
|
||||||
|
|
||||||
|
assign next_regs_vld = next_instr_is_32bit ? buf_level_next[1] : |buf_level_next;
|
||||||
|
|
||||||
|
assign next_regs_rs1 =
|
||||||
|
next_instr_is_32bit ? next_instr[19:15] :
|
||||||
|
next_instr[1:0] == 2'b10 ? next_instr[11:7] : {2'b01, next_instr[9:7]};
|
||||||
|
|
||||||
|
assign next_regs_rs2 =
|
||||||
|
next_instr_is_32bit ? next_instr[24:20] :
|
||||||
|
next_instr[1:0] == 2'b10 ? next_instr[6:2] : {2'b01, next_instr[4:2]};
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 24f0f32b1d91e7bb873ebefb997c324dbe90a325
|
|
@ -1,19 +1,67 @@
|
||||||
TEST = I-ADD-01
|
TEST_ARCH = I
|
||||||
TEST_ARCH = rv32i
|
|
||||||
BIN_ARCH = rv32i
|
BIN_ARCH = rv32i
|
||||||
SIM_EXEC = ../tb_cxxrtl/tb
|
SIM_EXEC = ../tb_cxxrtl/tb
|
||||||
|
|
||||||
CROSS_PREFIX = /opt/riscv/bin/riscv32-unknown-elf-
|
CROSS_PREFIX = /opt/riscv/bin/riscv32-unknown-elf-
|
||||||
|
|
||||||
TEST_BIN_NAME := $(TEST_ARCH)-$(TEST)-on-$(BIN_ARCH)
|
TESTLIST= \
|
||||||
TEST_SRC := riscv-compliance/riscv-test-suite/$(TEST_ARCH)/src/$(TEST).S
|
add-01 \
|
||||||
TEST_VEC := riscv-compliance/riscv-test-suite/$(TEST_ARCH)/references/$(TEST).reference_output
|
addi-01 \
|
||||||
|
and-01 \
|
||||||
|
andi-01 \
|
||||||
|
auipc-01 \
|
||||||
|
beq-01 \
|
||||||
|
bge-01 \
|
||||||
|
bgeu-01 \
|
||||||
|
blt-01 \
|
||||||
|
bltu-01 \
|
||||||
|
bne-01 \
|
||||||
|
fence-01 \
|
||||||
|
jal-01 \
|
||||||
|
jalr-01 \
|
||||||
|
lb-align-01 \
|
||||||
|
lbu-align-01 \
|
||||||
|
lh-align-01 \
|
||||||
|
lhu-align-01 \
|
||||||
|
lui-01 \
|
||||||
|
lw-align-01 \
|
||||||
|
or-01 \
|
||||||
|
ori-01 \
|
||||||
|
sb-align-01 \
|
||||||
|
sh-align-01 \
|
||||||
|
sll-01 \
|
||||||
|
slli-01 \
|
||||||
|
slt-01 \
|
||||||
|
slti-01 \
|
||||||
|
sltiu-01 \
|
||||||
|
sltu-01 \
|
||||||
|
sra-01 \
|
||||||
|
srai-01 \
|
||||||
|
srl-01 \
|
||||||
|
srli-01 \
|
||||||
|
sub-01 \
|
||||||
|
sw-align-01 \
|
||||||
|
xor-01 \
|
||||||
|
xori-01
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all testlist clean $(addprefix test-,$(TESTLIST))
|
||||||
all:
|
all: testlist
|
||||||
|
|
||||||
|
define make-test-target
|
||||||
|
# Turns out variable expansions inside functions don't work like I thought they did. Oh well this will do
|
||||||
|
test-$1:
|
||||||
mkdir -p tmp
|
mkdir -p tmp
|
||||||
$(CROSS_PREFIX)gcc -I include -T memmap.ld -nostartfiles -march=$(BIN_ARCH) $(TEST_SRC) -o tmp/$(TEST_BIN_NAME).elf
|
$(CROSS_PREFIX)gcc -I include -T memmap.ld -nostartfiles -march=$(BIN_ARCH) riscv-arch-test/riscv-test-suite/rv32i_m/$(TEST_ARCH)/src/$1.S -DXLEN=32 -o tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).elf
|
||||||
$(CROSS_PREFIX)objdump -d tmp/$(TEST_BIN_NAME).elf > tmp/$(TEST_BIN_NAME).dis
|
$(CROSS_PREFIX)objdump -h tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).elf > tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).dis
|
||||||
$(CROSS_PREFIX)objcopy -O binary tmp/$(TEST_BIN_NAME).elf tmp/$(TEST_BIN_NAME).bin
|
$(CROSS_PREFIX)objdump -d tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).elf >> tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).dis
|
||||||
$(SIM_EXEC) tmp/$(TEST_BIN_NAME).bin --dump 0x10000 0x10100 | tee tmp/$(TEST_BIN_NAME).log
|
$(CROSS_PREFIX)objcopy -O binary tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).elf tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).bin
|
||||||
./compare_testvec tmp/$(TEST_BIN_NAME).log $(TEST_VEC)
|
$(SIM_EXEC) tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).bin tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).vcd --dump 0x400000 0x401000 > tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).log
|
||||||
|
./compare_testvec tmp/$(TEST_ARCH)-$1-on-$(BIN_ARCH).log riscv-arch-test/riscv-test-suite/rv32i_m/$(TEST_ARCH)/references/$1.reference_output
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach test,$(TESTLIST),$(eval $(call make-test-target,$(test))))
|
||||||
|
|
||||||
|
testlist: $(addprefix test-,$(TESTLIST))
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf tmp/
|
||||||
|
|
|
@ -37,4 +37,4 @@ for i, g in enumerate(gold):
|
||||||
if all_match:
|
if all_match:
|
||||||
print("Test PASSED.")
|
print("Test PASSED.")
|
||||||
else:
|
else:
|
||||||
print("Test FAILED.")
|
sys.exit("Test FAILED.")
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../riscv-arch-test/riscv-test-env/arch_test.h
|
|
@ -1,17 +0,0 @@
|
||||||
#ifndef _COMPLIANCE_IO_H_
|
|
||||||
#define _COMPLIANCE_IO_H_
|
|
||||||
|
|
||||||
#define RVTEST_IO_INIT
|
|
||||||
#define RVTEST_IO_WRITE_STR(_SP, _STR)
|
|
||||||
#define RVTEST_IO_CHECK()
|
|
||||||
|
|
||||||
|
|
||||||
// Put this info into a label name so that it can be seen in the disassembly (holy hack batman)
|
|
||||||
#define LABEL_ASSERT_(reg, val, line) assert_ ## reg ## _ ## val ## _l ## line:
|
|
||||||
#define LABEL_ASSERT(reg, val, line) LABEL_ASSERT_(reg, val, line)
|
|
||||||
|
|
||||||
#define RVTEST_IO_ASSERT_GPR_EQ(_SP, _R, _I) LABEL_ASSERT(_R, xxx, __LINE__) nop
|
|
||||||
#define RVTEST_IO_ASSERT_SFPR_EQ(_F, _R, _I)
|
|
||||||
#define RVTEST_IO_ASSERT_DFPR_EQ(_D, _R, _I)
|
|
||||||
|
|
||||||
#endif // _COMPLIANCE_IO_H_
|
|
|
@ -1,30 +0,0 @@
|
||||||
#ifndef _COMPLIANCE_TEST_H_
|
|
||||||
#define _COMPLIANCE_TEST_H_
|
|
||||||
|
|
||||||
#define RV_COMPLIANCE_RV32M
|
|
||||||
|
|
||||||
#define RV_COMPLIANCE_CODE_BEGIN
|
|
||||||
|
|
||||||
#define RV_COMPLIANCE_CODE_END
|
|
||||||
|
|
||||||
#define MM_IO_EXIT 0x80000008
|
|
||||||
|
|
||||||
.macro RV_COMPLIANCE_HALT
|
|
||||||
.option push
|
|
||||||
.option norelax
|
|
||||||
_write_io_exit:
|
|
||||||
li a0, MM_IO_EXIT
|
|
||||||
sw zero, 0(a0)
|
|
||||||
// Note we should never reach this next instruction (assuming the
|
|
||||||
// processor is working correctly!)
|
|
||||||
_end_of_test:
|
|
||||||
j _end_of_test
|
|
||||||
.option pop
|
|
||||||
.endm
|
|
||||||
|
|
||||||
#define RV_COMPLIANCE_DATA_BEGIN .section .testdata, "a"
|
|
||||||
|
|
||||||
#define RV_COMPLIANCE_DATA_END
|
|
||||||
|
|
||||||
|
|
||||||
#endif // _COMPLIANCE_TEST_H_
|
|
|
@ -0,0 +1 @@
|
||||||
|
../riscv-arch-test/riscv-test-env/encoding.h
|
|
@ -0,0 +1,102 @@
|
||||||
|
#ifndef _COMPLIANCE_MODEL_H
|
||||||
|
#define _COMPLIANCE_MODEL_H
|
||||||
|
|
||||||
|
// Modified version of riscv-arch-test/riscv-target/example-target/model_test.h
|
||||||
|
|
||||||
|
#define IO_BASE 0x80000000
|
||||||
|
#define IO_PRINT_CHAR (IO_BASE + 0x0)
|
||||||
|
#define IO_PRINT_U32 (IO_BASE + 0x4)
|
||||||
|
#define IO_EXIT (IO_BASE + 0x8)
|
||||||
|
|
||||||
|
#define RVMODEL_DATA_SECTION \
|
||||||
|
.pushsection .testdata,"aw",@progbits; \
|
||||||
|
.align 8; .global tohost; tohost: .dword 0; \
|
||||||
|
.align 8; .global fromhost; fromhost: .dword 0; \
|
||||||
|
.popsection; \
|
||||||
|
.align 8; .global begin_regstate; begin_regstate: \
|
||||||
|
.word 128; \
|
||||||
|
.align 8; .global end_regstate; end_regstate: \
|
||||||
|
.word 4;
|
||||||
|
|
||||||
|
|
||||||
|
#define RVMODEL_HALT ; \
|
||||||
|
li a0, IO_EXIT ; \
|
||||||
|
li a1, 0 ; \
|
||||||
|
sw a1, (a0) ; \
|
||||||
|
1: j 1b \
|
||||||
|
|
||||||
|
//TODO: declare the start of your signature region here. Nothing else to be used here.
|
||||||
|
// The .align 4 ensures that the signature ends at a 16-byte boundary
|
||||||
|
#define RVMODEL_DATA_BEGIN \
|
||||||
|
.section .testdata, "aw"; \
|
||||||
|
.align 4; .global begin_signature; begin_signature:
|
||||||
|
|
||||||
|
//TODO: declare the end of the signature region here. Add other target specific contents here.
|
||||||
|
#define RVMODEL_DATA_END \
|
||||||
|
.align 4; .global end_signature; end_signature: \
|
||||||
|
RVMODEL_DATA_SECTION
|
||||||
|
|
||||||
|
|
||||||
|
#define RVMODEL_BOOT
|
||||||
|
|
||||||
|
// _SP = (volatile register)
|
||||||
|
//TODO: Macro to output a string to IO
|
||||||
|
#define LOCAL_IO_WRITE_STR(_STR) RVMODEL_IO_WRITE_STR(x31, _STR)
|
||||||
|
|
||||||
|
// Shut up
|
||||||
|
#define RVMODEL_IO_WRITE_STR(_STR)
|
||||||
|
|
||||||
|
// #define RVMODEL_IO_WRITE_STR(_SP, _STR) \
|
||||||
|
// .section .data.string; \
|
||||||
|
// 20001: \
|
||||||
|
// .string _STR; \
|
||||||
|
// .section .text.init; \
|
||||||
|
// la a0, 20001b; \
|
||||||
|
// jal FN_WriteStr;
|
||||||
|
|
||||||
|
#define RSIZE 4
|
||||||
|
// _SP = (volatile register)
|
||||||
|
#define LOCAL_IO_PUSH(_SP) \
|
||||||
|
la _SP, begin_regstate; \
|
||||||
|
sw ra, (1*RSIZE)(_SP); \
|
||||||
|
sw t0, (2*RSIZE)(_SP); \
|
||||||
|
sw t1, (3*RSIZE)(_SP); \
|
||||||
|
sw t2, (4*RSIZE)(_SP); \
|
||||||
|
sw t3, (5*RSIZE)(_SP); \
|
||||||
|
sw t4, (6*RSIZE)(_SP); \
|
||||||
|
sw s0, (7*RSIZE)(_SP); \
|
||||||
|
sw a0, (8*RSIZE)(_SP);
|
||||||
|
|
||||||
|
// _SP = (volatile register)
|
||||||
|
#define LOCAL_IO_POP(_SP) \
|
||||||
|
la _SP, begin_regstate; \
|
||||||
|
lw ra, (1*RSIZE)(_SP); \
|
||||||
|
lw t0, (2*RSIZE)(_SP); \
|
||||||
|
lw t1, (3*RSIZE)(_SP); \
|
||||||
|
lw t2, (4*RSIZE)(_SP); \
|
||||||
|
lw t3, (5*RSIZE)(_SP); \
|
||||||
|
lw t4, (6*RSIZE)(_SP); \
|
||||||
|
lw s0, (7*RSIZE)(_SP); \
|
||||||
|
lw a0, (8*RSIZE)(_SP);
|
||||||
|
|
||||||
|
#define RVMODEL_IO_ASSERT_GPR_EQ(_SP, _R, _I)
|
||||||
|
|
||||||
|
//RVTEST_IO_ASSERT_SFPR_EQ
|
||||||
|
#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I)
|
||||||
|
//RVTEST_IO_ASSERT_DFPR_EQ
|
||||||
|
#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I)
|
||||||
|
|
||||||
|
// TODO: specify the routine for setting machine software interrupt
|
||||||
|
#define RVMODEL_SET_MSW_INT
|
||||||
|
|
||||||
|
// TODO: specify the routine for clearing machine software interrupt
|
||||||
|
#define RVMODEL_CLEAR_MSW_INT
|
||||||
|
|
||||||
|
// TODO: specify the routine for clearing machine timer interrupt
|
||||||
|
#define RVMODEL_CLEAR_MTIMER_INT
|
||||||
|
|
||||||
|
// TODO: specify the routine for clearing machine external interrupt
|
||||||
|
#define RVMODEL_CLEAR_MEXT_INT
|
||||||
|
|
||||||
|
#endif // _COMPLIANCE_MODEL_H
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
../riscv-compliance/riscv-test-env/riscv_test_macros.h
|
|
|
@ -1,6 +1,6 @@
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
RAM (wx) : ORIGIN = 0x0, LENGTH = 64k
|
RAM (wx) : ORIGIN = 0x0, LENGTH = 4M
|
||||||
RESULT (w) : ORIGIN = ORIGIN(RAM) + LENGTH(RAM), LENGTH = 64k
|
RESULT (w) : ORIGIN = ORIGIN(RAM) + LENGTH(RAM), LENGTH = 64k
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,9 +11,6 @@ ENTRY(_start)
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
.text : {
|
.text : {
|
||||||
/* Padding in place of vector table (by default CPU reset vector points to
|
|
||||||
immediately after vector table */
|
|
||||||
. = ORIGIN(RAM) + 0xc0;
|
|
||||||
PROVIDE (_start = .);
|
PROVIDE (_start = .);
|
||||||
*(.text*)
|
*(.text*)
|
||||||
. = ALIGN(4);
|
. = ALIGN(4);
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit b436dd0939c968f2c3da86bb9b63bb2dfe03b134
|
Loading…
Reference in New Issue