diff --git a/README.md b/README.md index e35e270..ba27f0f 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,9 @@ fault handlers, or catch instructions from a larger ISA and emulate them in software. The optional Pico Co-Processor Interface (PCPI) can be used to implement -non-branching instructions in an external coprocessor. An implementation -of a core that implements the `MUL[H[SU|U]]` instructions is provided. +non-branching instructions in an external coprocessor. Implementations +of PCPI cores that implement the M Standard Extension instructions +`MUL[H[SU|U]]` and `DIV[U]/REM[U]` are included in this package. Files in this Repository @@ -209,6 +210,12 @@ This parameter internally enables PCPI and instantiates the `picorv32_pcpi_mul` core that implements the `MUL[H[SU|U]]` instructions. The external PCPI interface only becomes functional when ENABLE_PCPI is set as well. +#### ENABLE_DIV (default = 0) + +This parameter internally enables PCPI and instantiates the `picorv32_pcpi_div` +core that implements the `DIV[U]/REM[U]` instructions. The external PCPI +interface only becomes functional when ENABLE_PCPI is set as well. + #### ENABLE_IRQ (default = 0) Set this to 1 to enable IRQs. (see "Custom Instructions for IRQ Handling" below diff --git a/firmware/start.S b/firmware/start.S index a5547b8..403ac24 100644 --- a/firmware/start.S +++ b/firmware/start.S @@ -435,6 +435,11 @@ start: TEST(mulhu) TEST(mul) + TEST(div) + TEST(divu) + TEST(rem) + TEST(remu) + TEST(simple) /* set stack pointer */ diff --git a/picorv32.v b/picorv32.v index 93a9a01..d163754 100644 --- a/picorv32.v +++ b/picorv32.v @@ -50,6 +50,7 @@ module picorv32 #( parameter [ 0:0] CATCH_ILLINSN = 1, parameter [ 0:0] ENABLE_PCPI = 0, parameter [ 0:0] ENABLE_MUL = 0, + parameter [ 0:0] ENABLE_DIV = 0, parameter [ 0:0] ENABLE_IRQ = 0, parameter [ 0:0] ENABLE_IRQ_QREGS = 1, parameter [ 0:0] ENABLE_IRQ_TIMER = 1, @@ -99,7 +100,7 @@ module picorv32 #( localparam integer regfile_size = (ENABLE_REGS_16_31 ? 32 : 16) + 4*ENABLE_IRQ*ENABLE_IRQ_QREGS; localparam integer regindex_bits = (ENABLE_REGS_16_31 ? 5 : 4) + ENABLE_IRQ*ENABLE_IRQ_QREGS; - localparam WITH_PCPI = ENABLE_PCPI || ENABLE_MUL; + localparam WITH_PCPI = ENABLE_PCPI || ENABLE_MUL || ENABLE_DIV; reg [63:0] count_cycle, count_instr; reg [31:0] reg_pc, reg_next_pc, reg_op1, reg_op2, reg_out; @@ -127,6 +128,11 @@ module picorv32 #( wire pcpi_mul_wait; wire pcpi_mul_ready; + wire pcpi_div_wr; + wire [31:0] pcpi_div_rd; + wire pcpi_div_wait; + wire pcpi_div_ready; + reg pcpi_int_wr; reg [31:0] pcpi_int_rd; reg pcpi_int_wait; @@ -152,11 +158,31 @@ module picorv32 #( assign pcpi_mul_ready = 0; end endgenerate + generate if (ENABLE_DIV) begin + picorv32_pcpi_div pcpi_div ( + .clk (clk ), + .resetn (resetn ), + .pcpi_valid(pcpi_valid ), + .pcpi_insn (pcpi_insn ), + .pcpi_rs1 (pcpi_rs1 ), + .pcpi_rs2 (pcpi_rs2 ), + .pcpi_wr (pcpi_div_wr ), + .pcpi_rd (pcpi_div_rd ), + .pcpi_wait (pcpi_div_wait ), + .pcpi_ready(pcpi_div_ready ) + ); + end else begin + assign pcpi_div_wr = 0; + assign pcpi_div_rd = 1'bx; + assign pcpi_div_wait = 0; + assign pcpi_div_ready = 0; + end endgenerate + always @* begin pcpi_int_wr = 0; pcpi_int_rd = 1'bx; - pcpi_int_wait = |{ENABLE_PCPI && pcpi_wait, ENABLE_MUL && pcpi_mul_wait}; - pcpi_int_ready = |{ENABLE_PCPI && pcpi_ready, ENABLE_MUL && pcpi_mul_ready}; + pcpi_int_wait = |{ENABLE_PCPI && pcpi_wait, ENABLE_MUL && pcpi_mul_wait, ENABLE_DIV && pcpi_div_wait}; + pcpi_int_ready = |{ENABLE_PCPI && pcpi_ready, ENABLE_MUL && pcpi_mul_ready, ENABLE_DIV && pcpi_div_ready}; (* parallel_case *) case (1'b1) @@ -168,6 +194,10 @@ module picorv32 #( pcpi_int_wr = pcpi_mul_wr; pcpi_int_rd = pcpi_mul_rd; end + ENABLE_DIV && pcpi_div_ready: begin + pcpi_int_wr = pcpi_div_wr; + pcpi_int_rd = pcpi_div_rd; + end endcase end @@ -1548,6 +1578,90 @@ module picorv32_pcpi_mul #( endmodule +/*************************************************************** + * picorv32_pcpi_div + ***************************************************************/ + +module picorv32_pcpi_div ( + input clk, resetn, + + input pcpi_valid, + input [31:0] pcpi_insn, + input [31:0] pcpi_rs1, + input [31:0] pcpi_rs2, + output reg pcpi_wr, + output reg [31:0] pcpi_rd, + output reg pcpi_wait, + output reg pcpi_ready +); + reg instr_div, instr_divu, instr_rem, instr_remu; + wire instr_any_div_rem = |{instr_div, instr_divu, instr_rem, instr_remu}; + + reg pcpi_wait_q; + wire start = pcpi_wait && !pcpi_wait_q; + + always @(posedge clk) begin + instr_div <= 0; + instr_divu <= 0; + instr_rem <= 0; + instr_remu <= 0; + + if (resetn && pcpi_valid && !pcpi_ready && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin + case (pcpi_insn[14:12]) + 3'b100: instr_div <= 1; + 3'b101: instr_divu <= 1; + 3'b110: instr_rem <= 1; + 3'b111: instr_remu <= 1; + endcase + end + + pcpi_wait <= instr_any_div_rem; + pcpi_wait_q <= pcpi_wait; + end + + reg [31:0] dividend; + reg [62:0] divisor; + reg [31:0] quotient; + reg [31:0] quotient_msk; + reg running; + reg outsign; + + always @(posedge clk) begin + pcpi_ready <= 0; + pcpi_wr <= 0; + pcpi_rd <= 'bx; + + if (!resetn) begin + running <= 0; + end else + if (start) begin + running <= 1; + dividend <= (instr_div || instr_rem) && pcpi_rs1[31] ? -pcpi_rs1 : pcpi_rs1; + divisor <= ((instr_div || instr_rem) && pcpi_rs2[31] ? -pcpi_rs2 : pcpi_rs2) << 31; + outsign <= (instr_div && (pcpi_rs1[31] != pcpi_rs2[31])) || (instr_rem && pcpi_rs1[31]); + quotient <= 0; + quotient_msk <= 1 << 31; + end else + if (!quotient_msk && running) begin + running <= 0; + pcpi_ready <= 1; + pcpi_wr <= 1; + if (instr_div || instr_divu) + pcpi_rd <= outsign ? -quotient : quotient; + else + pcpi_rd <= outsign ? -dividend : dividend; + end else begin + if (divisor <= dividend) begin + dividend <= dividend - divisor; + quotient <= quotient | quotient_msk; + end + divisor <= divisor >> 1; + quotient_msk <= quotient_msk >> 1; + end + end +endmodule + + /*************************************************************** * picorv32_axi ***************************************************************/ @@ -1564,6 +1678,7 @@ module picorv32_axi #( parameter [ 0:0] CATCH_ILLINSN = 1, parameter [ 0:0] ENABLE_PCPI = 0, parameter [ 0:0] ENABLE_MUL = 0, + parameter [ 0:0] ENABLE_DIV = 0, parameter [ 0:0] ENABLE_IRQ = 0, parameter [ 0:0] ENABLE_IRQ_QREGS = 1, parameter [ 0:0] ENABLE_IRQ_TIMER = 1, @@ -1662,6 +1777,7 @@ module picorv32_axi #( .CATCH_ILLINSN (CATCH_ILLINSN ), .ENABLE_PCPI (ENABLE_PCPI ), .ENABLE_MUL (ENABLE_MUL ), + .ENABLE_DIV (ENABLE_DIV ), .ENABLE_IRQ (ENABLE_IRQ ), .ENABLE_IRQ_QREGS (ENABLE_IRQ_QREGS ), .ENABLE_IRQ_TIMER (ENABLE_IRQ_TIMER ), diff --git a/testbench.v b/testbench.v index ffbc13e..2a3f438 100644 --- a/testbench.v +++ b/testbench.v @@ -118,6 +118,7 @@ module picorv32_wrapper #( .COMPRESSED_ISA(1), `endif .ENABLE_MUL(1), + .ENABLE_DIV(1), .ENABLE_IRQ(1) ) uut ( .clk (clk ), diff --git a/tests/div.S b/tests/div.S new file mode 100644 index 0000000..a4504a7 --- /dev/null +++ b/tests/div.S @@ -0,0 +1,41 @@ +# See LICENSE for license details. + +#***************************************************************************** +# div.S +#----------------------------------------------------------------------------- +# +# Test div instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, div, 3, 20, 6 ); + TEST_RR_OP( 3, div, -3, -20, 6 ); + TEST_RR_OP( 4, div, -3, 20, -6 ); + TEST_RR_OP( 5, div, 3, -20, -6 ); + + TEST_RR_OP( 6, div, -1<<63, -1<<63, 1 ); + TEST_RR_OP( 7, div, -1<<63, -1<<63, -1 ); + + TEST_RR_OP( 8, div, -1, -1<<63, 0 ); + TEST_RR_OP( 9, div, -1, 1, 0 ); + TEST_RR_OP(10, div, -1, 0, 0 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/divu.S b/tests/divu.S new file mode 100644 index 0000000..cd348c9 --- /dev/null +++ b/tests/divu.S @@ -0,0 +1,41 @@ +# See LICENSE for license details. + +#***************************************************************************** +# divu.S +#----------------------------------------------------------------------------- +# +# Test divu instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, divu, 3, 20, 6 ); + TEST_RR_OP( 3, divu, 715827879, -20, 6 ); + TEST_RR_OP( 4, divu, 0, 20, -6 ); + TEST_RR_OP( 5, divu, 0, -20, -6 ); + + TEST_RR_OP( 6, divu, -1<<31, -1<<31, 1 ); + TEST_RR_OP( 7, divu, 0, -1<<31, -1 ); + + TEST_RR_OP( 8, divu, -1, -1<<31, 0 ); + TEST_RR_OP( 9, divu, -1, 1, 0 ); + TEST_RR_OP(10, divu, -1, 0, 0 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/rem.S b/tests/rem.S new file mode 100644 index 0000000..c318e2c --- /dev/null +++ b/tests/rem.S @@ -0,0 +1,41 @@ +# See LICENSE for license details. + +#***************************************************************************** +# rem.S +#----------------------------------------------------------------------------- +# +# Test rem instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, rem, 2, 20, 6 ); + TEST_RR_OP( 3, rem, -2, -20, 6 ); + TEST_RR_OP( 4, rem, 2, 20, -6 ); + TEST_RR_OP( 5, rem, -2, -20, -6 ); + + TEST_RR_OP( 6, rem, 0, -1<<63, 1 ); + TEST_RR_OP( 7, rem, 0, -1<<63, -1 ); + + TEST_RR_OP( 8, rem, -1<<63, -1<<63, 0 ); + TEST_RR_OP( 9, rem, 1, 1, 0 ); + TEST_RR_OP(10, rem, 0, 0, 0 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END diff --git a/tests/remu.S b/tests/remu.S new file mode 100644 index 0000000..38d641d --- /dev/null +++ b/tests/remu.S @@ -0,0 +1,41 @@ +# See LICENSE for license details. + +#***************************************************************************** +# remu.S +#----------------------------------------------------------------------------- +# +# Test remu instruction. +# + +#include "riscv_test.h" +#include "test_macros.h" + +RVTEST_RV32U +RVTEST_CODE_BEGIN + + #------------------------------------------------------------- + # Arithmetic tests + #------------------------------------------------------------- + + TEST_RR_OP( 2, remu, 2, 20, 6 ); + TEST_RR_OP( 3, remu, 2, -20, 6 ); + TEST_RR_OP( 4, remu, 20, 20, -6 ); + TEST_RR_OP( 5, remu, -20, -20, -6 ); + + TEST_RR_OP( 6, remu, 0, -1<<63, 1 ); + TEST_RR_OP( 7, remu, -1<<63, -1<<63, -1 ); + + TEST_RR_OP( 8, remu, -1<<63, -1<<63, 0 ); + TEST_RR_OP( 9, remu, 1, 1, 0 ); + TEST_RR_OP(10, remu, 0, 0, 0 ); + + TEST_PASSFAIL + +RVTEST_CODE_END + + .data +RVTEST_DATA_BEGIN + + TEST_DATA + +RVTEST_DATA_END