|
@@ -2,6 +2,7 @@
|
|
|
* PicoRV32 -- A Small RISC-V (RV32I) Processor Core
|
|
|
*
|
|
|
* Copyright (C) 2015 Clifford Wolf <clifford@clifford.at>
|
|
|
+ * Heavily modified (in incompatible ways!) by H. Peter Anvin <hpa@zytor.com>
|
|
|
*
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
@@ -20,34 +21,41 @@
|
|
|
* - retirq opcode changed to mret.
|
|
|
* - qregs replaced with a full register bank switch. In general,
|
|
|
* non-power-of-two register files don't save anything, especially in
|
|
|
- * FPGAs.
|
|
|
+ * FPGAs. The interrupt mask is saved in x27/s11.
|
|
|
* - getq and setq replaced with new instructions addqxi and addxqi
|
|
|
* for cross-bank register accesses if needed,
|
|
|
* taking immediate as additive argument.
|
|
|
* e.g. for stack setup (addqxi sp,sp,frame_size).
|
|
|
+ * - On FPGAs SRAM can be (and pretty much universally is)
|
|
|
+ * initialized to all zero in hardware. Implement x0 = 0 by
|
|
|
+ * disabling the write enable for this register; this improves
|
|
|
+ * timing by avoiding MUXes in the data and address paths.
|
|
|
* - PROGADDR_RESET and PROGADDR_IRQ changed to ports (allows external
|
|
|
* implementation of vectorized interrupts or fallback reset.)
|
|
|
* - maskirq, waitirq and timer require func3 == 3'b000.
|
|
|
- * - add two masks to waitirq: an AND mask and an OR mask.
|
|
|
+ * - Add two masks to waitirq: an AND mask and an OR mask.
|
|
|
* waitirq exists if either all interrupts in the AND
|
|
|
* mask are pending or any interrupt in the OR mask is pending.
|
|
|
* Note that waitirq with an AND mask of zero will exit immediately;
|
|
|
- * this can be used to poll the status of interrupts (masked and unmasked.)
|
|
|
- * - multiple user (non-interrupt) register banks (tasks) now supported;
|
|
|
+ * this can be used to poll the status of interrupts (masked and unmasked)
|
|
|
+ * without sending EOI (see pollirq below for variant with EOI.)
|
|
|
+ * - Multiple user (non-interrupt) register banks (tasks) now supported;
|
|
|
* these are set via a custom user_context CSR (0x7f0). They are numbered
|
|
|
* starting with 1; 0 is reserved for the IRQ context. After reset,
|
|
|
* this register is set to the maximum supported user context number.
|
|
|
* Writing this register also causes a transition to the IRQ context,
|
|
|
* so the context switch can be processed atomically.
|
|
|
- * - the interrupt return address moved the mepc CSR, to make it
|
|
|
+ * - The interrupt return address moved the mepc CSR, to make it
|
|
|
* globally available at interrupt time. This simplifies context switching.
|
|
|
- * - implement the ctz instruction from the Zbb extension to improve
|
|
|
+ * Writing mepc from user context switches to IRQ context.
|
|
|
+ * - Implement the ctz instruction from the Zbb extension to improve
|
|
|
* interrupt latency by speeding up the dispatch substantially.
|
|
|
- * - new pollirq instruction: returns a mask of pending unmasked
|
|
|
+ * - New pollirq instruction: returns a mask of pending unmasked
|
|
|
* interrupts AND ~rs1 OR rs2. EOIs pending unmasked interrupts AND ~rs1.
|
|
|
* This is intended to avoid priority inversion in the IRQ dispatch.
|
|
|
- * - separately parameterize the width of the cycle and instruction counters;
|
|
|
+ * - Separately parameterize the width of the cycle and instruction counters;
|
|
|
* they can be independently set to any value from 0 to 64 bits.
|
|
|
+ * - The user context number (user_context CSR) is exported to a port.
|
|
|
*/
|
|
|
|
|
|
/* verilator lint_off WIDTH */
|
|
@@ -106,105 +114,111 @@ endfunction // do_ctz
|
|
|
module picorv32 #(
|
|
|
parameter integer COUNTER_CYCLE_WIDTH = 64,
|
|
|
parameter integer COUNTER_INSTR_WIDTH = 64,
|
|
|
- parameter [ 0:0] ENABLE_REGS_16_31 = 1,
|
|
|
- parameter [ 0:0] ENABLE_REGS_DUALPORT = 1,
|
|
|
- parameter [ 0:0] LATCHED_MEM_RDATA = 0,
|
|
|
- parameter [ 0:0] TWO_STAGE_SHIFT = 1,
|
|
|
- parameter [ 0:0] BARREL_SHIFTER = 0,
|
|
|
- parameter [ 0:0] TWO_CYCLE_COMPARE = 0,
|
|
|
- parameter [ 0:0] TWO_CYCLE_ALU = 0,
|
|
|
- parameter [ 0:0] COMPRESSED_ISA = 0,
|
|
|
- parameter [ 0:0] CATCH_MISALIGN = 1,
|
|
|
- parameter [ 0:0] CATCH_ILLINSN = 1,
|
|
|
- parameter [ 0:0] ENABLE_PCPI = 0,
|
|
|
- parameter [ 0:0] ENABLE_MUL = 0,
|
|
|
- parameter [ 0:0] ENABLE_FAST_MUL = 0,
|
|
|
- parameter [ 0:0] ENABLE_DIV = 0,
|
|
|
- parameter [ 0:0] ENABLE_IRQ = 0,
|
|
|
- parameter [ 0:0] ENABLE_IRQ_TIMER = 1,
|
|
|
- parameter [ 0:0] ENABLE_TRACE = 0,
|
|
|
- parameter [ 0:0] REGS_INIT_ZERO = 0,
|
|
|
- parameter [31:0] MASKED_IRQ = 32'h 0000_0000,
|
|
|
- parameter [31:0] LATCHED_IRQ = 32'h ffff_ffff,
|
|
|
- parameter [31:0] STACKADDR = 32'h ffff_ffff,
|
|
|
- parameter [ 4:0] MASK_IRQ_REG = ENABLE_IRQ_QREGS ? 27 : 4,
|
|
|
- parameter USER_CONTEXTS = 1,
|
|
|
- parameter [ 0:0] ENABLE_IRQ_QREGS = USER_CONTEXTS > 0
|
|
|
+ parameter [ 0:0] ENABLE_REGS_16_31 = 1,
|
|
|
+ parameter [ 0:0] ENABLE_REGS_DUALPORT = 1,
|
|
|
+ parameter [ 0:0] LATCHED_MEM_RDATA = 0,
|
|
|
+ parameter [ 0:0] TWO_STAGE_SHIFT = 1,
|
|
|
+ parameter [ 0:0] BARREL_SHIFTER = 0,
|
|
|
+ parameter [ 0:0] TWO_CYCLE_COMPARE = 0,
|
|
|
+ parameter [ 0:0] TWO_CYCLE_ALU = 0,
|
|
|
+ parameter [ 0:0] COMPRESSED_ISA = 0,
|
|
|
+ parameter [ 0:0] CATCH_MISALIGN = 1,
|
|
|
+ parameter [ 0:0] CATCH_ILLINSN = 1,
|
|
|
+ parameter [ 0:0] ENABLE_PCPI = 0,
|
|
|
+ parameter [ 0:0] ENABLE_MUL = 0,
|
|
|
+ parameter [ 0:0] ENABLE_FAST_MUL = 0,
|
|
|
+ parameter [ 0:0] ENABLE_DIV = 0,
|
|
|
+ parameter [ 0:0] ENABLE_IRQ = 0,
|
|
|
+ parameter [ 0:0] ENABLE_IRQ_TIMER = 1,
|
|
|
+ parameter [ 0:0] ENABLE_TRACE = 0,
|
|
|
+ parameter [ 0:0] REGS_INIT_ZERO = 0,
|
|
|
+ parameter [31:0] MASKED_IRQ = 32'h 0000_0000,
|
|
|
+ parameter [31:0] LATCHED_IRQ = 32'h ffff_ffff,
|
|
|
+ parameter [31:0] STACKADDR = 32'h ffff_ffff,
|
|
|
+ parameter [ 4:0] MASK_IRQ_REG = ENABLE_IRQ_QREGS ? 27 : 4,
|
|
|
+ parameter integer USER_CONTEXTS = 1,
|
|
|
+ parameter [ 0:0] ENABLE_IRQ_QREGS = USER_CONTEXTS > 0,
|
|
|
+
|
|
|
+ parameter integer context_bits = $clog2(USER_CONTEXTS + 1),
|
|
|
+ parameter integer context_max_bit = context_bits ? context_bits-1 : 0
|
|
|
) (
|
|
|
- input clk, resetn,
|
|
|
- input halt,
|
|
|
- output reg trap,
|
|
|
+ input clk, resetn,
|
|
|
+ input halt,
|
|
|
+ output reg trap,
|
|
|
|
|
|
- input [31:0] progaddr_reset,
|
|
|
- input [31:0] progaddr_irq,
|
|
|
+ input [31:0] progaddr_reset,
|
|
|
+ input [31:0] progaddr_irq,
|
|
|
|
|
|
- output reg mem_valid,
|
|
|
- output reg mem_instr,
|
|
|
- input mem_ready,
|
|
|
+ output reg mem_valid,
|
|
|
+ output reg mem_instr,
|
|
|
+ input mem_ready,
|
|
|
|
|
|
- output reg [31:0] mem_addr,
|
|
|
- output reg [31:0] mem_wdata,
|
|
|
- output reg [ 3:0] mem_wstrb,
|
|
|
- input [31:0] mem_rdata,
|
|
|
+ output reg [31:0] mem_addr,
|
|
|
+ output reg [31:0] mem_wdata,
|
|
|
+ output reg [ 3:0] mem_wstrb,
|
|
|
+ input [31:0] mem_rdata,
|
|
|
|
|
|
// Look-Ahead Interface
|
|
|
- output mem_la_read,
|
|
|
- output mem_la_write,
|
|
|
- output [31:0] mem_la_addr,
|
|
|
- output reg [31:0] mem_la_wdata,
|
|
|
- output reg [ 3:0] mem_la_wstrb,
|
|
|
+ output mem_la_read,
|
|
|
+ output mem_la_write,
|
|
|
+ output [31:0] mem_la_addr,
|
|
|
+ output reg [31:0] mem_la_wdata,
|
|
|
+ output reg [ 3:0] mem_la_wstrb,
|
|
|
|
|
|
// Pico Co-Processor Interface (PCPI)
|
|
|
- output reg pcpi_valid,
|
|
|
- output reg [31:0] pcpi_insn,
|
|
|
- output [31:0] pcpi_rs1,
|
|
|
- output [31:0] pcpi_rs2,
|
|
|
- input pcpi_wr,
|
|
|
- input [31:0] pcpi_rd,
|
|
|
- input pcpi_wait,
|
|
|
- input pcpi_ready,
|
|
|
+ output reg pcpi_valid,
|
|
|
+ output reg [31:0] pcpi_insn,
|
|
|
+ output [31:0] pcpi_rs1,
|
|
|
+ output [31:0] pcpi_rs2,
|
|
|
+ input pcpi_wr,
|
|
|
+ input [31:0] pcpi_rd,
|
|
|
+ input pcpi_wait,
|
|
|
+ input pcpi_ready,
|
|
|
|
|
|
// IRQ Interface
|
|
|
- input [31:0] irq,
|
|
|
- output reg [31:0] eoi,
|
|
|
+ input [31:0] irq,
|
|
|
+ output reg [31:0] eoi,
|
|
|
+
|
|
|
+ // user_context export
|
|
|
+ output reg [context_max_bit:0] user_context,
|
|
|
|
|
|
`ifdef RISCV_FORMAL
|
|
|
- output reg rvfi_valid,
|
|
|
- output reg [63:0] rvfi_order,
|
|
|
- output reg [31:0] rvfi_insn,
|
|
|
- output reg rvfi_trap,
|
|
|
- output reg rvfi_halt,
|
|
|
- output reg rvfi_intr,
|
|
|
- output reg [ 1:0] rvfi_mode,
|
|
|
- output reg [ 1:0] rvfi_ixl,
|
|
|
- output reg [ 4:0] rvfi_rs1_addr,
|
|
|
- output reg [ 4:0] rvfi_rs2_addr,
|
|
|
- output reg [31:0] rvfi_rs1_rdata,
|
|
|
- output reg [31:0] rvfi_rs2_rdata,
|
|
|
- output reg [ 4:0] rvfi_rd_addr,
|
|
|
- output reg [31:0] rvfi_rd_wdata,
|
|
|
- output reg [31:0] rvfi_pc_rdata,
|
|
|
- output reg [31:0] rvfi_pc_wdata,
|
|
|
- output reg [31:0] rvfi_mem_addr,
|
|
|
- output reg [ 3:0] rvfi_mem_rmask,
|
|
|
- output reg [ 3:0] rvfi_mem_wmask,
|
|
|
- output reg [31:0] rvfi_mem_rdata,
|
|
|
- output reg [31:0] rvfi_mem_wdata,
|
|
|
-
|
|
|
- output reg [63:0] rvfi_csr_mcycle_rmask,
|
|
|
- output reg [63:0] rvfi_csr_mcycle_wmask,
|
|
|
- output reg [63:0] rvfi_csr_mcycle_rdata,
|
|
|
- output reg [63:0] rvfi_csr_mcycle_wdata,
|
|
|
-
|
|
|
- output reg [63:0] rvfi_csr_minstret_rmask,
|
|
|
- output reg [63:0] rvfi_csr_minstret_wmask,
|
|
|
- output reg [63:0] rvfi_csr_minstret_rdata,
|
|
|
- output reg [63:0] rvfi_csr_minstret_wdata,
|
|
|
+ output reg rvfi_valid,
|
|
|
+ output reg [63:0] rvfi_order,
|
|
|
+ output reg [31:0] rvfi_insn,
|
|
|
+ output reg rvfi_trap,
|
|
|
+ output reg rvfi_halt,
|
|
|
+ output reg rvfi_intr,
|
|
|
+ output reg [ 1:0] rvfi_mode,
|
|
|
+ output reg [ 1:0] rvfi_ixl,
|
|
|
+ output reg [ 4:0] rvfi_rs1_addr,
|
|
|
+ output reg [ 4:0] rvfi_rs2_addr,
|
|
|
+ output reg [31:0] rvfi_rs1_rdata,
|
|
|
+ output reg [31:0] rvfi_rs2_rdata,
|
|
|
+ output reg [ 4:0] rvfi_rd_addr,
|
|
|
+ output reg [31:0] rvfi_rd_wdata,
|
|
|
+ output reg [31:0] rvfi_pc_rdata,
|
|
|
+ output reg [31:0] rvfi_pc_wdata,
|
|
|
+ output reg [31:0] rvfi_mem_addr,
|
|
|
+ output reg [ 3:0] rvfi_mem_rmask,
|
|
|
+ output reg [ 3:0] rvfi_mem_wmask,
|
|
|
+ output reg [31:0] rvfi_mem_rdata,
|
|
|
+ output reg [31:0] rvfi_mem_wdata,
|
|
|
+
|
|
|
+ output reg [63:0] rvfi_csr_mcycle_rmask,
|
|
|
+ output reg [63:0] rvfi_csr_mcycle_wmask,
|
|
|
+ output reg [63:0] rvfi_csr_mcycle_rdata,
|
|
|
+ output reg [63:0] rvfi_csr_mcycle_wdata,
|
|
|
+
|
|
|
+ output reg [63:0] rvfi_csr_minstret_rmask,
|
|
|
+ output reg [63:0] rvfi_csr_minstret_wmask,
|
|
|
+ output reg [63:0] rvfi_csr_minstret_rdata,
|
|
|
+ output reg [63:0] rvfi_csr_minstret_wdata,
|
|
|
`endif
|
|
|
|
|
|
// Trace Interface
|
|
|
- output reg trace_valid,
|
|
|
- output reg [35:0] trace_data
|
|
|
+ output reg trace_valid,
|
|
|
+ output reg [35:0] trace_data
|
|
|
);
|
|
|
localparam integer irq_timer = 0;
|
|
|
localparam integer irq_ebreak = 1;
|
|
@@ -213,14 +227,11 @@ module picorv32 #(
|
|
|
localparam integer xreg_count = ENABLE_REGS_16_31 ? 32 : 16;
|
|
|
localparam integer xreg_bits = $clog2(xreg_count);
|
|
|
localparam integer xreg_banks = USER_CONTEXTS + 1;
|
|
|
- localparam integer context_bits = $clog2(xreg_banks);
|
|
|
localparam integer regfile_size = xreg_count * xreg_banks;
|
|
|
localparam integer regfile_bits = $clog2(regfile_size);
|
|
|
wire [regfile_bits-1:0] xreg_mask = xreg_count - 1;
|
|
|
|
|
|
- reg [context_bits-1:0] user_context;
|
|
|
-
|
|
|
- wire [regfile_bits-1:0] xreg_offset;
|
|
|
+ wire [regfile_bits-1:0] xreg_offset;
|
|
|
assign xreg_offset[regfile_bits-1:xreg_bits] = irq_active ? 0 : user_context;
|
|
|
assign xreg_offset[xreg_bits-1:0] = 0;
|
|
|
|
|
@@ -445,7 +456,7 @@ module picorv32 #(
|
|
|
assign mem_la_write = resetn && !mem_state && mem_do_wdata;
|
|
|
assign mem_la_read = resetn && ((!mem_la_use_prefetched_high_word && !mem_state && (mem_do_rinst || mem_do_prefetch || mem_do_rdata)) ||
|
|
|
(COMPRESSED_ISA && mem_xfer && (!last_mem_valid ? mem_la_firstword : mem_la_firstword_reg) && !mem_la_secondword && &mem_rdata_latched[1:0]));
|
|
|
- assign mem_la_addr = (mem_do_prefetch || mem_do_rinst) ? {next_pc[31:2] + mem_la_firstword_xfer, 2'b00} : {reg_op1[31:2], 2'b00};
|
|
|
+ assign mem_la_addr = (mem_do_prefetch || mem_do_rinst) ? {next_pc[31:2] + mem_la_firstword_xfer, 2'b00} : reg_op1;
|
|
|
|
|
|
assign mem_rdata_latched_noshuffle = (mem_xfer || LATCHED_MEM_RDATA) ? mem_rdata : mem_rdata_q;
|
|
|
|
|
@@ -1748,13 +1759,14 @@ module picorv32 #(
|
|
|
// Bitops not supported ATM, treat as readonly
|
|
|
if (~instr_funct2[1])
|
|
|
case (decoded_imm[11:0])
|
|
|
- 12'h341: begin // mepc
|
|
|
+ 12'h341: if (ENABLE_IRQ) begin // mepc
|
|
|
reg_mepc <= csrr_src;
|
|
|
end
|
|
|
- 12'h7f0: begin // user_context
|
|
|
- user_context <= csrr_src;
|
|
|
- irq_active <= 1'b1;
|
|
|
- end
|
|
|
+ 12'h7f0:
|
|
|
+ if (USER_CONTEXTS > 0) begin
|
|
|
+ user_context <= csrr_src;
|
|
|
+ irq_active <= 1'b1;
|
|
|
+ end
|
|
|
default: begin
|
|
|
// Do nothing
|
|
|
end
|