1394 lines
51 KiB
Verilog
1394 lines
51 KiB
Verilog
module riscv_core #(
|
|
parameter SUPPORT_MUL = 1,
|
|
parameter SUPPORT_DIV = 1,
|
|
parameter SUPPORT_CSR = 1,
|
|
parameter SUPPORT_TRAP_LSU_ALIGN = 1,
|
|
parameter SUPPORT_MTVEC = 0,
|
|
parameter SUPPORT_MTVAL = 0,
|
|
parameter SUPPORT_MIP_MIE = 0,
|
|
parameter SUPPORT_MSCRATCH = 0,
|
|
parameter SUPPORT_MCYCLE = 1,
|
|
parameter SUPPORT_MTIMECMP = 0,
|
|
parameter SUPPORT_TRAP_INVALID_OPC = 1,
|
|
parameter SUPPORT_BRAM_REGFILE = 0,
|
|
parameter ISR_VECTOR = 32'h00000010
|
|
) (
|
|
input clk_i
|
|
, input rst_i
|
|
// External interrupt (M_EXT)
|
|
, input intr_i
|
|
// Initial boot address
|
|
, input [31:0] reset_vector_i
|
|
// MHARTID value
|
|
, input [31:0] cpu_id_i
|
|
// Instruction Fetch
|
|
, output mem_i_rd_o,
|
|
output [31:0] mem_i_pc_o,
|
|
input mem_i_accept_i,
|
|
input mem_i_valid_i,
|
|
input [31:0] mem_i_inst_i
|
|
// Instruction fetch: Unused on this core
|
|
, output mem_i_flush_o,
|
|
output mem_i_invalidate_o
|
|
// Instruction fetch: Unused (tie low)
|
|
, input mem_i_error_i
|
|
// Data Access
|
|
, output [31:0] mem_d_addr_o,
|
|
output [31:0] mem_d_data_wr_o,
|
|
output mem_d_rd_o,
|
|
output [ 3:0] mem_d_wr_o,
|
|
input [31:0] mem_d_data_rd_i,
|
|
input mem_d_accept_i,
|
|
input mem_d_ack_i
|
|
// Instruction fetch: Unused on this core
|
|
, output mem_d_cacheable_o,
|
|
output [10:0] mem_d_req_tag_o,
|
|
output mem_d_invalidate_o,
|
|
output mem_d_writeback_o,
|
|
output mem_d_flush_o
|
|
// Data Access: Unused (tie low)
|
|
, input mem_d_error_i,
|
|
input [10:0] mem_d_resp_tag_i
|
|
);
|
|
localparam PC_W = 32;
|
|
localparam PC_PAD_W = 0;
|
|
localparam PC_EXT_W = 0;
|
|
localparam ADDR_W = 32;
|
|
localparam ADDR_PAD_W = 0;
|
|
localparam STATE_W = 3;
|
|
localparam STATE_RESET = 0;
|
|
localparam STATE_FETCH_WB = 1;
|
|
localparam STATE_EXEC = 2;
|
|
localparam STATE_MEM = 3;
|
|
localparam STATE_DECODE = 4; // Only if SUPPORT_BRAM_REGFILE = 1
|
|
reg [STATE_W-1:0] state_q;
|
|
reg [ PC_W-1:0] pc_q;
|
|
reg [ 4:0] rd_q;
|
|
reg rd_wr_en_q;
|
|
reg [ 31:0] alu_a_q;
|
|
reg [ 31:0] alu_b_q;
|
|
reg [ 3:0] alu_func_q;
|
|
wire [ 31:0] csr_data_w;
|
|
reg invalid_inst_r;
|
|
wire [ 4:0] rd_w;
|
|
wire [ 4:0] rs1_w;
|
|
wire [ 4:0] rs2_w;
|
|
wire [ 31:0] rs1_val_w;
|
|
wire [ 31:0] rs2_val_w;
|
|
wire [ 31:0] opcode_w;
|
|
wire opcode_valid_w;
|
|
wire opcode_fetch_w = mem_i_rd_o & mem_i_accept_i;
|
|
wire exception_w;
|
|
wire [ 5:0] exception_type_w;
|
|
wire [ 31:0] exception_target_w;
|
|
wire [ 31:0] csr_mepc_w;
|
|
reg [ 31:0] load_result_r;
|
|
wire rd_writeen_w;
|
|
wire [ 31:0] rd_val_w;
|
|
wire mem_misaligned_w;
|
|
reg [ ADDR_W-1:0] mem_addr_q;
|
|
reg [ 31:0] mem_data_q;
|
|
reg [ 3:0] mem_wr_q;
|
|
reg mem_rd_q;
|
|
reg [ 1:0] load_offset_q;
|
|
reg load_signed_q;
|
|
reg load_byte_q;
|
|
reg load_half_q;
|
|
wire enable_w = 1'b1;
|
|
wire [ 31:0] muldiv_result_w;
|
|
wire muldiv_ready_w;
|
|
wire muldiv_inst_w;
|
|
uriscv_alu alu (
|
|
// ALU operation select
|
|
.op_i(alu_func_q),
|
|
// Operands
|
|
.a_i (alu_a_q),
|
|
.b_i (alu_b_q),
|
|
// Result
|
|
.p_o (rd_val_w)
|
|
);
|
|
reg [31:0] reg_file[0:31];
|
|
always @(posedge clk_i) if (rd_writeen_w) reg_file[rd_q] <= rd_val_w;
|
|
wire [31:0] rs1_val_gpr_w = reg_file[mem_i_inst_i[19:15]];
|
|
wire [31:0] rs2_val_gpr_w = reg_file[mem_i_inst_i[24:20]];
|
|
reg [31:0] rs1_val_gpr_q;
|
|
reg [31:0] rs2_val_gpr_q;
|
|
always @(posedge clk_i) begin
|
|
rs1_val_gpr_q <= rs1_val_gpr_w;
|
|
rs2_val_gpr_q <= rs2_val_gpr_w;
|
|
end
|
|
assign rs1_val_w = SUPPORT_BRAM_REGFILE ? rs1_val_gpr_q : rs1_val_gpr_w;
|
|
assign rs2_val_w = SUPPORT_BRAM_REGFILE ? rs2_val_gpr_q : rs2_val_gpr_w;
|
|
assign rd_writeen_w = rd_wr_en_q & (state_q == STATE_FETCH_WB);
|
|
reg [STATE_W-1:0] next_state_r;
|
|
always @* begin
|
|
next_state_r = state_q;
|
|
case (state_q)
|
|
// RESET - First cycle after reset
|
|
STATE_RESET: begin
|
|
next_state_r = STATE_FETCH_WB;
|
|
end
|
|
// FETCH_WB - Writeback / Fetch next isn
|
|
STATE_FETCH_WB: begin
|
|
if (opcode_fetch_w) next_state_r = SUPPORT_BRAM_REGFILE ? STATE_DECODE : STATE_EXEC;
|
|
end
|
|
// DECODE - Used to access register file if SUPPORT_BRAM_REGFILE=1
|
|
STATE_DECODE: begin
|
|
if (mem_i_valid_i) next_state_r = STATE_EXEC;
|
|
end
|
|
// EXEC - Execute instruction (when ready)
|
|
STATE_EXEC: begin
|
|
// Instruction ready
|
|
if (opcode_valid_w) begin
|
|
if (exception_w) next_state_r = STATE_FETCH_WB;
|
|
else if (type_load_w || type_store_w) next_state_r = STATE_MEM;
|
|
// Multiplication / division - stay in exec state until result ready
|
|
else
|
|
if (muldiv_inst_w);
|
|
else next_state_r = STATE_FETCH_WB;
|
|
end else if (muldiv_ready_w) next_state_r = STATE_FETCH_WB;
|
|
end
|
|
// MEM - Perform load or store
|
|
STATE_MEM: begin
|
|
// Memory access complete
|
|
if (mem_d_ack_i) next_state_r = STATE_FETCH_WB;
|
|
end
|
|
default: ;
|
|
endcase
|
|
if (!enable_w) next_state_r = STATE_RESET;
|
|
end
|
|
always @(posedge clk_i)
|
|
if (rst_i) state_q <= STATE_RESET;
|
|
else state_q <= next_state_r;
|
|
reg [31:0] opcode_q;
|
|
always @(posedge clk_i)
|
|
if (rst_i) opcode_q <= 32'b0;
|
|
else if (state_q == STATE_DECODE) opcode_q <= mem_i_inst_i;
|
|
reg opcode_valid_q;
|
|
always @(posedge clk_i)
|
|
if (rst_i) opcode_valid_q <= 1'b0;
|
|
else if (state_q == STATE_DECODE) opcode_valid_q <= mem_i_valid_i;
|
|
else opcode_valid_q <= 1'b0;
|
|
assign opcode_w = SUPPORT_BRAM_REGFILE ? opcode_q : mem_i_inst_i;
|
|
assign opcode_valid_w = SUPPORT_BRAM_REGFILE ? opcode_valid_q : mem_i_valid_i;
|
|
assign rs1_w = opcode_w[19:15];
|
|
assign rs2_w = opcode_w[24:20];
|
|
assign rd_w = opcode_w[11:7];
|
|
wire type_rvc_w = (opcode_w[1:0] != 2'b11);
|
|
wire type_load_w = (opcode_w[6:2] == 5'b00000);
|
|
wire type_opimm_w = (opcode_w[6:2] == 5'b00100);
|
|
wire type_auipc_w = (opcode_w[6:2] == 5'b00101);
|
|
wire type_store_w = (opcode_w[6:2] == 5'b01000);
|
|
wire type_op_w = (opcode_w[6:2] == 5'b01100);
|
|
wire type_lui_w = (opcode_w[6:2] == 5'b01101);
|
|
wire type_branch_w = (opcode_w[6:2] == 5'b11000);
|
|
wire type_jalr_w = (opcode_w[6:2] == 5'b11001);
|
|
wire type_jal_w = (opcode_w[6:2] == 5'b11011);
|
|
wire type_system_w = (opcode_w[6:2] == 5'b11100);
|
|
wire type_miscm_w = (opcode_w[6:2] == 5'b00011);
|
|
wire [2:0] func3_w = opcode_w[14:12]; // R, I, S
|
|
wire [6:0] func7_w = opcode_w[31:25]; // R
|
|
wire type_alu_op_w = (type_op_w && (func7_w == 7'b0000000)) ||
|
|
(type_op_w && (func7_w == 7'b0100000));
|
|
wire inst_lb_w = (func3_w == 3'b000);
|
|
wire inst_lh_w = (func3_w == 3'b001);
|
|
wire inst_lbu_w = (func3_w == 3'b100);
|
|
wire inst_lhu_w = (func3_w == 3'b101);
|
|
wire inst_ecall_w = SUPPORT_CSR && type_system_w && (opcode_w[31:7] == 25'h000000);
|
|
wire inst_ebreak_w = SUPPORT_CSR && type_system_w && (opcode_w[31:7] == 25'h002000);
|
|
wire inst_mret_w = SUPPORT_CSR && type_system_w && (opcode_w[31:7] == 25'h604000);
|
|
wire inst_csr_w = SUPPORT_CSR && type_system_w && (func3_w != 3'b000 && func3_w != 3'b100);
|
|
wire mul_inst_w = SUPPORT_MUL && type_op_w && (func7_w == 7'b0000001) && ~func3_w[2];
|
|
wire div_inst_w = SUPPORT_DIV && type_op_w && (func7_w == 7'b0000001) && func3_w[2];
|
|
wire inst_mul_w = mul_inst_w && (func3_w == 3'b000);
|
|
wire inst_mulh_w = mul_inst_w && (func3_w == 3'b001);
|
|
wire inst_mulhsu_w = mul_inst_w && (func3_w == 3'b010);
|
|
wire inst_mulhu_w = mul_inst_w && (func3_w == 3'b011);
|
|
wire inst_div_w = div_inst_w && (func3_w == 3'b100);
|
|
wire inst_divu_w = div_inst_w && (func3_w == 3'b101);
|
|
wire inst_rem_w = div_inst_w && (func3_w == 3'b110);
|
|
wire inst_remu_w = div_inst_w && (func3_w == 3'b111);
|
|
wire inst_nop_w = (type_miscm_w && (func3_w == 3'b000)) | // fence
|
|
(type_miscm_w && (func3_w == 3'b001)); // fence.i
|
|
assign muldiv_inst_w = mul_inst_w | div_inst_w;
|
|
reg [31:0] imm20_r;
|
|
reg [31:0] imm12_r;
|
|
always @* begin
|
|
imm20_r = {opcode_w[31:12], 12'b0};
|
|
imm12_r = {{20{opcode_w[31]}}, opcode_w[31:20]};
|
|
end
|
|
reg [ 3:0] alu_func_r;
|
|
reg [31:0] alu_input_a_r;
|
|
reg [31:0] alu_input_b_r;
|
|
reg write_rd_r;
|
|
always @* begin
|
|
alu_func_r = 4'b0000;
|
|
alu_input_a_r = rs1_val_w;
|
|
alu_input_b_r = rs2_val_w;
|
|
write_rd_r = 1'b0;
|
|
case (1'b1)
|
|
type_alu_op_w: begin
|
|
alu_input_a_r = rs1_val_w;
|
|
alu_input_b_r = rs2_val_w;
|
|
end
|
|
type_opimm_w: begin
|
|
alu_input_a_r = rs1_val_w;
|
|
alu_input_b_r = imm12_r;
|
|
end
|
|
type_lui_w: begin
|
|
alu_input_a_r = 32'b0;
|
|
alu_input_b_r = imm20_r;
|
|
end
|
|
type_auipc_w: begin
|
|
alu_input_a_r[PC_W-1:0] = pc_q;
|
|
alu_input_b_r = imm20_r;
|
|
end
|
|
type_jal_w, type_jalr_w: begin
|
|
alu_input_a_r[PC_W-1:0] = pc_q;
|
|
alu_input_b_r = 32'd4;
|
|
end
|
|
default: ;
|
|
endcase
|
|
if (muldiv_inst_w) write_rd_r = 1'b1;
|
|
else if (type_opimm_w || type_alu_op_w) begin
|
|
case (func3_w)
|
|
3'b000: alu_func_r = (type_op_w & opcode_w[30]) ?
|
|
4'b0110: // SUB
|
|
4'b0100; // ADD / ADDI
|
|
3'b001: alu_func_r = 4'b0001; // SLL / SLLI
|
|
3'b010: alu_func_r = 4'b1011; // SLT / SLTI
|
|
3'b011: alu_func_r = 4'b1010; // SLTU / SLTIU
|
|
3'b100: alu_func_r = 4'b1001; // XOR / XORI
|
|
3'b101: alu_func_r = opcode_w[30] ?
|
|
4'b0011: // SRA / SRAI
|
|
4'b0010; // SRL / SRLI
|
|
3'b110: alu_func_r = 4'b1000; // OR / ORI
|
|
3'b111: alu_func_r = 4'b0111; // AND / ANDI
|
|
endcase
|
|
write_rd_r = 1'b1;
|
|
end else if (inst_csr_w) begin
|
|
alu_func_r = 4'b0100;
|
|
alu_input_a_r = 32'b0;
|
|
alu_input_b_r = csr_data_w;
|
|
write_rd_r = 1'b1;
|
|
end else if (type_auipc_w || type_lui_w || type_jalr_w || type_jal_w) begin
|
|
write_rd_r = 1'b1;
|
|
alu_func_r = 4'b0100;
|
|
end else if (type_load_w) write_rd_r = 1'b1;
|
|
end
|
|
always @* begin
|
|
load_result_r = 32'b0;
|
|
if (load_byte_q) begin
|
|
case (load_offset_q[1:0])
|
|
2'h3: load_result_r = {24'b0, mem_d_data_rd_i[31:24]};
|
|
2'h2: load_result_r = {24'b0, mem_d_data_rd_i[23:16]};
|
|
2'h1: load_result_r = {24'b0, mem_d_data_rd_i[15:8]};
|
|
2'h0: load_result_r = {24'b0, mem_d_data_rd_i[7:0]};
|
|
endcase
|
|
if (load_signed_q && load_result_r[7]) load_result_r = {24'hFFFFFF, load_result_r[7:0]};
|
|
end else if (load_half_q) begin
|
|
if (load_offset_q[1]) load_result_r = {16'b0, mem_d_data_rd_i[31:16]};
|
|
else load_result_r = {16'b0, mem_d_data_rd_i[15:0]};
|
|
if (load_signed_q && load_result_r[15]) load_result_r = {16'hFFFF, load_result_r[15:0]};
|
|
end else load_result_r = mem_d_data_rd_i;
|
|
end
|
|
wire branch_w;
|
|
wire [31:0] branch_target_w;
|
|
wire [31:0] pc_ext_w = {{PC_EXT_W{1'b0}}, pc_q};
|
|
uriscv_branch u_branch (
|
|
.pc_i(pc_ext_w),
|
|
.opcode_i(opcode_w),
|
|
.rs1_val_i(rs1_val_w),
|
|
.rs2_val_i(rs2_val_w),
|
|
.branch_o(branch_w),
|
|
.branch_target_o(branch_target_w)
|
|
);
|
|
always @* begin
|
|
invalid_inst_r = SUPPORT_TRAP_INVALID_OPC;
|
|
if ( type_load_w
|
|
| type_opimm_w
|
|
| type_auipc_w
|
|
| type_store_w
|
|
| type_alu_op_w
|
|
| type_lui_w
|
|
| type_branch_w
|
|
| type_jalr_w
|
|
| type_jal_w
|
|
| inst_ecall_w
|
|
| inst_ebreak_w
|
|
| inst_mret_w
|
|
| inst_csr_w
|
|
| inst_nop_w
|
|
| muldiv_inst_w)
|
|
invalid_inst_r = SUPPORT_TRAP_INVALID_OPC && type_rvc_w;
|
|
end
|
|
always @(posedge clk_i)
|
|
if (rst_i) begin
|
|
alu_func_q <= 4'b0000;
|
|
alu_a_q <= 32'h00000000;
|
|
alu_b_q <= 32'h00000000;
|
|
rd_q <= 5'b00000;
|
|
// Reset x0 in-case of RAM
|
|
rd_wr_en_q <= 1'b1;
|
|
end else if ((state_q == STATE_MEM) && mem_d_ack_i) begin
|
|
// Update ALU input with load result
|
|
alu_func_q <= 4'b0000;
|
|
alu_a_q <= load_result_r;
|
|
alu_b_q <= 32'b0;
|
|
end else if (muldiv_ready_w) begin
|
|
// Update ALU input with load result
|
|
alu_func_q <= 4'b0000;
|
|
alu_a_q <= muldiv_result_w;
|
|
alu_b_q <= 32'b0;
|
|
end else if (opcode_valid_w) begin
|
|
// Update ALU input flops
|
|
alu_func_q <= alu_func_r;
|
|
alu_a_q <= alu_input_a_r;
|
|
alu_b_q <= alu_input_b_r;
|
|
// Take exception
|
|
if (exception_w) begin
|
|
// No register writeback
|
|
rd_q <= 5'b0;
|
|
rd_wr_en_q <= 1'b0;
|
|
end // Valid instruction
|
|
else begin
|
|
// Instruction with register writeback
|
|
rd_q <= rd_w;
|
|
rd_wr_en_q <= write_rd_r & (rd_w != 5'b0);
|
|
end
|
|
end else if (state_q == STATE_FETCH_WB) rd_wr_en_q <= 1'b0;
|
|
wire [31:0] boot_vector_w = reset_vector_i;
|
|
always @(posedge clk_i)
|
|
if (rst_i) pc_q <= boot_vector_w[PC_W-1:0];
|
|
else if (state_q == STATE_RESET) pc_q <= boot_vector_w[PC_W-1:0];
|
|
else if (opcode_valid_w) begin
|
|
// Exception / Break / ecall (branch to ISR)
|
|
if (exception_w || inst_ebreak_w || inst_ecall_w) pc_q <= exception_target_w[PC_W-1:0];
|
|
// MRET (branch to EPC)
|
|
else if (inst_mret_w) pc_q <= csr_mepc_w;
|
|
// Branch
|
|
else if (branch_w) pc_q <= branch_target_w[PC_W-1:0];
|
|
else pc_q <= pc_q + 32'd4;
|
|
end
|
|
assign mem_i_rd_o = (state_q == STATE_FETCH_WB);
|
|
assign mem_i_pc_o = pc_ext_w;
|
|
wire mem_rd_w;
|
|
wire [ 3:0] mem_wr_w;
|
|
wire [31:0] mem_addr_w;
|
|
wire [31:0] mem_data_w;
|
|
uriscv_lsu #(
|
|
.SUPPORT_TRAP_LSU_ALIGN(SUPPORT_TRAP_LSU_ALIGN)
|
|
) u_lsu (
|
|
.opcode_i(opcode_w),
|
|
.rs1_val_i(rs1_val_w),
|
|
.rs2_val_i(rs2_val_w),
|
|
.mem_rd_o(mem_rd_w),
|
|
.mem_wr_o(mem_wr_w),
|
|
.mem_addr_o(mem_addr_w),
|
|
.mem_data_o(mem_data_w),
|
|
.mem_misaligned_o(mem_misaligned_w)
|
|
);
|
|
always @(posedge clk_i)
|
|
if (rst_i) begin
|
|
mem_addr_q <= {ADDR_W{1'b0}};
|
|
mem_data_q <= 32'h00000000;
|
|
mem_wr_q <= 4'b0000;
|
|
mem_rd_q <= 1'b0;
|
|
end else if (opcode_valid_w && !exception_w) begin
|
|
mem_addr_q <= {mem_addr_w[ADDR_W-1:2], 2'b0};
|
|
mem_data_q <= mem_data_w;
|
|
mem_wr_q <= mem_wr_w;
|
|
mem_rd_q <= mem_rd_w;
|
|
end else if (mem_d_accept_i) begin
|
|
mem_wr_q <= 4'b0000;
|
|
mem_rd_q <= 1'b0;
|
|
end
|
|
always @(posedge clk_i)
|
|
if (rst_i) begin
|
|
load_signed_q <= 1'b0;
|
|
load_byte_q <= 1'b0;
|
|
load_half_q <= 1'b0;
|
|
load_offset_q <= 2'b0;
|
|
end else if (opcode_valid_w) begin
|
|
load_signed_q <= inst_lh_w | inst_lb_w;
|
|
load_byte_q <= inst_lb_w | inst_lbu_w;
|
|
load_half_q <= inst_lh_w | inst_lhu_w;
|
|
load_offset_q <= mem_addr_w[1:0];
|
|
end
|
|
assign mem_d_addr_o = {{ADDR_PAD_W{1'b0}}, mem_addr_q};
|
|
assign mem_d_data_wr_o = mem_data_q;
|
|
assign mem_d_wr_o = mem_wr_q;
|
|
assign mem_d_rd_o = mem_rd_q;
|
|
uriscv_csr #(
|
|
.SUPPORT_CSR(SUPPORT_CSR),
|
|
.SUPPORT_MCYCLE(SUPPORT_MCYCLE),
|
|
.SUPPORT_MTIMECMP(SUPPORT_MTIMECMP),
|
|
.SUPPORT_MSCRATCH(SUPPORT_MSCRATCH),
|
|
.SUPPORT_MIP_MIE(SUPPORT_MIP_MIE),
|
|
.SUPPORT_MTVEC(SUPPORT_MTVEC),
|
|
.SUPPORT_MTVAL(SUPPORT_MTVAL),
|
|
.SUPPORT_MULDIV(SUPPORT_MUL || SUPPORT_DIV)
|
|
) u_csr (
|
|
.clk_i(clk_i),
|
|
.rst_i(rst_i)
|
|
// Reset vector (only used if SUPPORT_MTVEC=0)
|
|
, .isr_vector_i(reset_vector_i + ISR_VECTOR)
|
|
// HartID
|
|
, .cpu_id_i(cpu_id_i)
|
|
// External interrupt
|
|
, .intr_i(intr_i)
|
|
// Executing instruction
|
|
, .valid_i(opcode_valid_w),
|
|
.opcode_i(opcode_w),
|
|
.pc_i(pc_q),
|
|
.rs1_val_i(rs1_val_w),
|
|
.rs2_val_i(rs2_val_w)
|
|
// CSR read result
|
|
, .csr_rdata_o(csr_data_w)
|
|
// Exception sources
|
|
, .excpn_invalid_inst_i(invalid_inst_r),
|
|
.excpn_lsu_align_i(mem_misaligned_w)
|
|
// Used on memory alignment errors
|
|
, .mem_addr_i(mem_addr_w)
|
|
// CSR registers
|
|
, .csr_mepc_o(csr_mepc_w)
|
|
// Exception entry
|
|
, .exception_o(exception_w),
|
|
.exception_type_o(exception_type_w),
|
|
.exception_pc_o(exception_target_w)
|
|
);
|
|
generate
|
|
if (SUPPORT_MUL != 0 || SUPPORT_DIV != 0) begin
|
|
uriscv_muldiv u_muldiv (
|
|
.clk_i(clk_i),
|
|
.rst_i(rst_i),
|
|
// Operation select
|
|
.valid_i(opcode_valid_w & ~exception_w),
|
|
.inst_mul_i(inst_mul_w),
|
|
.inst_mulh_i(inst_mulh_w),
|
|
.inst_mulhsu_i(inst_mulhsu_w),
|
|
.inst_mulhu_i(inst_mulhu_w),
|
|
.inst_div_i(inst_div_w),
|
|
.inst_divu_i(inst_divu_w),
|
|
.inst_rem_i(inst_rem_w),
|
|
.inst_remu_i(inst_remu_w),
|
|
// Operands
|
|
.operand_ra_i(rs1_val_w),
|
|
.operand_rb_i(rs2_val_w),
|
|
// Result
|
|
.stall_o(),
|
|
.ready_o(muldiv_ready_w),
|
|
.result_o(muldiv_result_w)
|
|
);
|
|
end else begin
|
|
assign muldiv_ready_w = 1'b0;
|
|
assign muldiv_result_w = 32'b0;
|
|
end
|
|
endgenerate
|
|
assign mem_i_flush_o = 1'b0;
|
|
assign mem_i_invalidate_o = 1'b0;
|
|
assign mem_d_flush_o = 1'b0;
|
|
assign mem_d_cacheable_o = 1'b0;
|
|
assign mem_d_req_tag_o = 11'b0;
|
|
assign mem_d_invalidate_o = 1'b0;
|
|
assign mem_d_writeback_o = 1'b0;
|
|
endmodule
|
|
module uriscv_alu (
|
|
// ALU operation select
|
|
input [ 3:0] op_i,
|
|
// Operands
|
|
input [31:0] a_i,
|
|
input [31:0] b_i,
|
|
// Result
|
|
output [31:0] p_o
|
|
);
|
|
reg [ 31:0] result_r;
|
|
reg [31:16] shift_right_fill_r;
|
|
reg [ 31:0] shift_right_1_r;
|
|
reg [ 31:0] shift_right_2_r;
|
|
reg [ 31:0] shift_right_4_r;
|
|
reg [ 31:0] shift_right_8_r;
|
|
reg [ 31:0] shift_left_1_r;
|
|
reg [ 31:0] shift_left_2_r;
|
|
reg [ 31:0] shift_left_4_r;
|
|
reg [ 31:0] shift_left_8_r;
|
|
wire [ 31:0] sub_res_w = a_i - b_i;
|
|
always @* begin
|
|
case (op_i)
|
|
//----------------------------------------------
|
|
// Shift Left
|
|
//----------------------------------------------
|
|
4'b0001: begin
|
|
if (b_i[0] == 1'b1) shift_left_1_r = {a_i[30:0], 1'b0};
|
|
else shift_left_1_r = a_i;
|
|
if (b_i[1] == 1'b1) shift_left_2_r = {shift_left_1_r[29:0], 2'b00};
|
|
else shift_left_2_r = shift_left_1_r;
|
|
if (b_i[2] == 1'b1) shift_left_4_r = {shift_left_2_r[27:0], 4'b0000};
|
|
else shift_left_4_r = shift_left_2_r;
|
|
if (b_i[3] == 1'b1) shift_left_8_r = {shift_left_4_r[23:0], 8'b00000000};
|
|
else shift_left_8_r = shift_left_4_r;
|
|
if (b_i[4] == 1'b1) result_r = {shift_left_8_r[15:0], 16'b0000000000000000};
|
|
else result_r = shift_left_8_r;
|
|
end
|
|
//----------------------------------------------
|
|
// Shift Right
|
|
//----------------------------------------------
|
|
4'b0010, 4'b0011: begin
|
|
// Arithmetic shift? Fill with 1's if MSB set
|
|
if (a_i[31] == 1'b1 && op_i == 4'b0011) shift_right_fill_r = 16'b1111111111111111;
|
|
else shift_right_fill_r = 16'b0000000000000000;
|
|
if (b_i[0] == 1'b1) shift_right_1_r = {shift_right_fill_r[31], a_i[31:1]};
|
|
else shift_right_1_r = a_i;
|
|
if (b_i[1] == 1'b1) shift_right_2_r = {shift_right_fill_r[31:30], shift_right_1_r[31:2]};
|
|
else shift_right_2_r = shift_right_1_r;
|
|
if (b_i[2] == 1'b1) shift_right_4_r = {shift_right_fill_r[31:28], shift_right_2_r[31:4]};
|
|
else shift_right_4_r = shift_right_2_r;
|
|
if (b_i[3] == 1'b1) shift_right_8_r = {shift_right_fill_r[31:24], shift_right_4_r[31:8]};
|
|
else shift_right_8_r = shift_right_4_r;
|
|
if (b_i[4] == 1'b1) result_r = {shift_right_fill_r[31:16], shift_right_8_r[31:16]};
|
|
else result_r = shift_right_8_r;
|
|
end
|
|
//----------------------------------------------
|
|
// Arithmetic
|
|
//----------------------------------------------
|
|
4'b0100: begin
|
|
result_r = (a_i + b_i);
|
|
end
|
|
4'b0110: begin
|
|
result_r = sub_res_w;
|
|
end
|
|
//----------------------------------------------
|
|
// Logical
|
|
//----------------------------------------------
|
|
4'b0111: begin
|
|
result_r = (a_i & b_i);
|
|
end
|
|
4'b1000: begin
|
|
result_r = (a_i | b_i);
|
|
end
|
|
4'b1001: begin
|
|
result_r = (a_i ^ b_i);
|
|
end
|
|
//----------------------------------------------
|
|
// Comparision
|
|
//----------------------------------------------
|
|
4'b1010: begin
|
|
result_r = (a_i < b_i) ? 32'h1 : 32'h0;
|
|
end
|
|
4'b1011: begin
|
|
if (a_i[31] != b_i[31]) result_r = a_i[31] ? 32'h1 : 32'h0;
|
|
else result_r = sub_res_w[31] ? 32'h1 : 32'h0;
|
|
end
|
|
default: begin
|
|
result_r = a_i;
|
|
end
|
|
endcase
|
|
end
|
|
assign p_o = result_r;
|
|
endmodule
|
|
module uriscv_branch (
|
|
input [31:0] pc_i,
|
|
input [31:0] opcode_i,
|
|
input [31:0] rs1_val_i,
|
|
input [31:0] rs2_val_i,
|
|
output branch_o,
|
|
output [31:0] branch_target_o
|
|
);
|
|
function [0:0] less_than_signed;
|
|
input [31:0] x;
|
|
input [31:0] y;
|
|
reg [31:0] v;
|
|
begin
|
|
v = (x - y);
|
|
if (x[31] != y[31]) less_than_signed = x[31];
|
|
else less_than_signed = v[31];
|
|
end
|
|
endfunction
|
|
function [0:0] greater_than_signed;
|
|
input [31:0] x;
|
|
input [31:0] y;
|
|
reg [31:0] v;
|
|
begin
|
|
v = (y - x);
|
|
if (x[31] != y[31]) greater_than_signed = y[31];
|
|
else greater_than_signed = v[31];
|
|
end
|
|
endfunction
|
|
wire type_branch_w = (opcode_i[6:2] == 5'b11000);
|
|
wire type_jalr_w = (opcode_i[6:2] == 5'b11001);
|
|
wire type_jal_w = (opcode_i[6:2] == 5'b11011);
|
|
wire [2:0] func3_w = opcode_i[14:12]; // R, I, S
|
|
wire [6:0] func7_w = opcode_i[31:25]; // R
|
|
wire branch_beq_w = (func3_w == 3'b000);
|
|
wire branch_bne_w = (func3_w == 3'b001);
|
|
wire branch_blt_w = (func3_w == 3'b100);
|
|
wire branch_bge_w = (func3_w == 3'b101);
|
|
wire branch_bltu_w = (func3_w == 3'b110);
|
|
wire branch_bgeu_w = (func3_w == 3'b111);
|
|
reg branch_r;
|
|
reg [31:0] branch_target_r;
|
|
reg [31:0] imm12_r;
|
|
reg [31:0] bimm_r;
|
|
reg [31:0] jimm20_r;
|
|
always @* begin
|
|
branch_r = 1'b0;
|
|
branch_target_r = 32'b0;
|
|
// Opcode decode
|
|
imm12_r = {{20{opcode_i[31]}}, opcode_i[31:20]};
|
|
bimm_r = {{19{opcode_i[31]}}, opcode_i[31], opcode_i[7], opcode_i[30:25], opcode_i[11:8], 1'b0};
|
|
jimm20_r = {
|
|
{12{opcode_i[31]}}, opcode_i[19:12], opcode_i[20], opcode_i[30:25], opcode_i[24:21], 1'b0
|
|
};
|
|
// Default branch target is relative to current PC
|
|
branch_target_r = (pc_i + bimm_r);
|
|
if (type_jal_w) begin
|
|
branch_r = 1'b1;
|
|
branch_target_r = pc_i + jimm20_r;
|
|
end else if (type_jalr_w) begin
|
|
branch_r = 1'b1;
|
|
branch_target_r = rs1_val_i + imm12_r;
|
|
branch_target_r[0] = 1'b0;
|
|
end else if (type_branch_w) begin
|
|
case (1'b1)
|
|
branch_beq_w: // beq
|
|
branch_r = (rs1_val_i == rs2_val_i);
|
|
branch_bne_w: // bne
|
|
branch_r = (rs1_val_i != rs2_val_i);
|
|
branch_blt_w: // blt
|
|
branch_r = less_than_signed(rs1_val_i, rs2_val_i);
|
|
branch_bge_w: // bge
|
|
branch_r = greater_than_signed(rs1_val_i, rs2_val_i) | (rs1_val_i == rs2_val_i);
|
|
branch_bltu_w: // bltu
|
|
branch_r = (rs1_val_i < rs2_val_i);
|
|
branch_bgeu_w: // bgeu
|
|
branch_r = (rs1_val_i >= rs2_val_i);
|
|
default: ;
|
|
endcase
|
|
end
|
|
end
|
|
assign branch_o = branch_r;
|
|
assign branch_target_o = branch_target_r;
|
|
endmodule
|
|
module uriscv_csr #(
|
|
parameter SUPPORT_CSR = 1,
|
|
parameter SUPPORT_MCYCLE = 1,
|
|
parameter SUPPORT_MTIMECMP = 1,
|
|
parameter SUPPORT_MSCRATCH = 1,
|
|
parameter SUPPORT_MIP_MIE = 1,
|
|
parameter SUPPORT_MTVEC = 1,
|
|
parameter SUPPORT_MTVAL = 1,
|
|
parameter SUPPORT_MULDIV = 1
|
|
) (
|
|
input clk_i,
|
|
input rst_i,
|
|
input intr_i,
|
|
input [31:0] isr_vector_i,
|
|
input [31:0] cpu_id_i,
|
|
input valid_i,
|
|
input [31:0] pc_i,
|
|
input [31:0] opcode_i,
|
|
input [31:0] rs1_val_i,
|
|
input [31:0] rs2_val_i,
|
|
output [31:0] csr_rdata_o,
|
|
input excpn_invalid_inst_i,
|
|
input excpn_lsu_align_i,
|
|
input [31:0] mem_addr_i,
|
|
output [31:0] csr_mepc_o,
|
|
output exception_o,
|
|
output [ 5:0] exception_type_o,
|
|
output [31:0] exception_pc_o
|
|
);
|
|
wire take_interrupt_w;
|
|
wire exception_w;
|
|
wire [2:0] func3_w = opcode_i[14:12]; // R, I, S
|
|
wire [4:0] rs1_w = opcode_i[19:15];
|
|
wire [4:0] rs2_w = opcode_i[24:20];
|
|
wire [4:0] rd_w = opcode_i[11:7];
|
|
wire type_system_w = (opcode_i[6:2] == 5'b11100);
|
|
wire type_store_w = (opcode_i[6:2] == 5'b01000);
|
|
wire inst_csr_w = SUPPORT_CSR && type_system_w && (func3_w != 3'b000 && func3_w != 3'b100);
|
|
wire inst_csrrw_w = inst_csr_w && (func3_w == 3'b001);
|
|
wire inst_csrrs_w = inst_csr_w && (func3_w == 3'b010);
|
|
wire inst_csrrc_w = inst_csr_w && (func3_w == 3'b011);
|
|
wire inst_csrrwi_w = inst_csr_w && (func3_w == 3'b101);
|
|
wire inst_csrrsi_w = inst_csr_w && (func3_w == 3'b110);
|
|
wire inst_csrrci_w = inst_csr_w && (func3_w == 3'b111);
|
|
wire inst_ecall_w = SUPPORT_CSR && type_system_w && (opcode_i[31:7] == 25'h000000);
|
|
wire inst_ebreak_w = SUPPORT_CSR && type_system_w && (opcode_i[31:7] == 25'h002000);
|
|
wire inst_mret_w = SUPPORT_CSR && type_system_w && (opcode_i[31:7] == 25'h604000);
|
|
wire [11:0] csr_addr_w = valid_i ? opcode_i[31:20] : 12'b0;
|
|
wire [31:0] csr_data_w = (inst_csrrwi_w || inst_csrrsi_w || inst_csrrci_w) ? {27'b0, rs1_w} : rs1_val_i;
|
|
wire csr_set_w = (valid_i && !exception_w) ? (inst_csrrw_w || inst_csrrs_w || inst_csrrwi_w || inst_csrrsi_w): 1'b0;
|
|
wire csr_clr_w = (valid_i && !exception_w) ? (inst_csrrw_w || inst_csrrc_w || inst_csrrwi_w || inst_csrrci_w): 1'b0;
|
|
reg [31:0] csr_mepc_q;
|
|
reg [31:0] csr_mepc_r;
|
|
reg [31:0] csr_mcause_q;
|
|
reg [31:0] csr_mcause_r;
|
|
reg [31:0] csr_sr_q;
|
|
reg [31:0] csr_sr_r;
|
|
reg [31:0] csr_mcycle_q;
|
|
reg [31:0] csr_mcycle_r;
|
|
reg [31:0] csr_mtimecmp_q;
|
|
reg [31:0] csr_mtimecmp_r;
|
|
reg [31:0] csr_mscratch_q;
|
|
reg [31:0] csr_mscratch_r;
|
|
reg [31:0] csr_mip_q;
|
|
reg [31:0] csr_mip_r;
|
|
reg [31:0] csr_mie_q;
|
|
reg [31:0] csr_mie_r;
|
|
reg [31:0] csr_mtvec_q;
|
|
reg [31:0] csr_mtvec_r;
|
|
reg [31:0] csr_mtval_q;
|
|
reg [31:0] csr_mtval_r;
|
|
always @* begin
|
|
csr_mepc_r = csr_mepc_q;
|
|
csr_mcause_r = csr_mcause_q;
|
|
csr_sr_r = csr_sr_q;
|
|
csr_mcycle_r = csr_mcycle_q + 32'd1;
|
|
csr_mtimecmp_r = csr_mtimecmp_q;
|
|
csr_mscratch_r = csr_mscratch_q;
|
|
csr_mip_r = csr_mip_q;
|
|
csr_mie_r = csr_mie_q;
|
|
csr_mtvec_r = csr_mtvec_q;
|
|
csr_mtval_r = csr_mtval_q;
|
|
// External interrupt
|
|
if (intr_i) csr_mip_r[11] = 1'b1;
|
|
// Timer match - generate IRQ
|
|
if (SUPPORT_MTIMECMP && csr_mcycle_r == csr_mtimecmp_r) csr_mip_r[7] = 1'b1;
|
|
// Execute instruction / exception
|
|
if (valid_i) begin
|
|
// Exception / break / ecall
|
|
if (exception_w || inst_ebreak_w || inst_ecall_w) begin
|
|
// Save interrupt / supervisor state
|
|
csr_sr_r[7] = csr_sr_q[3];
|
|
csr_sr_r[12:11] = 3;
|
|
// Disable interrupts and enter supervisor mode
|
|
csr_sr_r[3] = 1'b0;
|
|
// Save PC of next instruction (not yet executed)
|
|
csr_mepc_r = pc_i;
|
|
// Extra info (badaddr / fault opcode)
|
|
csr_mtval_r = 32'b0;
|
|
// Exception source
|
|
if (excpn_invalid_inst_i) begin
|
|
csr_mcause_r = ((0 << 31) | 2);
|
|
csr_mtval_r = opcode_i;
|
|
end else if (inst_ebreak_w) csr_mcause_r = ((0 << 31) | 3);
|
|
else if (inst_ecall_w) csr_mcause_r = ((0 << 31) | 11);
|
|
else if (excpn_lsu_align_i) begin
|
|
csr_mcause_r = type_store_w ? ((0 << 31) | 6) : ((0 << 31) | 4);
|
|
csr_mtval_r = mem_addr_i;
|
|
end else if (take_interrupt_w) csr_mcause_r = (1 << 31);
|
|
end // MRET
|
|
else if (inst_mret_w) begin
|
|
// Interrupt enable pop
|
|
csr_sr_r[3] = csr_sr_r[7];
|
|
csr_sr_r[7] = 1'b1;
|
|
// This CPU only supports machine mode
|
|
csr_sr_r[12:11] = 3;
|
|
end else begin
|
|
case (csr_addr_w)
|
|
12'h341: begin
|
|
if (csr_set_w && csr_clr_w) csr_mepc_r = csr_data_w;
|
|
else if (csr_set_w) csr_mepc_r = csr_mepc_r | csr_data_w;
|
|
else if (csr_clr_w) csr_mepc_r = csr_mepc_r & ~csr_data_w;
|
|
end
|
|
12'h342: begin
|
|
if (csr_set_w && csr_clr_w) csr_mcause_r = csr_data_w;
|
|
else if (csr_set_w) csr_mcause_r = csr_mcause_r | csr_data_w;
|
|
else if (csr_clr_w) csr_mcause_r = csr_mcause_r & ~csr_data_w;
|
|
end
|
|
12'h300: begin
|
|
if (csr_set_w && csr_clr_w) csr_sr_r = csr_data_w;
|
|
else if (csr_set_w) csr_sr_r = csr_sr_r | csr_data_w;
|
|
else if (csr_clr_w) csr_sr_r = csr_sr_r & ~csr_data_w;
|
|
end
|
|
12'h7c0: begin
|
|
if (SUPPORT_MTIMECMP && csr_set_w && csr_data_w != 32'b0) begin
|
|
csr_mtimecmp_r = csr_data_w;
|
|
// Clear interrupt pending
|
|
csr_mip_r[7] = 1'b0;
|
|
end
|
|
end
|
|
12'h340: begin
|
|
if (csr_set_w && csr_clr_w) csr_mscratch_r = csr_data_w;
|
|
else if (csr_set_w) csr_mscratch_r = csr_mscratch_r | csr_data_w;
|
|
else if (csr_clr_w) csr_mscratch_r = csr_mscratch_r & ~csr_data_w;
|
|
end
|
|
12'h344: begin
|
|
if (csr_set_w && csr_clr_w) csr_mip_r = csr_data_w;
|
|
else if (csr_set_w) csr_mip_r = csr_mip_r | csr_data_w;
|
|
else if (csr_clr_w) csr_mip_r = csr_mip_r & ~csr_data_w;
|
|
end
|
|
12'h304: begin
|
|
if (csr_set_w && csr_clr_w) csr_mie_r = csr_data_w;
|
|
else if (csr_set_w) csr_mie_r = csr_mie_r | csr_data_w;
|
|
else if (csr_clr_w) csr_mie_r = csr_mie_r & ~csr_data_w;
|
|
end
|
|
12'h305: begin
|
|
if (csr_set_w && csr_clr_w) csr_mtvec_r = csr_data_w;
|
|
else if (csr_set_w) csr_mtvec_r = csr_mtvec_r | csr_data_w;
|
|
else if (csr_clr_w) csr_mtvec_r = csr_mtvec_r & ~csr_data_w;
|
|
end
|
|
12'h343: begin
|
|
if (csr_set_w && csr_clr_w) csr_mtval_r = csr_data_w;
|
|
else if (csr_set_w) csr_mtval_r = csr_mtval_r | csr_data_w;
|
|
else if (csr_clr_w) csr_mtval_r = csr_mtval_r & ~csr_data_w;
|
|
end
|
|
default: ;
|
|
endcase
|
|
end
|
|
end
|
|
end
|
|
always @(posedge clk_i)
|
|
if (rst_i) begin
|
|
csr_mepc_q <= 32'b0;
|
|
csr_mcause_q <= 32'b0;
|
|
csr_sr_q <= 32'b0;
|
|
csr_mcycle_q <= 32'b0;
|
|
csr_mtimecmp_q <= 32'b0;
|
|
csr_mscratch_q <= 32'b0;
|
|
csr_mie_q <= 32'b0;
|
|
csr_mip_q <= 32'b0;
|
|
csr_mtvec_q <= 32'b0;
|
|
csr_mtval_q <= 32'b0;
|
|
end else begin
|
|
csr_mepc_q <= csr_mepc_r;
|
|
csr_mcause_q <= csr_mcause_r;
|
|
csr_sr_q <= csr_sr_r;
|
|
csr_mcycle_q <= SUPPORT_MCYCLE ? csr_mcycle_r : 32'b0;
|
|
csr_mtimecmp_q <= SUPPORT_MTIMECMP ? csr_mtimecmp_r : 32'b0;
|
|
csr_mscratch_q <= SUPPORT_MSCRATCH ? csr_mscratch_r : 32'b0;
|
|
csr_mie_q <= SUPPORT_MIP_MIE ? csr_mie_r : 32'b0;
|
|
csr_mip_q <= SUPPORT_MIP_MIE ? csr_mip_r : 32'b0;
|
|
csr_mtvec_q <= SUPPORT_MTVEC ? csr_mtvec_r : 32'b0;
|
|
csr_mtval_q <= SUPPORT_MTVAL ? csr_mtval_r : 32'b0;
|
|
end
|
|
reg [31:0] csr_data_r;
|
|
always @* begin
|
|
csr_data_r = 32'b0;
|
|
case (csr_addr_w)
|
|
12'h341: csr_data_r = csr_mepc_q & 32'hFFFFFFFF;
|
|
12'h342: csr_data_r = csr_mcause_q & 32'h8000000F;
|
|
12'h300: csr_data_r = csr_sr_q & 32'hFFFFFFFF;
|
|
12'h305: csr_data_r = csr_mtvec_q & 32'hFFFFFFFF;
|
|
12'h343: csr_data_r = csr_mtval_q & 32'hFFFFFFFF;
|
|
12'hc01, 12'hc00: csr_data_r = csr_mcycle_q & 32'hFFFFFFFF;
|
|
12'h7c0: csr_data_r = csr_mtimecmp_q & 32'hFFFFFFFF;
|
|
12'h340: csr_data_r = csr_mscratch_q & 32'hFFFFFFFF;
|
|
12'h344: csr_data_r = csr_mip_q & ((1 << 11) | (1 << 7) | (1 << 3));
|
|
12'h304: csr_data_r = csr_mie_q & ((1 << 11) | (1 << 7) | (1 << 3));
|
|
12'h301: csr_data_r = (SUPPORT_MULDIV ? 32'h00001000 : 32'b0) | 32'h40000000 | 32'h00000100;
|
|
12'hF14: csr_data_r = cpu_id_i;
|
|
default: csr_data_r = 32'b0;
|
|
endcase
|
|
end
|
|
assign csr_rdata_o = csr_data_r;
|
|
assign take_interrupt_w = SUPPORT_MIP_MIE ? ((|(csr_mip_q & csr_mie_q)) & csr_sr_q[3]) : (intr_i & csr_sr_q[3]);
|
|
assign exception_w = valid_i && (take_interrupt_w || excpn_invalid_inst_i || (SUPPORT_CSR && excpn_lsu_align_i));
|
|
assign exception_o = exception_w;
|
|
assign exception_pc_o = SUPPORT_MTVEC ? csr_mtvec_q : SUPPORT_CSR ? isr_vector_i : pc_i + 32'd4;
|
|
assign csr_mepc_o = csr_mepc_q;
|
|
reg [5:0] v_etype_r;
|
|
always @* begin
|
|
v_etype_r = 6'b0;
|
|
if (csr_mcause_r[31]) v_etype_r = 6'h20;
|
|
else
|
|
case (csr_mcause_r)
|
|
((0 << 31) | 0): v_etype_r = 6'h10;
|
|
((0 << 31) | 1): v_etype_r = 6'h11;
|
|
((0 << 31) | 2): v_etype_r = 6'h12;
|
|
((0 << 31) | 3): v_etype_r = 6'h13;
|
|
((0 << 31) | 4): v_etype_r = 6'h14;
|
|
((0 << 31) | 5): v_etype_r = 6'h15;
|
|
((0 << 31) | 6): v_etype_r = 6'h16;
|
|
((0 << 31) | 7): v_etype_r = 6'h17;
|
|
((0 << 31) | 8): v_etype_r = 6'h18;
|
|
((0 << 31) | 9): v_etype_r = 6'h19;
|
|
((0 << 31) | 10): v_etype_r = 6'h1a;
|
|
((0 << 31) | 11): v_etype_r = 6'h1b;
|
|
((0 << 31) | 12): v_etype_r = 6'h1c;
|
|
((0 << 31) | 13): v_etype_r = 6'h1d;
|
|
((0 << 31) | 15): v_etype_r = 6'h1f;
|
|
endcase
|
|
end
|
|
assign exception_type_o = v_etype_r;
|
|
endmodule
|
|
module uriscv_lsu #(
|
|
parameter SUPPORT_TRAP_LSU_ALIGN = 1
|
|
) (
|
|
input [31:0] opcode_i,
|
|
input [31:0] rs1_val_i,
|
|
input [31:0] rs2_val_i,
|
|
output mem_rd_o,
|
|
output [ 3:0] mem_wr_o,
|
|
output [31:0] mem_addr_o,
|
|
output [31:0] mem_data_o,
|
|
output mem_misaligned_o
|
|
);
|
|
wire type_load_w = (opcode_i[6:2] == 5'b00000);
|
|
wire type_store_w = (opcode_i[6:2] == 5'b01000);
|
|
wire [2:0] func3_w = opcode_i[14:12]; // R, I, S
|
|
wire inst_lb_w = type_load_w && (func3_w == 3'b000);
|
|
wire inst_lh_w = type_load_w && (func3_w == 3'b001);
|
|
wire inst_lw_w = type_load_w && (func3_w == 3'b010);
|
|
wire inst_lbu_w = type_load_w && (func3_w == 3'b100);
|
|
wire inst_lhu_w = type_load_w && (func3_w == 3'b101);
|
|
wire inst_sb_w = type_store_w && (func3_w == 3'b000);
|
|
wire inst_sh_w = type_store_w && (func3_w == 3'b001);
|
|
wire inst_sw_w = type_store_w && (func3_w == 3'b010);
|
|
reg [31:0] imm12_r;
|
|
reg [31:0] storeimm_r;
|
|
reg [31:0] mem_addr_r;
|
|
reg [31:0] mem_data_r;
|
|
reg [3:0] mem_wr_r;
|
|
reg mem_rd_r;
|
|
reg mem_misaligned_r;
|
|
always @* begin
|
|
imm12_r = {{20{opcode_i[31]}}, opcode_i[31:20]};
|
|
storeimm_r = {{20{opcode_i[31]}}, opcode_i[31:25], opcode_i[11:7]};
|
|
// Memory address
|
|
mem_addr_r = rs1_val_i + (type_store_w ? storeimm_r : imm12_r);
|
|
if (SUPPORT_TRAP_LSU_ALIGN)
|
|
mem_misaligned_r = (inst_lh_w | inst_lhu_w | inst_sh_w) ? mem_addr_r[0]:
|
|
(inst_lw_w | inst_sw_w) ? (|mem_addr_r[1:0]):
|
|
1'b0;
|
|
else mem_misaligned_r = 1'b0;
|
|
mem_data_r = 32'h00000000;
|
|
mem_wr_r = 4'b0000;
|
|
mem_rd_r = 1'b0;
|
|
case (1'b1)
|
|
type_load_w: mem_rd_r = 1'b1;
|
|
inst_sb_w: begin
|
|
case (mem_addr_r[1:0])
|
|
2'h3: begin
|
|
mem_data_r = {rs2_val_i[7:0], 24'h000000};
|
|
mem_wr_r = 4'b1000;
|
|
mem_rd_r = 1'b0;
|
|
end
|
|
2'h2: begin
|
|
mem_data_r = {8'h00, rs2_val_i[7:0], 16'h0000};
|
|
mem_wr_r = 4'b0100;
|
|
mem_rd_r = 1'b0;
|
|
end
|
|
2'h1: begin
|
|
mem_data_r = {16'h0000, rs2_val_i[7:0], 8'h00};
|
|
mem_wr_r = 4'b0010;
|
|
mem_rd_r = 1'b0;
|
|
end
|
|
2'h0: begin
|
|
mem_data_r = {24'h000000, rs2_val_i[7:0]};
|
|
mem_wr_r = 4'b0001;
|
|
mem_rd_r = 1'b0;
|
|
end
|
|
default: ;
|
|
endcase
|
|
end
|
|
inst_sh_w: begin
|
|
case (mem_addr_r[1:0])
|
|
2'h2: begin
|
|
mem_data_r = {rs2_val_i[15:0], 16'h0000};
|
|
mem_wr_r = 4'b1100;
|
|
mem_rd_r = 1'b0;
|
|
end
|
|
default: begin
|
|
mem_data_r = {16'h0000, rs2_val_i[15:0]};
|
|
mem_wr_r = 4'b0011;
|
|
mem_rd_r = 1'b0;
|
|
end
|
|
endcase
|
|
end
|
|
inst_sw_w: begin
|
|
mem_data_r = rs2_val_i;
|
|
mem_wr_r = 4'b1111;
|
|
mem_rd_r = 1'b0;
|
|
end
|
|
// Non load / store
|
|
default: ;
|
|
endcase
|
|
end
|
|
assign mem_rd_o = mem_rd_r;
|
|
assign mem_wr_o = mem_wr_r;
|
|
assign mem_addr_o = mem_addr_r;
|
|
assign mem_data_o = mem_data_r;
|
|
assign mem_misaligned_o = mem_misaligned_r;
|
|
endmodule
|
|
module uriscv_muldiv (
|
|
input clk_i,
|
|
input rst_i,
|
|
// Operation select
|
|
input valid_i,
|
|
input inst_mul_i,
|
|
input inst_mulh_i,
|
|
input inst_mulhsu_i,
|
|
input inst_mulhu_i,
|
|
input inst_div_i,
|
|
input inst_divu_i,
|
|
input inst_rem_i,
|
|
input inst_remu_i,
|
|
// Operands
|
|
input [31:0] operand_ra_i,
|
|
input [31:0] operand_rb_i,
|
|
// Result
|
|
output stall_o,
|
|
output ready_o,
|
|
output [31:0] result_o
|
|
);
|
|
reg [32:0] mul_operand_a_q;
|
|
reg [32:0] mul_operand_b_q;
|
|
reg mulhi_sel_q;
|
|
wire [64:0] mult_result_w;
|
|
reg [32:0] operand_b_r;
|
|
reg [32:0] operand_a_r;
|
|
reg [31:0] mul_result_r;
|
|
wire mult_inst_w = inst_mul_i | inst_mulh_i | inst_mulhsu_i | inst_mulhu_i;
|
|
always @* begin
|
|
if (inst_mulhsu_i) operand_a_r = {operand_ra_i[31], operand_ra_i[31:0]};
|
|
else if (inst_mulh_i) operand_a_r = {operand_ra_i[31], operand_ra_i[31:0]};
|
|
else // MULHU || MUL
|
|
operand_a_r = {1'b0, operand_ra_i[31:0]};
|
|
end
|
|
always @* begin
|
|
if (inst_mulhsu_i) operand_b_r = {1'b0, operand_rb_i[31:0]};
|
|
else if (inst_mulh_i) operand_b_r = {operand_rb_i[31], operand_rb_i[31:0]};
|
|
else // MULHU || MUL
|
|
operand_b_r = {1'b0, operand_rb_i[31:0]};
|
|
end
|
|
always @(posedge clk_i)
|
|
if (rst_i) begin
|
|
mul_operand_a_q <= 33'b0;
|
|
mul_operand_b_q <= 33'b0;
|
|
mulhi_sel_q <= 1'b0;
|
|
end else if (valid_i && mult_inst_w) begin
|
|
mul_operand_a_q <= operand_a_r;
|
|
mul_operand_b_q <= operand_b_r;
|
|
mulhi_sel_q <= ~inst_mul_i;
|
|
end else begin
|
|
mul_operand_a_q <= 33'b0;
|
|
mul_operand_b_q <= 33'b0;
|
|
mulhi_sel_q <= 1'b0;
|
|
end
|
|
assign mult_result_w = {{ 32 {mul_operand_a_q[32]}}, mul_operand_a_q}*{{ 32 {mul_operand_b_q[32]}}, mul_operand_b_q};
|
|
always @* begin
|
|
mul_result_r = mulhi_sel_q ? mult_result_w[63:32] : mult_result_w[31:0];
|
|
end
|
|
reg mul_busy_q;
|
|
always @(posedge clk_i)
|
|
if (rst_i) mul_busy_q <= 1'b0;
|
|
else mul_busy_q <= valid_i & mult_inst_w;
|
|
wire div_rem_inst_w = inst_div_i || inst_divu_i || inst_rem_i || inst_remu_i;
|
|
wire signed_operation_w = inst_div_i || inst_rem_i;
|
|
wire div_operation_w = inst_div_i || inst_divu_i;
|
|
reg [31:0] dividend_q;
|
|
reg [62:0] divisor_q;
|
|
reg [31:0] quotient_q;
|
|
reg [31:0] q_mask_q;
|
|
reg div_inst_q;
|
|
reg div_busy_q;
|
|
reg invert_res_q;
|
|
wire div_start_w = valid_i & div_rem_inst_w & !stall_o;
|
|
wire div_complete_w = !(|q_mask_q) & div_busy_q;
|
|
always @(posedge clk_i)
|
|
if (rst_i) begin
|
|
div_busy_q <= 1'b0;
|
|
dividend_q <= 32'b0;
|
|
divisor_q <= 63'b0;
|
|
invert_res_q <= 1'b0;
|
|
quotient_q <= 32'b0;
|
|
q_mask_q <= 32'b0;
|
|
div_inst_q <= 1'b0;
|
|
end else if (div_start_w) begin
|
|
div_busy_q <= 1'b1;
|
|
div_inst_q <= div_operation_w;
|
|
if (signed_operation_w && operand_ra_i[31]) dividend_q <= -operand_ra_i;
|
|
else dividend_q <= operand_ra_i;
|
|
if (signed_operation_w && operand_rb_i[31]) divisor_q <= {-operand_rb_i, 31'b0};
|
|
else divisor_q <= {operand_rb_i, 31'b0};
|
|
invert_res_q <= (inst_div_i && (operand_ra_i[31] != operand_rb_i[31]) && |operand_rb_i) ||
|
|
(inst_rem_i && operand_ra_i[31]);
|
|
quotient_q <= 32'b0;
|
|
q_mask_q <= 32'h80000000;
|
|
end else if (div_complete_w) begin
|
|
div_busy_q <= 1'b0;
|
|
end else if (div_busy_q) begin
|
|
if (divisor_q <= {31'b0, dividend_q}) begin
|
|
dividend_q <= dividend_q - divisor_q[31:0];
|
|
quotient_q <= quotient_q | q_mask_q;
|
|
end
|
|
divisor_q <= {1'b0, divisor_q[62:1]};
|
|
q_mask_q <= {1'b0, q_mask_q[31:1]};
|
|
end
|
|
reg [31:0] div_result_r;
|
|
always @* begin
|
|
div_result_r = 32'b0;
|
|
if (div_inst_q) div_result_r = invert_res_q ? -quotient_q : quotient_q;
|
|
else div_result_r = invert_res_q ? -dividend_q : dividend_q;
|
|
end
|
|
assign stall_o = (div_busy_q & (mult_inst_w | div_rem_inst_w)) || (mul_busy_q & div_rem_inst_w);
|
|
reg [31:0] result_q;
|
|
reg ready_q;
|
|
always @(posedge clk_i)
|
|
if (rst_i) ready_q <= 1'b0;
|
|
else if (mul_busy_q) ready_q <= 1'b1;
|
|
else if (div_complete_w) ready_q <= 1'b1;
|
|
else ready_q <= 1'b0;
|
|
always @(posedge clk_i)
|
|
if (rst_i) result_q <= 32'b0;
|
|
else if (div_complete_w) result_q <= div_result_r;
|
|
else if (mul_busy_q) result_q <= mul_result_r;
|
|
assign result_o = result_q;
|
|
assign ready_o = ready_q;
|
|
endmodule
|
|
module soc_top (
|
|
input bit clk,
|
|
input bit rst
|
|
);
|
|
reg [7:0] mem[65535:0];
|
|
integer i;
|
|
integer f;
|
|
initial begin
|
|
$display("Starting bench");
|
|
// Load TCM memory
|
|
for (i = 0; i < 65535; i = i + 1) mem[i] = 0;
|
|
// $readmemh("test.hex", mem);
|
|
$readmemh("program.hex", mem);
|
|
for (i = 0; i < 65535; i = i + 1) u_mem.write(i, mem[i]);
|
|
end
|
|
wire mem_i_rd_w;
|
|
wire mem_i_flush_w;
|
|
wire mem_i_invalidate_w;
|
|
wire [31:0] mem_i_pc_w;
|
|
wire [31:0] mem_d_addr_w;
|
|
wire [31:0] mem_d_data_wr_w;
|
|
wire mem_d_rd_w;
|
|
wire [ 3:0] mem_d_wr_w;
|
|
wire mem_d_cacheable_w;
|
|
wire [10:0] mem_d_req_tag_w;
|
|
wire mem_d_invalidate_w;
|
|
wire mem_d_writeback_w;
|
|
wire mem_d_flush_w;
|
|
wire mem_i_accept_w;
|
|
wire mem_i_valid_w;
|
|
wire mem_i_error_w;
|
|
wire [31:0] mem_i_inst_w;
|
|
wire [31:0] mem_d_data_rd_w;
|
|
wire mem_d_accept_w;
|
|
wire mem_d_ack_w;
|
|
wire mem_d_error_w;
|
|
wire [10:0] mem_d_resp_tag_w;
|
|
riscv_core u_dut
|
|
//-----------------------------------------------------------------
|
|
// Ports
|
|
//-----------------------------------------------------------------
|
|
(
|
|
// Inputs
|
|
.clk_i(clk),
|
|
.rst_i(rst),
|
|
.mem_d_data_rd_i(mem_d_data_rd_w),
|
|
.mem_d_accept_i(mem_d_accept_w),
|
|
.mem_d_ack_i(mem_d_ack_w),
|
|
.mem_d_error_i(mem_d_error_w),
|
|
.mem_d_resp_tag_i(mem_d_resp_tag_w),
|
|
.mem_i_accept_i(mem_i_accept_w),
|
|
.mem_i_valid_i(mem_i_valid_w),
|
|
.mem_i_error_i(mem_i_error_w),
|
|
.mem_i_inst_i(mem_i_inst_w),
|
|
.intr_i(1'b0),
|
|
.reset_vector_i(32'h80000000),
|
|
.cpu_id_i('b0)
|
|
// Outputs
|
|
, .mem_d_addr_o(mem_d_addr_w),
|
|
.mem_d_data_wr_o(mem_d_data_wr_w),
|
|
.mem_d_rd_o(mem_d_rd_w),
|
|
.mem_d_wr_o(mem_d_wr_w),
|
|
.mem_d_cacheable_o(mem_d_cacheable_w),
|
|
.mem_d_req_tag_o(mem_d_req_tag_w),
|
|
.mem_d_invalidate_o(mem_d_invalidate_w),
|
|
.mem_d_writeback_o(mem_d_writeback_w),
|
|
.mem_d_flush_o(mem_d_flush_w),
|
|
.mem_i_rd_o(mem_i_rd_w),
|
|
.mem_i_flush_o(mem_i_flush_w),
|
|
.mem_i_invalidate_o(mem_i_invalidate_w),
|
|
.mem_i_pc_o(mem_i_pc_w)
|
|
);
|
|
tcm_mem u_mem (
|
|
// Inputs
|
|
.clk_i(clk),
|
|
.rst_i(rst),
|
|
.mem_i_rd_i(mem_i_rd_w),
|
|
.mem_i_flush_i(mem_i_flush_w),
|
|
.mem_i_invalidate_i(mem_i_invalidate_w),
|
|
.mem_i_pc_i(mem_i_pc_w),
|
|
.mem_d_addr_i(mem_d_addr_w),
|
|
.mem_d_data_wr_i(mem_d_data_wr_w),
|
|
.mem_d_rd_i(mem_d_rd_w),
|
|
.mem_d_wr_i(mem_d_wr_w),
|
|
.mem_d_cacheable_i(mem_d_cacheable_w),
|
|
.mem_d_req_tag_i(mem_d_req_tag_w),
|
|
.mem_d_invalidate_i(mem_d_invalidate_w),
|
|
.mem_d_writeback_i(mem_d_writeback_w),
|
|
.mem_d_flush_i(mem_d_flush_w)
|
|
// Outputs
|
|
, .mem_i_accept_o(mem_i_accept_w),
|
|
.mem_i_valid_o(mem_i_valid_w),
|
|
.mem_i_error_o(mem_i_error_w),
|
|
.mem_i_inst_o(mem_i_inst_w),
|
|
.mem_d_data_rd_o(mem_d_data_rd_w),
|
|
.mem_d_accept_o(mem_d_accept_w),
|
|
.mem_d_ack_o(mem_d_ack_w),
|
|
.mem_d_error_o(mem_d_error_w),
|
|
.mem_d_resp_tag_o(mem_d_resp_tag_w)
|
|
);
|
|
endmodule
|
|
module tcm_mem_ram (
|
|
// Inputs
|
|
input clk0_i,
|
|
input rst0_i,
|
|
input [13:0] addr0_i,
|
|
input [31:0] data0_i,
|
|
input [ 3:0] wr0_i,
|
|
input clk1_i,
|
|
input rst1_i,
|
|
input [13:0] addr1_i,
|
|
input [31:0] data1_i,
|
|
input [ 3:0] wr1_i
|
|
// Outputs
|
|
, output [31:0] data0_o,
|
|
output [31:0] data1_o
|
|
);
|
|
integer memlog;
|
|
initial begin
|
|
memlog = $fopen("mem.log", "w");
|
|
end
|
|
//-----------------------------------------------------------------
|
|
// Dual Port RAM 64KB
|
|
// Mode: Read First
|
|
//-----------------------------------------------------------------
|
|
/* verilator lint_off MULTIDRIVEN */
|
|
reg [31:0] ram[16383:0] /*verilator public*/;
|
|
/* verilator lint_on MULTIDRIVEN */
|
|
reg [31:0] ram_read0_q;
|
|
reg [31:0] ram_read1_q;
|
|
// Synchronous write
|
|
always @(posedge clk0_i) begin
|
|
if (wr0_i[0]) ram[addr0_i][7:0] <= data0_i[7:0];
|
|
if (wr0_i[1]) ram[addr0_i][15:8] <= data0_i[15:8];
|
|
if (wr0_i[2]) ram[addr0_i][23:16] <= data0_i[23:16];
|
|
if (wr0_i[3]) ram[addr0_i][31:24] <= data0_i[31:24];
|
|
ram_read0_q <= ram[addr0_i];
|
|
$fwrite(memlog, "addr0: 0x%0h data: 0x%0h \n", addr0_i, ram[addr0_i]);
|
|
end
|
|
always @(posedge clk1_i) begin
|
|
if (wr1_i[0]) ram[addr1_i][7:0] <= data1_i[7:0];
|
|
if (wr1_i[1]) ram[addr1_i][15:8] <= data1_i[15:8];
|
|
if (wr1_i[2]) ram[addr1_i][23:16] <= data1_i[23:16];
|
|
if (wr1_i[3]) ram[addr1_i][31:24] <= data1_i[31:24];
|
|
ram_read1_q <= ram[addr1_i];
|
|
$fwrite(memlog, "addr1: 0x%0h data: 0x%0h \n", addr1_i, ram[addr1_i]);
|
|
end
|
|
assign data0_o = ram_read0_q;
|
|
assign data1_o = ram_read1_q;
|
|
endmodule
|
|
module tcm_mem (
|
|
// Inputs
|
|
input clk_i,
|
|
input rst_i,
|
|
input mem_i_rd_i,
|
|
input mem_i_flush_i,
|
|
input mem_i_invalidate_i,
|
|
input [31:0] mem_i_pc_i,
|
|
input [31:0] mem_d_addr_i,
|
|
input [31:0] mem_d_data_wr_i,
|
|
input mem_d_rd_i,
|
|
input [ 3:0] mem_d_wr_i,
|
|
input mem_d_cacheable_i,
|
|
input [10:0] mem_d_req_tag_i,
|
|
input mem_d_invalidate_i,
|
|
input mem_d_writeback_i,
|
|
input mem_d_flush_i
|
|
// Outputs
|
|
, output mem_i_accept_o,
|
|
output mem_i_valid_o,
|
|
output mem_i_error_o,
|
|
output [31:0] mem_i_inst_o,
|
|
output [31:0] mem_d_data_rd_o,
|
|
output mem_d_accept_o,
|
|
output mem_d_ack_o,
|
|
output mem_d_error_o,
|
|
output [10:0] mem_d_resp_tag_o
|
|
);
|
|
wire [31:0] data_r_w;
|
|
tcm_mem_ram u_ram (
|
|
// Instruction fetch
|
|
.clk0_i (clk_i),
|
|
.rst0_i (rst_i),
|
|
.addr0_i(mem_i_pc_i[15:2]),
|
|
.data0_i(32'b0),
|
|
.wr0_i (4'b0)
|
|
// External access / Data access
|
|
, .clk1_i (clk_i),
|
|
.rst1_i (rst_i),
|
|
.addr1_i(mem_d_addr_i[15:2]),
|
|
.data1_i(mem_d_data_wr_i),
|
|
.wr1_i (mem_d_wr_i)
|
|
// Outputs
|
|
, .data0_o(mem_i_inst_o),
|
|
.data1_o(data_r_w)
|
|
);
|
|
reg mem_i_valid_q;
|
|
always @(posedge clk_i)
|
|
if (rst_i) mem_i_valid_q <= 1'b0;
|
|
else mem_i_valid_q <= mem_i_rd_i;
|
|
assign mem_i_accept_o = 1'b1;
|
|
assign mem_i_valid_o = mem_i_valid_q;
|
|
assign mem_i_error_o = 1'b0;
|
|
reg mem_d_accept_q;
|
|
reg mem_d_ack_q;
|
|
reg [10:0] mem_d_tag_q;
|
|
always @(posedge clk_i)
|
|
if (rst_i) begin
|
|
mem_d_ack_q <= 1'b0;
|
|
mem_d_tag_q <= 11'b0;
|
|
end
|
|
else if ((mem_d_rd_i || mem_d_wr_i != 4'b0 || mem_d_flush_i || mem_d_invalidate_i || mem_d_writeback_i) && mem_d_accept_o)
|
|
begin
|
|
mem_d_ack_q <= 1'b1;
|
|
mem_d_tag_q <= mem_d_req_tag_i;
|
|
end else mem_d_ack_q <= 1'b0;
|
|
assign mem_d_ack_o = mem_d_ack_q;
|
|
assign mem_d_resp_tag_o = mem_d_tag_q;
|
|
assign mem_d_data_rd_o = data_r_w;
|
|
assign mem_d_error_o = 1'b0;
|
|
assign mem_d_accept_o = 1'b1;
|
|
task write; /*verilator public*/
|
|
input [31:0] addr;
|
|
input [7:0] data;
|
|
begin
|
|
case (addr[1:0])
|
|
2'd0: u_ram.ram[addr/4][7:0] = data;
|
|
2'd1: u_ram.ram[addr/4][15:8] = data;
|
|
2'd2: u_ram.ram[addr/4][23:16] = data;
|
|
2'd3: u_ram.ram[addr/4][31:24] = data;
|
|
endcase
|
|
end
|
|
endtask
|
|
endmodule
|
|
module soc_sim (
|
|
input bit core_clk
|
|
);
|
|
logic rst_l;
|
|
parameter MAX_CYCLES = 1000;
|
|
int cycleCnt;
|
|
always @(posedge core_clk) begin
|
|
cycleCnt <= cycleCnt + 1;
|
|
if (cycleCnt == MAX_CYCLES) begin
|
|
$display("Hit max cycle count (%0d) .. stopping", cycleCnt);
|
|
$finish;
|
|
end
|
|
end
|
|
assign rst_l = cycleCnt > 5;
|
|
soc_top rvsoc (
|
|
.clk(core_clk),
|
|
.rst(rst_l)
|
|
);
|
|
endmodule
|