abstractaccelerator/uriscv/demo/top.v

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