|
@@ -15,14 +15,15 @@
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
*
|
|
|
- * Changes by hpa 2021-2022:
|
|
|
+ * Changes by hpa 2021-2023:
|
|
|
* - maskirq instruction takes a mask in rs2.
|
|
|
- * - retirq opcode changed to mret; no functional change.
|
|
|
+ * - 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.
|
|
|
* - 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).
|
|
|
* - PROGADDR_RESET and PROGADDR_IRQ changed to ports (allows external
|
|
|
* implementation of vectorized interrupts or fallback reset.)
|
|
@@ -30,8 +31,23 @@
|
|
|
* - 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;
|
|
|
- *
|
|
|
+ * 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
|
|
|
+ * globally available at interrupt time. This simplifies context switching.
|
|
|
+ * - 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
|
|
|
+ * 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;
|
|
|
+ * they can be independently set to any value from 0 to 64 bits.
|
|
|
*/
|
|
|
|
|
|
/* verilator lint_off WIDTH */
|
|
@@ -88,8 +104,8 @@ endfunction // do_ctz
|
|
|
***************************************************************/
|
|
|
|
|
|
module picorv32 #(
|
|
|
- parameter [ 0:0] ENABLE_COUNTERS = 1,
|
|
|
- parameter [ 0:0] ENABLE_COUNTERS64 = 1,
|
|
|
+ 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,
|
|
@@ -111,9 +127,8 @@ module picorv32 #(
|
|
|
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] RA_IRQ_REG = ENABLE_IRQ_QREGS ? 26 : 3,
|
|
|
- parameter [ 4:0] MASK_IRQ_REG = ENABLE_IRQ_QREGS ? 27 : 4,
|
|
|
- parameter USER_CONTEXTS = 1,
|
|
|
+ parameter [ 4:0] MASK_IRQ_REG = ENABLE_IRQ_QREGS ? 27 : 4,
|
|
|
+ parameter USER_CONTEXTS = 1,
|
|
|
parameter [ 0:0] ENABLE_IRQ_QREGS = USER_CONTEXTS > 0
|
|
|
) (
|
|
|
input clk, resetn,
|
|
@@ -121,7 +136,7 @@ module picorv32 #(
|
|
|
output reg trap,
|
|
|
|
|
|
input [31:0] progaddr_reset,
|
|
|
- input [31:0] progaddr_irq,
|
|
|
+ input [31:0] progaddr_irq,
|
|
|
|
|
|
output reg mem_valid,
|
|
|
output reg mem_instr,
|
|
@@ -215,8 +230,12 @@ module picorv32 #(
|
|
|
localparam [35:0] TRACE_ADDR = {4'b 0010, 32'b 0};
|
|
|
localparam [35:0] TRACE_IRQ = {4'b 1000, 32'b 0};
|
|
|
|
|
|
- reg [63:0] count_cycle, count_instr;
|
|
|
- reg [31:0] reg_pc, reg_next_pc, reg_op1, reg_op2, reg_out;
|
|
|
+ reg [63:0] count_cycle;
|
|
|
+ localparam [63:0] count_cycle_mask = (1'b1 << COUNTER_CYCLE_WIDTH) - 1'b1;
|
|
|
+ reg [63:0] count_instr;
|
|
|
+ localparam [63:0] count_instr_mask = (1'b1 << COUNTER_INSTR_WIDTH) - 1'b1;
|
|
|
+
|
|
|
+ reg [31:0] reg_pc, reg_next_pc, reg_mepc, reg_op1, reg_op2, reg_out;
|
|
|
reg [4:0] reg_sh;
|
|
|
|
|
|
reg [31:0] next_insn_opcode;
|
|
@@ -695,7 +714,7 @@ module picorv32 #(
|
|
|
reg instr_addi, instr_slti, instr_sltiu, instr_xori, instr_ori, instr_andi, instr_slli, instr_srli, instr_srai;
|
|
|
reg instr_add, instr_sub, instr_sll, instr_slt, instr_sltu, instr_xor, instr_srl, instr_sra, instr_or, instr_and;
|
|
|
reg instr_csrr, instr_ecall_ebreak;
|
|
|
- reg instr_addqxi, instr_addxqi, instr_retirq, instr_maskirq, instr_waitirq, instr_timer;
|
|
|
+ reg instr_addqxi, instr_addxqi, instr_retirq, instr_maskirq, instr_waitirq, instr_timer, instr_pollirq;
|
|
|
reg instr_ctz;
|
|
|
reg [2:0] instr_funct2;
|
|
|
|
|
@@ -730,7 +749,7 @@ module picorv32 #(
|
|
|
instr_lb, instr_lh, instr_lw, instr_lbu, instr_lhu, instr_sb, instr_sh, instr_sw,
|
|
|
instr_addi, instr_slti, instr_sltiu, instr_xori, instr_ori, instr_andi, instr_slli, instr_srli, instr_srai,
|
|
|
instr_add, instr_sub, instr_sll, instr_slt, instr_sltu, instr_xor, instr_srl, instr_sra, instr_or, instr_and,
|
|
|
- instr_csrr, instr_addqxi, instr_retirq, instr_maskirq, instr_waitirq, instr_timer, instr_ctz};
|
|
|
+ instr_csrr, instr_addqxi, instr_retirq, instr_maskirq, instr_waitirq, instr_timer, instr_pollirq, instr_ctz};
|
|
|
|
|
|
reg [63:0] new_ascii_instr;
|
|
|
`FORMAL_KEEP reg [63:0] dbg_ascii_instr;
|
|
@@ -793,10 +812,11 @@ module picorv32 #(
|
|
|
|
|
|
if (instr_addqxi) new_ascii_instr = "addqxi";
|
|
|
if (instr_addxqi) new_ascii_instr = "addxqi";
|
|
|
- if (instr_retirq) new_ascii_instr = "retirq";
|
|
|
+ if (instr_retirq) new_ascii_instr = "mret";
|
|
|
if (instr_maskirq) new_ascii_instr = "maskirq";
|
|
|
if (instr_waitirq) new_ascii_instr = "waitirq";
|
|
|
if (instr_timer) new_ascii_instr = "timer";
|
|
|
+ if (instr_pollirq) new_ascii_instr = "pollirq";
|
|
|
end
|
|
|
|
|
|
reg [63:0] q_ascii_instr;
|
|
@@ -929,9 +949,6 @@ module picorv32 #(
|
|
|
decoded_rs1 <= mem_rdata_latched[19:15];
|
|
|
decoded_rs2 <= mem_rdata_latched[24:20];
|
|
|
|
|
|
- if (instr_la_retirq)
|
|
|
- decoded_rs1 <= RA_IRQ_REG;
|
|
|
-
|
|
|
compressed_instr <= 0;
|
|
|
if (COMPRESSED_ISA && mem_rdata_latched[1:0] != 2'b11) begin
|
|
|
compressed_instr <= 1;
|
|
@@ -1148,6 +1165,8 @@ module picorv32 #(
|
|
|
instr_maskirq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[14:12] == 3'b000 && mem_rdata_q[31:25] == 7'b0000011 && ENABLE_IRQ;
|
|
|
instr_waitirq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[14:12] == 3'b000 && mem_rdata_q[31:25] == 7'b0000100 && ENABLE_IRQ;
|
|
|
instr_timer <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[14:12] == 3'b000 && mem_rdata_q[31:25] == 7'b0000101 && ENABLE_IRQ && ENABLE_IRQ_TIMER;
|
|
|
+ instr_pollirq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[14:12] == 3'b000 && mem_rdata_q[31:25] == 7'b0000110 && ENABLE_IRQ;
|
|
|
+
|
|
|
|
|
|
// instr_addqxi includes addxqi; instr_addxqi is only used for debug
|
|
|
instr_addqxi <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[14:13] == 2'b01 && ENABLE_IRQ && ENABLE_IRQ_QREGS;
|
|
@@ -1230,6 +1249,7 @@ module picorv32 #(
|
|
|
instr_addxqi <= 0;
|
|
|
instr_maskirq <= 0;
|
|
|
instr_waitirq <= 0;
|
|
|
+ instr_pollirq <= 0;
|
|
|
instr_timer <= 0;
|
|
|
|
|
|
instr_ecall_ebreak <= 0;
|
|
@@ -1394,10 +1414,6 @@ module picorv32 #(
|
|
|
cpuregs_wrdata = latched_stalu ? alu_out_q : reg_out;
|
|
|
cpuregs_write = 1;
|
|
|
end
|
|
|
- ENABLE_IRQ && irq_state[0]: begin
|
|
|
- cpuregs_wrdata = reg_next_pc | latched_compr;
|
|
|
- cpuregs_write = 1;
|
|
|
- end
|
|
|
ENABLE_IRQ && irq_state[1]: begin
|
|
|
cpuregs_wrdata = irq_pending & ~irq_mask;
|
|
|
cpuregs_write = 1;
|
|
@@ -1516,14 +1532,6 @@ module picorv32 #(
|
|
|
pcpi_timeout <= !pcpi_timeout_counter;
|
|
|
end
|
|
|
|
|
|
- if (ENABLE_COUNTERS) begin
|
|
|
- count_cycle <= resetn ? count_cycle + 1 : 0;
|
|
|
- if (!ENABLE_COUNTERS64) count_cycle[63:32] <= 0;
|
|
|
- end else begin
|
|
|
- count_cycle <= 'bx;
|
|
|
- count_instr <= 'bx;
|
|
|
- end
|
|
|
-
|
|
|
next_irq_pending = ENABLE_IRQ ? (irq_pending & LATCHED_IRQ & ~MASKED_IRQ) : 'bx;
|
|
|
|
|
|
if (ENABLE_IRQ && ENABLE_IRQ_TIMER && timer) begin
|
|
@@ -1541,11 +1549,16 @@ module picorv32 #(
|
|
|
if (!ENABLE_TRACE)
|
|
|
trace_data <= 'bx;
|
|
|
|
|
|
+ if (!resetn)
|
|
|
+ count_cycle <= 0;
|
|
|
+ else
|
|
|
+ count_cycle <= (count_cycle + 1'b1) & count_cycle_mask;
|
|
|
+
|
|
|
if (!resetn) begin
|
|
|
reg_pc <= progaddr_reset;
|
|
|
reg_next_pc <= progaddr_reset;
|
|
|
- if (ENABLE_COUNTERS)
|
|
|
- count_instr <= 0;
|
|
|
+ reg_mepc <= 0;
|
|
|
+ count_instr <= 0;
|
|
|
latched_store <= 0;
|
|
|
latched_stalu <= 0;
|
|
|
latched_branch <= 0;
|
|
@@ -1569,7 +1582,7 @@ module picorv32 #(
|
|
|
reg_out <= STACKADDR;
|
|
|
end
|
|
|
cpu_state <= cpu_state_fetch;
|
|
|
- end else
|
|
|
+ end else // if (!resetn)
|
|
|
(* parallel_case, full_case *)
|
|
|
case (cpu_state)
|
|
|
cpu_state_trap: begin
|
|
@@ -1592,7 +1605,7 @@ module picorv32 #(
|
|
|
`debug($display("ST_RD: %2d 0x%08x", latched_rd, latched_stalu ? alu_out_q : reg_out);)
|
|
|
end
|
|
|
ENABLE_IRQ && irq_state[0]: begin
|
|
|
- current_pc = progaddr_irq;
|
|
|
+ current_pc = progaddr_irq & ~1;
|
|
|
irq_active <= 1;
|
|
|
mem_do_rinst <= 1;
|
|
|
end
|
|
@@ -1631,7 +1644,8 @@ module picorv32 #(
|
|
|
irq_state == 2'b00 ? 2'b01 :
|
|
|
irq_state == 2'b01 ? 2'b10 : 2'b00;
|
|
|
latched_compr <= latched_compr;
|
|
|
- latched_rd <= irq_state[0] ? MASK_IRQ_REG : RA_IRQ_REG;
|
|
|
+ latched_rd <= MASK_IRQ_REG;
|
|
|
+ reg_mepc <= reg_next_pc | latched_compr;
|
|
|
end else
|
|
|
if (ENABLE_IRQ && do_waitirq) begin
|
|
|
if (&(irq_pending | ~reg_op1) || |(irq_pending & reg_op2)) begin
|
|
@@ -1654,10 +1668,7 @@ module picorv32 #(
|
|
|
reg_next_pc <= current_pc + (compressed_instr ? 2 : 4);
|
|
|
if (ENABLE_TRACE)
|
|
|
latched_trace <= 1;
|
|
|
- if (ENABLE_COUNTERS) begin
|
|
|
- count_instr <= count_instr + 1;
|
|
|
- if (!ENABLE_COUNTERS64) count_instr[63:32] <= 0;
|
|
|
- end
|
|
|
+ count_instr <= (count_instr + 1'b1) & count_instr_mask;
|
|
|
if (instr_jal) begin
|
|
|
mem_do_rinst <= 1;
|
|
|
reg_next_pc <= current_pc + decoded_imm_j;
|
|
@@ -1722,13 +1733,15 @@ module picorv32 #(
|
|
|
reg_out <= 32'bx;
|
|
|
case (decoded_imm[11:0])
|
|
|
12'hc00, 12'hc01: // cycle, time
|
|
|
- if (ENABLE_COUNTERS) reg_out <= count_cycle[31:0];
|
|
|
+ reg_out <= count_cycle[31:0];
|
|
|
12'hc80, 12'hc81: // cycleh, timeh
|
|
|
- if (ENABLE_COUNTERS64) reg_out <= count_cycle[63:32];
|
|
|
+ reg_out <= count_cycle[63:32];
|
|
|
12'hc02: // instret (rdinstr)
|
|
|
- if (ENABLE_COUNTERS) reg_out <= count_instr[31:0];
|
|
|
+ reg_out <= count_instr[31:0];
|
|
|
12'hc82: // instret (rdinstr)
|
|
|
- if (ENABLE_COUNTERS64) reg_out <= count_instr[63:32];
|
|
|
+ reg_out <= count_instr[63:32];
|
|
|
+ 12'h341: // mepc
|
|
|
+ if (ENABLE_IRQ) reg_out <= reg_mepc;
|
|
|
12'h343: // mtval
|
|
|
if (CATCH_MISALIGN) reg_out <= buserr_address;
|
|
|
12'h7f0: // user_context
|
|
@@ -1740,6 +1753,9 @@ module picorv32 #(
|
|
|
// Bitops not supported ATM, treat as readonly
|
|
|
if (~instr_funct2[1])
|
|
|
case (decoded_imm[11:0])
|
|
|
+ 12'h341: begin // mepc
|
|
|
+ reg_mepc <= csrr_src;
|
|
|
+ end
|
|
|
12'h7f0: begin // user_context
|
|
|
user_context <= csrr_src;
|
|
|
irq_active <= 1'b1;
|
|
@@ -1766,9 +1782,9 @@ module picorv32 #(
|
|
|
irq_active <= 0;
|
|
|
latched_branch <= 1;
|
|
|
latched_store <= 1;
|
|
|
- `debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);)
|
|
|
- reg_out <= CATCH_MISALIGN ? (cpuregs_rs1 & 32'h fffffffe) : cpuregs_rs1;
|
|
|
- dbg_rs1val <= cpuregs_rs1;
|
|
|
+ `debug($display("MRET: 0x%08x", reg_mepc);)
|
|
|
+ reg_out <= reg_mepc & ~1;
|
|
|
+ dbg_rs1val <= reg_mepc;
|
|
|
dbg_rs1val_valid <= 1;
|
|
|
cpu_state <= cpu_state_fetch;
|
|
|
end
|
|
@@ -1806,6 +1822,12 @@ module picorv32 #(
|
|
|
dbg_rs1val_valid <= 1;
|
|
|
cpu_state <= cpu_state_fetch;
|
|
|
end
|
|
|
+ ENABLE_IRQ && instr_pollirq: begin
|
|
|
+ latched_store <= 1;
|
|
|
+ reg_out <= (irq_pending & ~irq_mask & ~cpuregs_rs1) | cpuregs_rs2;
|
|
|
+ eoi <= irq_pending & ~irq_mask & ~cpuregs_rs1;
|
|
|
+ next_irq_pending = next_irq_pending & (irq_mask | cpuregs_rs1);
|
|
|
+ end
|
|
|
is_lb_lh_lw_lbu_lhu && !instr_trap: begin
|
|
|
`debug($display("LD_RS1: %2d 0x%08x", decoded_rs1, cpuregs_rs1);)
|
|
|
reg_op1 <= cpuregs_rs1;
|