417 lines
14 KiB
Verilog
417 lines
14 KiB
Verilog
//-----------------------------------------------------------------
|
|
// uRISC-V CPU
|
|
// V0.5.0
|
|
// github.com/ultraembedded/core_uriscv
|
|
// Copyright 2015-2021
|
|
//
|
|
// admin@ultra-embedded.com
|
|
//
|
|
// License: Apache 2.0
|
|
//-----------------------------------------------------------------
|
|
// Copyright 2015-2021 github.com/ultraembedded
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//-----------------------------------------------------------------
|
|
module uriscv_csr
|
|
//-----------------------------------------------------------------
|
|
// Params
|
|
//-----------------------------------------------------------------
|
|
#(
|
|
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
|
|
)
|
|
//-----------------------------------------------------------------
|
|
// Ports
|
|
//-----------------------------------------------------------------
|
|
(
|
|
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
|
|
);
|
|
|
|
|
|
//-----------------------------------------------------------------
|
|
// Includes
|
|
//-----------------------------------------------------------------
|
|
`include "uriscv_defs.v"
|
|
|
|
wire take_interrupt_w;
|
|
wire exception_w;
|
|
|
|
//-----------------------------------------------------------------
|
|
// Instruction Decode
|
|
//-----------------------------------------------------------------
|
|
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;
|
|
|
|
//-----------------------------------------------------------------
|
|
// Execute: CSR Access
|
|
//-----------------------------------------------------------------
|
|
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[`IRQ_M_EXT] = 1'b1;
|
|
|
|
// Timer match - generate IRQ
|
|
if (SUPPORT_MTIMECMP && csr_mcycle_r == csr_mtimecmp_r)
|
|
csr_mip_r[`SR_IP_MTIP_R] = 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[`SR_MPIE_R] = csr_sr_q[`SR_MIE_R];
|
|
csr_sr_r[`SR_MPP_R] = `PRIV_MACHINE;
|
|
|
|
// Disable interrupts and enter supervisor mode
|
|
csr_sr_r[`SR_MIE_R] = 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 = `MCAUSE_ILLEGAL_INSTRUCTION;
|
|
csr_mtval_r = opcode_i;
|
|
end
|
|
else if (inst_ebreak_w)
|
|
csr_mcause_r = `MCAUSE_BREAKPOINT;
|
|
else if (inst_ecall_w)
|
|
csr_mcause_r = `MCAUSE_ECALL_M;
|
|
else if (excpn_lsu_align_i)
|
|
begin
|
|
csr_mcause_r = type_store_w ? `MCAUSE_MISALIGNED_STORE : `MCAUSE_MISALIGNED_LOAD;
|
|
csr_mtval_r = mem_addr_i;
|
|
end
|
|
else if (take_interrupt_w)
|
|
csr_mcause_r = `MCAUSE_INTERRUPT;
|
|
end
|
|
// MRET
|
|
else if (inst_mret_w)
|
|
begin
|
|
// Interrupt enable pop
|
|
csr_sr_r[`SR_MIE_R] = csr_sr_r[`SR_MPIE_R];
|
|
csr_sr_r[`SR_MPIE_R] = 1'b1;
|
|
|
|
// This CPU only supports machine mode
|
|
csr_sr_r[`SR_MPP_R] = `PRIV_MACHINE;
|
|
end
|
|
else
|
|
begin
|
|
case (csr_addr_w)
|
|
`CSR_MEPC:
|
|
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
|
|
`CSR_MCAUSE:
|
|
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
|
|
`CSR_MSTATUS:
|
|
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
|
|
`CSR_MTIMECMP:
|
|
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[`SR_IP_MTIP_R] = 1'b0;
|
|
end
|
|
end
|
|
`CSR_MSCRATCH:
|
|
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
|
|
`CSR_MIP:
|
|
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
|
|
`CSR_MIE:
|
|
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
|
|
`CSR_MTVEC:
|
|
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
|
|
`CSR_MTVAL:
|
|
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
|
|
|
|
`ifdef verilator
|
|
`define HAS_SIM_CTRL
|
|
`endif
|
|
`ifdef verilog_sim
|
|
`define HAS_SIM_CTRL
|
|
`endif
|
|
|
|
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;
|
|
|
|
`ifdef HAS_SIM_CTRL
|
|
if (valid_i && (csr_addr_w == `CSR_DSCRATCH || csr_addr_w == `CSR_SIM_CTRL) && inst_csr_w)
|
|
begin
|
|
case (csr_data_w & 32'hFF000000)
|
|
`CSR_SIM_CTRL_EXIT:
|
|
begin
|
|
$finish;
|
|
$finish;
|
|
end
|
|
`CSR_SIM_CTRL_PUTC:
|
|
begin
|
|
$write("%c", csr_data_w[7:0]);
|
|
end
|
|
endcase
|
|
end
|
|
`endif
|
|
end
|
|
|
|
//-----------------------------------------------------------------
|
|
// CSR Read Data MUX
|
|
//-----------------------------------------------------------------
|
|
reg [31:0] csr_data_r;
|
|
|
|
always @ *
|
|
begin
|
|
csr_data_r = 32'b0;
|
|
|
|
case (csr_addr_w)
|
|
`CSR_MEPC: csr_data_r = csr_mepc_q & `CSR_MEPC_MASK;
|
|
`CSR_MCAUSE: csr_data_r = csr_mcause_q & `CSR_MCAUSE_MASK;
|
|
`CSR_MSTATUS: csr_data_r = csr_sr_q & `CSR_MSTATUS_MASK;
|
|
`CSR_MTVEC: csr_data_r = csr_mtvec_q & `CSR_MTVEC_MASK;
|
|
`CSR_MTVAL: csr_data_r = csr_mtval_q & `CSR_MTVAL_MASK;
|
|
`CSR_MTIME,
|
|
`CSR_MCYCLE: csr_data_r = csr_mcycle_q & `CSR_MTIME_MASK;
|
|
`CSR_MTIMECMP: csr_data_r = csr_mtimecmp_q & `CSR_MTIMECMP_MASK;
|
|
`CSR_MSCRATCH: csr_data_r = csr_mscratch_q & `CSR_MSCRATCH_MASK;
|
|
`CSR_MIP: csr_data_r = csr_mip_q & `CSR_MIP_MASK;
|
|
`CSR_MIE: csr_data_r = csr_mie_q & `CSR_MIE_MASK;
|
|
`CSR_MISA: csr_data_r = (SUPPORT_MULDIV ? `MISA_RVM : 32'b0) |
|
|
`MISA_RV32 | `MISA_RVI;
|
|
`CSR_MHARTID: csr_data_r = cpu_id_i;
|
|
default: csr_data_r = 32'b0;
|
|
endcase
|
|
end
|
|
|
|
assign csr_rdata_o = csr_data_r;
|
|
|
|
// Interrupt request and interrupt enabled
|
|
assign take_interrupt_w = SUPPORT_MIP_MIE ? ((|(csr_mip_q & csr_mie_q)) & csr_sr_q[`SR_MIE_R]) : (intr_i & csr_sr_q[`SR_MIE_R]);
|
|
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;
|
|
|
|
//-----------------------------------------------------------------
|
|
// Debug - exception type (checker use only)
|
|
//-----------------------------------------------------------------
|
|
reg [5:0] v_etype_r;
|
|
|
|
always @ *
|
|
begin
|
|
v_etype_r = 6'b0;
|
|
|
|
if (csr_mcause_r[`MCAUSE_INT])
|
|
v_etype_r = `RV_EXCPN_INTERRUPT;
|
|
else case (csr_mcause_r)
|
|
`MCAUSE_MISALIGNED_FETCH : v_etype_r = `RV_EXCPN_MISALIGNED_FETCH;
|
|
`MCAUSE_FAULT_FETCH : v_etype_r = `RV_EXCPN_FAULT_FETCH;
|
|
`MCAUSE_ILLEGAL_INSTRUCTION: v_etype_r = `RV_EXCPN_ILLEGAL_INSTRUCTION;
|
|
`MCAUSE_BREAKPOINT : v_etype_r = `RV_EXCPN_BREAKPOINT;
|
|
`MCAUSE_MISALIGNED_LOAD : v_etype_r = `RV_EXCPN_MISALIGNED_LOAD;
|
|
`MCAUSE_FAULT_LOAD : v_etype_r = `RV_EXCPN_FAULT_LOAD;
|
|
`MCAUSE_MISALIGNED_STORE : v_etype_r = `RV_EXCPN_MISALIGNED_STORE;
|
|
`MCAUSE_FAULT_STORE : v_etype_r = `RV_EXCPN_FAULT_STORE;
|
|
`MCAUSE_ECALL_U : v_etype_r = `RV_EXCPN_ECALL_U;
|
|
`MCAUSE_ECALL_S : v_etype_r = `RV_EXCPN_ECALL_S;
|
|
`MCAUSE_ECALL_H : v_etype_r = `RV_EXCPN_ECALL_H;
|
|
`MCAUSE_ECALL_M : v_etype_r = `RV_EXCPN_ECALL_M;
|
|
`MCAUSE_PAGE_FAULT_INST : v_etype_r = `RV_EXCPN_PAGE_FAULT_INST;
|
|
`MCAUSE_PAGE_FAULT_LOAD : v_etype_r = `RV_EXCPN_PAGE_FAULT_LOAD;
|
|
`MCAUSE_PAGE_FAULT_STORE : v_etype_r = `RV_EXCPN_PAGE_FAULT_STORE;
|
|
endcase
|
|
end
|
|
|
|
assign exception_type_o = v_etype_r;
|
|
|
|
endmodule
|