From 23b700cf73b7b01b4737db106226bd4220799290 Mon Sep 17 00:00:00 2001 From: Clifford Wolf Date: Thu, 25 Jun 2015 14:08:39 +0200 Subject: [PATCH] Added basic IRQ support --- .gitignore | 2 + Makefile | 7 +- README.md | 144 ++++++++++++++++++++++++++++++++++++++++- firmware/sieve.c | 101 +++++++++++++++++++++++++++++ firmware/start.S | 163 ++++++++++++++++++++++++++++++++++++++++++++++- picorv32.v | 140 ++++++++++++++++++++++++++++++++++++---- testbench.v | 8 ++- 7 files changed, 546 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 528f8cb..3dcece6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /tests/*.o +/firmware/start.o /firmware/firmware.bin /firmware/firmware.elf /firmware/firmware.hex @@ -16,3 +17,4 @@ /testbench.exe /testbench_axi.exe /testbench.vcd +.*.swp diff --git a/Makefile b/Makefile index cd610ae..cf981e8 100644 --- a/Makefile +++ b/Makefile @@ -22,12 +22,15 @@ firmware/firmware.bin: firmware/firmware.elf riscv64-unknown-elf-objcopy -O binary $< $@ chmod -x $@ -firmware/firmware.elf: $(TEST_OBJS) firmware/sections.lds firmware/start.S firmware/sieve.c firmware/stats.c +firmware/firmware.elf: $(TEST_OBJS) firmware/sections.lds firmware/start.o firmware/sieve.c firmware/stats.c riscv64-unknown-elf-gcc -Os -m32 -march=RV32I -ffreestanding -nostdlib -o $@ \ -Wl,-Bstatic,-T,firmware/sections.lds,-Map,firmware/firmware.map,--strip-debug \ - firmware/start.S firmware/sieve.c firmware/stats.c $(TEST_OBJS) -lgcc + firmware/start.o firmware/sieve.c firmware/stats.c $(TEST_OBJS) -lgcc chmod -x $@ +firmware/start.o: firmware/start.S + riscv64-unknown-elf-gcc -c -m32 -o $@ $< + tests/%.o: tests/%.S tests/riscv_test.h tests/test_macros.h riscv64-unknown-elf-gcc -m32 -march=RV32I -c -o $@ -DTEST_FUNC_NAME=$(notdir $(basename $<)) \ -DTEST_FUNC_TXT='"$(notdir $(basename $<))"' -DTEST_FUNC_RET=$(notdir $(basename $<))_ret $< diff --git a/README.md b/README.md index 486b50d..47c9f1b 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,27 @@ transaction. In the default configuration the PicoRV32 core only expects the `mem_rdata` input to be valid in the cycle with `mem_valid && mem_ready` and latches the value internally. +### ENABLE_EXTERNAL_IRQ (default = 0) + +Set this to 1 to enable external IRQs. + +### ENABLE_ILLINSTR_IRQ (default = 0) + +Set this to 1 to enable the illigal instruction IRQ. This can be used for +software implementations of instructions such as `MUL` and `DIV`. + +### ENABLE_TIMER_IRQ (default = 0) + +Set this to 1 to enable the built-in timer and timer IRQ. + +### PROGADDR_RESET (default = 0) + +The start address of the program. + +### PROGADDR_IRQ (default = 16) + +The start address of the interrupt handler. + Performance: ------------ @@ -115,10 +136,131 @@ Dhrystone benchmark results: 0.309 DMIPS/MHz (544 Dhrystones/Second/MHz) For the Dhrystone benchmark the average CPI is 4.167. +Custom Instructions: +-------------------- + +### IRQ Handling + +The following custom instructions are supported when IRQs are enabled. + +The core has 4 additional 32-bit general-purpose registers `q0 .. q3` +that are used for IRQ handling. When an IRQ triggers, the register +`q0` contains the return address and `q1` contains the IRQ number. +Registers `q2` and `q3` are uninitialized. + +#### getq rd, qs + +This instruction copies the value from a q-register to a general-purpose +register. The Instruction is encoded under the `custom0` opcode: + + 0000000 00000 000XX 000 XXXXX 0001011 + f7 f5 qs f3 rd opcode + +Example assember code using the `custom0` mnemonic: + +| Instruction | Assember Code | +| ------------------| --------------------| +| getq x5, q2 | custom0 5, 2, 0, 0 | +| getq x3, q0 | custom0 3, 0, 0, 0 | +| getq x1, q3 | custom0 1, 3, 0, 0 | + +#### setq qd, rs + +This instruction copies the value from a general-purpose register to a +q-register. The Instruction is encoded under the `custom0` opcode: + + 0000001 00000 XXXXX 000 000XX 0001011 + f7 f5 rs f3 qd opcode + +Example assember code using the `custom0` mnemonic: + +| Instruction | Assember Code | +| ------------------| --------------------| +| setq q2, x5 | custom0 2, 5, 0, 1 | +| setq q0, x3 | custom0 0, 3, 0, 1 | +| setq q3, x1 | custom0 3, 1, 0, 1 | + +#### retirq + +Return from interrupt. This instruction copies the value from `q0` +to the program counter and enables interrupts. The Instruction is +encoded under the `custom0` opcode: + + 0000010 00000 00000 000 00000 0001011 + f7 f5 rs f3 rd opcode + +Example assember code using the `custom0` mnemonic: + +| Instruction | Assember Code | +| ------------------| --------------------| +| retirq | custom0 0, 0, 0, 2 | + +#### maskirq + +Enable/disable interrupt sources. The Instruction is encoded under the +`custom0` opcode: + + 0000011 XXXXX 00000 000 00000 0001011 + f7 f5 rs f3 rd opcode + +The following interrupt sources occupy the following bits +in the `f5` field: + +| Bit | Interrupt Source | +| ------| ---------------------| +| f5[0] | External IRQ | +| f5[1] | Timer Interrupt | +| f5[2] | Illegal Instruction | +| f5[3] | Reserved | +| f5[4] | Reserved | + +Set bits in the IRQ mask correspond to enabled interrupt sources. + +Example assember code using the `custom0` mnemonic: + +| Instruction | Assember Code | +| ------------------| --------------------| +| maskirq 0 | custom0 0, 0, 0, 3 | +| maskirq 1 | custom0 0, 0, 1, 3 | + +The processor starts with all interrupts disabled. + +An illegal instruction while the illegal instruction interrupt is disabled will +cause the processor to halt. + +#### waitirq (unimplemented) + +Pause execution until an interrupt triggers. The Instruction is encoded under the +`custom0` opcode: + + 0000100 00000 00000 000 00000 0001011 + f7 f5 rs f3 rd opcode + +Example assember code using the `custom0` mnemonic: + +| Instruction | Assember Code | +| ------------------| --------------------| +| waitirq | custom0 0, 0, 0, 4 | + +#### timer + +Reset the timer counter to a new value. The counter counts down cycles and +triggers the timer interrupt when transitioning from 1 to 0. Setting the +counter to zero disables the timer. + + 0000101 00000 XXXXX 000 00000 0001011 + f7 f5 rs f3 rd opcode + +Example assember code using the `custom0` mnemonic: + +| Instruction | Assember Code | +| ------------------| --------------------| +| timer x2 | custom0 0, 2, 0, 5 | + + Todos: ------ -- Optional IRQ support - Optional FENCE support - Optional write-through cache - Optional support for compressed ISA diff --git a/firmware/sieve.c b/firmware/sieve.c index c3372f4..7f1c82e 100644 --- a/firmware/sieve.c +++ b/firmware/sieve.c @@ -18,6 +18,11 @@ static bool bitmap_get(int idx) return (bitmap[idx/32] & (1 << (idx % 32))) != 0; } +static void print_chr(char ch) +{ + *((volatile uint32_t*)OUTPORT) = ch; +} + static void print_str(const char *p) { while (*p != 0) @@ -37,6 +42,13 @@ static void print_dec(int val) } } +static void print_hex(unsigned int val) +{ + int i; + for (i = 32-4; i >= 0; i -= 4) + *((volatile uint32_t*)OUTPORT) = "0123456789ABCDEF"[(val >> i) % 16]; +} + static void print_prime(int idx, int val) { if (idx < 10) @@ -76,3 +88,92 @@ void sieve() } } +void irq(uint32_t *regs, uint32_t irqnum) +{ + static int ext_irq_count = 0; + static int timer_irq_count = 0; + + if (irqnum == 0) { + ext_irq_count++; + // print_str("[EXT-IRQ]"); + return; + } + + if (irqnum == 1) { + timer_irq_count++; + // print_str("[TIMER-IRQ]"); + return; + } + + if (irqnum == 2) + { + int i, k; + uint32_t pc = regs[0] - 4; + uint32_t instr = *(uint32_t*)pc; + + print_str("\n"); + print_str("------------------------------------------------------------\n"); + + if (instr == 0x00100073) { + print_str("SBREAK instruction at 0x"); + print_hex(pc); + print_str("\n"); + } else { + print_str("Illegal Instruction at 0x"); + print_hex(pc); + print_str(": 0x"); + print_hex(instr); + print_str("\n"); + } + + for (i = 0; i < 8; i++) + for (k = 0; k < 4; k++) + { + int r = i + k*8; + + if (r == 0) { + print_str("pc "); + } else + if (r < 10) { + print_chr('x'); + print_chr('0' + r); + print_chr(' '); + print_chr(' '); + } else + if (r < 20) { + print_chr('x'); + print_chr('1'); + print_chr('0' + r - 10); + print_chr(' '); + } else + if (r < 30) { + print_chr('x'); + print_chr('2'); + print_chr('0' + r - 20); + print_chr(' '); + } else { + print_chr('x'); + print_chr('3'); + print_chr('0' + r - 30); + print_chr(' '); + } + + print_hex(regs[r]); + print_str(k == 3 ? "\n" : " "); + } + + print_str("------------------------------------------------------------\n"); + + print_str("Number of external IRQs counted: "); + print_dec(ext_irq_count); + print_str("\n"); + + print_str("Number of timer IRQs counted: "); + print_dec(timer_irq_count); + print_str("\n"); + + __asm__("sbreak"); + return; + } +} + diff --git a/firmware/start.S b/firmware/start.S index d9e0b3e..cdb8da0 100644 --- a/firmware/start.S +++ b/firmware/start.S @@ -1,13 +1,174 @@ .section .text - .global start + .global irq .global sieve .global stats #define TEST(n) \ .global n; .global n ## _ret; \ + addi x1, zero, 1000; \ + custom0 0,1,0,5; /* timer x1 */ \ jal zero,n; n ## _ret: +reset_vec: + custom0 0,0,7,3 // maskirq 7 + j start + nop + nop + +irq_vec: + /* save registers */ + + custom0 2,1,0,1 // setq q2, x1 + custom0 3,2,0,1 // setq q3, x2 + + lui x1, %hi(irq_regs) + addi x1, x1, %lo(irq_regs) + + custom0 2,0,0,0 // getq x2, q0 + sw x2, 0*4(x1) + + custom0 2,2,0,0 // getq x2, q2 + sw x2, 1*4(x1) + + custom0 2,3,0,0 // getq x2, q3 + sw x2, 2*4(x1) + + sw x3, 3*4(x1) + sw x4, 4*4(x1) + sw x5, 5*4(x1) + sw x6, 6*4(x1) + sw x7, 7*4(x1) + sw x8, 8*4(x1) + sw x9, 9*4(x1) + sw x10, 10*4(x1) + sw x11, 11*4(x1) + sw x12, 12*4(x1) + sw x13, 13*4(x1) + sw x14, 14*4(x1) + sw x15, 15*4(x1) + sw x16, 16*4(x1) + sw x17, 17*4(x1) + sw x18, 18*4(x1) + sw x19, 19*4(x1) + sw x20, 20*4(x1) + sw x21, 21*4(x1) + sw x22, 22*4(x1) + sw x23, 23*4(x1) + sw x24, 24*4(x1) + sw x25, 25*4(x1) + sw x26, 26*4(x1) + sw x27, 27*4(x1) + sw x28, 28*4(x1) + sw x29, 29*4(x1) + sw x30, 30*4(x1) + sw x31, 31*4(x1) + + /* call interrupt handler */ + + lui sp, %hi(irq_stack_top) + addi sp, sp, %lo(irq_stack_top) + + // arg0 = address of regs + lui a0, %hi(irq_regs) + addi a0, a0, %lo(irq_regs) + + // arg1 = interrupt type + custom0 11,1,0,0 // getq x11, q1 + + // call to c function + jal ra, irq + + /* restore registers */ + + lui x1, %hi(irq_regs) + addi x1, x1, %lo(irq_regs) + + lw x2, 0*4(x1) + custom0 0,2,0,1 // setq q0, x2 + + lw x2, 1*4(x1) + custom0 1,2,0,1 // setq q1, x2 + + lw x2, 2*4(x1) + custom0 2,2,0,1 // setq q2, x2 + + lw x3, 3*4(x1) + lw x4, 4*4(x1) + lw x5, 5*4(x1) + lw x6, 6*4(x1) + lw x7, 7*4(x1) + lw x8, 8*4(x1) + lw x9, 9*4(x1) + lw x10, 10*4(x1) + lw x11, 11*4(x1) + lw x12, 12*4(x1) + lw x13, 13*4(x1) + lw x14, 14*4(x1) + lw x15, 15*4(x1) + lw x16, 16*4(x1) + lw x17, 17*4(x1) + lw x18, 18*4(x1) + lw x19, 19*4(x1) + lw x20, 20*4(x1) + lw x21, 21*4(x1) + lw x22, 22*4(x1) + lw x23, 23*4(x1) + lw x24, 24*4(x1) + lw x25, 25*4(x1) + lw x26, 26*4(x1) + lw x27, 27*4(x1) + lw x28, 28*4(x1) + lw x29, 29*4(x1) + lw x30, 30*4(x1) + lw x31, 31*4(x1) + + custom0 1,1,0,0 // getq x1, q1 + custom0 2,2,0,0 // getq x2, q2 + + custom0 0,0,0,2 // retirq + +irq_regs: + // registers are saved to this memory region + // the program counter is saved as register 0 + .fill 32,4 + +irq_stack: + .fill 128,4 +irq_stack_top: + start: + addi x1, zero, 0 + addi x2, zero, 0 + addi x3, zero, 0 + addi x4, zero, 0 + addi x5, zero, 0 + addi x6, zero, 0 + addi x7, zero, 0 + addi x8, zero, 0 + addi x9, zero, 0 + addi x10, zero, 0 + addi x11, zero, 0 + addi x12, zero, 0 + addi x13, zero, 0 + addi x14, zero, 0 + addi x15, zero, 0 + addi x16, zero, 0 + addi x17, zero, 0 + addi x18, zero, 0 + addi x19, zero, 0 + addi x20, zero, 0 + addi x21, zero, 0 + addi x22, zero, 0 + addi x23, zero, 0 + addi x24, zero, 0 + addi x25, zero, 0 + addi x26, zero, 0 + addi x27, zero, 0 + addi x28, zero, 0 + addi x29, zero, 0 + addi x30, zero, 0 + addi x31, zero, 0 + TEST(lui) TEST(auipc) TEST(j) diff --git a/picorv32.v b/picorv32.v index 4f4462f..58edeaa 100644 --- a/picorv32.v +++ b/picorv32.v @@ -29,9 +29,14 @@ module picorv32 #( parameter ENABLE_COUNTERS = 1, parameter ENABLE_REGS_16_31 = 1, parameter ENABLE_REGS_DUALPORT = 1, - parameter LATCHED_MEM_RDATA = 0 + parameter LATCHED_MEM_RDATA = 0, + parameter ENABLE_EXTERNAL_IRQ = 0, + parameter ENABLE_ILLINSTR_IRQ = 0, + parameter ENABLE_TIMER_IRQ = 0, + parameter PROGADDR_RESET = 0, + parameter PROGADDR_IRQ = 16 ) ( - input clk, resetn, + input clk, resetn, irq, output reg trap, output reg mem_valid, @@ -50,8 +55,11 @@ module picorv32 #( output reg [31:0] mem_la_wdata, output reg [ 3:0] mem_la_wstrb ); - localparam integer regfile_size = ENABLE_REGS_16_31 ? 32 : 16; - localparam integer regindex_bits = ENABLE_REGS_16_31 ? 5 : 4; + localparam ENABLE_IRQ = ENABLE_EXTERNAL_IRQ || ENABLE_ILLINSTR_IRQ || ENABLE_TIMER_IRQ; + + localparam integer irqregs_offset = ENABLE_REGS_16_31 ? 32 : 16; + localparam integer regfile_size = (ENABLE_REGS_16_31 ? 32 : 16) + 4*ENABLE_IRQ; + localparam integer regindex_bits = (ENABLE_REGS_16_31 ? 5 : 4) + ENABLE_IRQ; reg [63:0] count_cycle, count_instr; reg [31:0] reg_pc, reg_next_pc, reg_op1, reg_op2, reg_out, reg_alu_out; @@ -60,6 +68,11 @@ module picorv32 #( wire [31:0] next_pc; + reg irq_active; + reg [4:0] irq_mask; + reg [4:0] irq_pending; + reg [31:0] timer; + // Memory Interface reg [1:0] mem_state; @@ -166,6 +179,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_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh; + reg instr_getq, instr_setq, instr_retirq, instr_maskirq, instr_waitirq, instr_timer; wire instr_trap; reg [regindex_bits-1:0] decoded_rd, decoded_rs1, decoded_rs2; @@ -193,7 +207,8 @@ 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_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh}; + instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh, + instr_getq, instr_setq, instr_retirq, instr_maskirq, instr_waitirq, instr_timer}; wire is_rdcycle_rdcycleh_rdinstr_rdinstrh; assign is_rdcycle_rdcycleh_rdinstr_rdinstrh = |{instr_rdcycle, instr_rdcycleh, instr_rdinstr, instr_rdinstrh}; @@ -250,6 +265,13 @@ module picorv32 #( if (instr_rdinstr) new_instruction = "rdinstr"; if (instr_rdinstrh) new_instruction = "rdinstrh"; + if (instr_getq) new_instruction = "getq"; + if (instr_setq) new_instruction = "setq"; + if (instr_retirq) new_instruction = "retirq"; + if (instr_maskirq) new_instruction = "maskirq"; + if (instr_waitirq) new_instruction = "waitirq"; + if (instr_timer) new_instruction = "timer"; + if (new_instruction) instruction = new_instruction; end @@ -266,8 +288,9 @@ module picorv32 #( instr_lui <= mem_rdata_latched[6:0] == 7'b0110111; instr_auipc <= mem_rdata_latched[6:0] == 7'b0010111; - instr_jal <= mem_rdata_latched[6:0] == 7'b1101111; - instr_jalr <= mem_rdata_latched[6:0] == 7'b1100111; + instr_jal <= mem_rdata_latched[6:0] == 7'b1101111; + instr_jalr <= mem_rdata_latched[6:0] == 7'b1100111; + instr_retirq <= mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000010 && ENABLE_IRQ; is_beq_bne_blt_bge_bltu_bgeu <= mem_rdata_latched[6:0] == 7'b1100011; is_lb_lh_lw_lbu_lhu <= mem_rdata_latched[6:0] == 7'b0000011; @@ -280,6 +303,12 @@ module picorv32 #( decoded_rd <= mem_rdata_latched[11:7]; decoded_rs1 <= mem_rdata_latched[19:15]; decoded_rs2 <= mem_rdata_latched[24:20]; + + if (mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000000 && ENABLE_IRQ) + decoded_rs1[regindex_bits-1] <= 1; // instr_getq + + if (mem_rdata_latched[6:0] == 7'b0001011 && mem_rdata_latched[31:25] == 7'b0000010 && ENABLE_IRQ) + decoded_rs1 <= irqregs_offset; // instr_retirq end if (decoder_trigger && !decoder_pseudo_trigger) begin @@ -329,6 +358,12 @@ module picorv32 #( instr_rdinstr <= (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11000000001000000010) && ENABLE_COUNTERS; instr_rdinstrh <= (mem_rdata_q[6:0] == 7'b1110011 && mem_rdata_q[31:12] == 'b11001000001000000010) && ENABLE_COUNTERS; + instr_getq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000000 && ENABLE_IRQ; + instr_setq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000001 && ENABLE_IRQ; + instr_maskirq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000011 && ENABLE_IRQ; + instr_waitirq <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000100 && ENABLE_IRQ; + instr_timer <= mem_rdata_q[6:0] == 7'b0001011 && mem_rdata_q[31:25] == 7'b0000101 && ENABLE_TIMER_IRQ; + is_slli_srli_srai <= is_alu_reg_imm && |{ mem_rdata_q[14:12] == 3'b001 && mem_rdata_q[31:25] == 7'b0000000, mem_rdata_q[14:12] == 3'b101 && mem_rdata_q[31:25] == 7'b0000000, @@ -380,6 +415,7 @@ module picorv32 #( localparam cpu_state_stmem = 6; localparam cpu_state_ldmem = 7; reg [2:0] cpu_state; + reg [1:0] irq_state; reg set_mem_do_rinst; reg set_mem_do_rdata; @@ -448,12 +484,22 @@ module picorv32 #( if (ENABLE_COUNTERS) count_cycle <= resetn ? count_cycle + 1 : 0; + if (ENABLE_TIMER_IRQ && timer) begin + if (timer - 1 == 0) + irq_pending[1] <= 1; + timer <= timer - 1; + end + + if (ENABLE_EXTERNAL_IRQ && irq) begin + irq_pending[0] <= 1; + end + decoder_trigger <= mem_do_rinst && mem_done; decoder_pseudo_trigger <= 0; if (!resetn) begin - reg_pc <= 0; - reg_next_pc <= 0; + reg_pc <= PROGADDR_RESET; + reg_next_pc <= PROGADDR_RESET; if (ENABLE_COUNTERS) count_instr <= 0; latched_store <= 0; @@ -462,6 +508,11 @@ module picorv32 #( latched_is_lu <= 0; latched_is_lh <= 0; latched_is_lb <= 0; + irq_active <= 0; + irq_mask <= 0; + irq_pending <= 0; + irq_state <= 0; + timer <= 0; cpu_state <= cpu_state_fetch; end else (* parallel_case, full_case *) @@ -487,6 +538,24 @@ module picorv32 #( $display("ST_RD: %2d 0x%08x", latched_rd, latched_stalu ? reg_alu_out : reg_out); `endif cpuregs[latched_rd] <= latched_stalu ? reg_alu_out : reg_out; + end else + if (ENABLE_IRQ && irq_state[0]) begin + cpuregs[latched_rd] <= current_pc; + current_pc = PROGADDR_IRQ; + irq_active <= 1; + mem_do_rinst <= 1; + end else + if (ENABLE_IRQ && irq_state[1]) begin + cpuregs[latched_rd] <= + irq_pending[0] && irq_mask[0] ? 0 : + irq_pending[1] && irq_mask[1] ? 1 : + irq_pending[2] && irq_mask[2] ? 2 : + irq_pending[3] && irq_mask[3] ? 3 : 4; + irq_pending <= + irq_pending[0] && irq_mask[0] ? irq_pending & 5'b11110 : + irq_pending[1] && irq_mask[1] ? irq_pending & 5'b11101 : + irq_pending[2] && irq_mask[2] ? irq_pending & 5'b11011 : + irq_pending[3] && irq_mask[3] ? irq_pending & 5'b10111 : irq_pending & 5'b01111; end reg_pc <= current_pc; @@ -500,6 +569,12 @@ module picorv32 #( latched_is_lb <= 0; latched_rd <= decoded_rd; + if (ENABLE_IRQ && ((decoder_trigger && !irq_active && |(irq_pending & irq_mask)) || irq_state)) begin + irq_state <= + irq_state == 2'b00 ? 2'b01 : + irq_state == 2'b01 ? 2'b10 : 2'b00; + latched_rd <= irqregs_offset | irq_state[0]; + end else if (decoder_trigger) begin `ifdef DEBUG $display("-- %-0t", $time); @@ -516,7 +591,7 @@ module picorv32 #( latched_branch <= 1; end else begin mem_do_rinst <= 0; - mem_do_prefetch <= !instr_jalr; + mem_do_prefetch <= !instr_jalr && !instr_retirq; cpu_state <= cpu_state_ld_rs1; end end @@ -531,7 +606,11 @@ module picorv32 #( `ifdef DEBUG $display("SBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc); `endif - cpu_state <= cpu_state_trap; + if (ENABLE_ILLINSTR_IRQ && irq_mask[2] && !irq_active) begin + irq_pending[2] <= 1; + cpu_state <= cpu_state_fetch; + end else + cpu_state <= cpu_state_trap; end else if (is_rdcycle_rdcycleh_rdinstr_rdinstrh) begin (* parallel_case, full_case *) @@ -553,6 +632,32 @@ module picorv32 #( reg_op2 <= decoded_imm; mem_do_rinst <= mem_do_prefetch; cpu_state <= cpu_state_exec; + end else + if (ENABLE_IRQ && instr_getq) begin + reg_out <= cpuregs[decoded_rs1]; + latched_store <= 1; + cpu_state <= cpu_state_fetch; + end else + if (ENABLE_IRQ && instr_setq) begin + reg_out <= cpuregs[decoded_rs1]; + latched_rd <= latched_rd | irqregs_offset; + latched_store <= 1; + cpu_state <= cpu_state_fetch; + end else + if (ENABLE_IRQ && instr_retirq) begin + irq_active <= 0; + latched_branch <= 1; + latched_store <= 1; + reg_out <= cpuregs[decoded_rs1]; + cpu_state <= cpu_state_fetch; + end else + if (ENABLE_IRQ && instr_maskirq) begin + irq_mask = decoded_rs2; + cpu_state <= cpu_state_fetch; + end else + if (ENABLE_TIMER_IRQ && instr_timer) begin + timer <= cpuregs[decoded_rs1]; + cpu_state <= cpu_state_fetch; end else begin `ifdef DEBUG $display("LD_RS1: %2d 0x%08x", decoded_rs1, decoded_rs1 ? cpuregs[decoded_rs1] : 0); @@ -745,9 +850,12 @@ endmodule module picorv32_axi #( parameter ENABLE_COUNTERS = 1, parameter ENABLE_REGS_16_31 = 1, - parameter ENABLE_REGS_DUALPORT = 1 + parameter ENABLE_REGS_DUALPORT = 1, + parameter ENABLE_EXTERNAL_IRQ = 0, + parameter ENABLE_ILLINSTR_IRQ = 0, + parameter ENABLE_TIMER_IRQ = 0 ) ( - input clk, resetn, + input clk, resetn, irq, output trap, // AXI4-lite master memory interface @@ -814,10 +922,14 @@ module picorv32_axi #( picorv32 #( .ENABLE_COUNTERS (ENABLE_COUNTERS ), .ENABLE_REGS_16_31 (ENABLE_REGS_16_31 ), - .ENABLE_REGS_DUALPORT(ENABLE_REGS_DUALPORT) + .ENABLE_REGS_DUALPORT(ENABLE_REGS_DUALPORT), + .ENABLE_EXTERNAL_IRQ (ENABLE_EXTERNAL_IRQ ), + .ENABLE_ILLINSTR_IRQ (ENABLE_ILLINSTR_IRQ ), + .ENABLE_TIMER_IRQ (ENABLE_TIMER_IRQ ) ) picorv32_core ( .clk (clk ), .resetn (resetn ), + .irq (irq ), .trap (trap ), .mem_valid(mem_valid), .mem_addr (mem_addr ), diff --git a/testbench.v b/testbench.v index c14122d..b9914f5 100644 --- a/testbench.v +++ b/testbench.v @@ -6,6 +6,7 @@ module testbench; reg clk = 1; reg resetn = 0; + wire irq = &uut.picorv32_core.count_cycle[12:0]; wire trap; always #5 clk = ~clk; @@ -37,9 +38,14 @@ module testbench; wire mem_axi_rready; reg [31:0] mem_axi_rdata; - picorv32_axi uut ( + picorv32_axi #( + .ENABLE_EXTERNAL_IRQ (1), + .ENABLE_ILLINSTR_IRQ (1), + .ENABLE_TIMER_IRQ (1) + ) uut ( .clk (clk ), .resetn (resetn ), + .irq (irq ), .trap (trap ), .mem_axi_awvalid(mem_axi_awvalid), .mem_axi_awready(mem_axi_awready),