Added basic IRQ support
This commit is contained in:
parent
982e5cc600
commit
23b700cf73
|
@ -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
|
||||
|
|
7
Makefile
7
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 $<
|
||||
|
|
144
README.md
144
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
|
||||
|
|
101
firmware/sieve.c
101
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
163
firmware/start.S
163
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)
|
||||
|
|
140
picorv32.v
140
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 ),
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue