Skip to content

Commit

Permalink
Merge pull request #525 from visitorckw/add-bitmanip-extension-support
Browse files Browse the repository at this point in the history
Add bitmanip extension support
  • Loading branch information
jserv authored Dec 23, 2024
2 parents 5b90d0f + 73dea0c commit f69d9dd
Show file tree
Hide file tree
Showing 11 changed files with 988 additions and 3 deletions.
1 change: 1 addition & 0 deletions .ci/riscv-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ make ENABLE_EXT_M=1 ENABLE_EXT_A=1 ENABLE_EXT_F=1 ENABLE_EXT_C=1 \
ENABLE_Zicsr=1 ENABLE_Zifencei=1 ENABLE_FULL4G=1
make arch-test RISCV_DEVICE=IMAFCZicsrZifencei || exit 1
make arch-test RISCV_DEVICE=FCZicsr || exit 1
make arch-test RISCV_DEVICE=IMZbaZbbZbcZbs || exit 1
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,22 @@ $(call set-feature, Zicsr)
ENABLE_Zifencei ?= 1
$(call set-feature, Zifencei)

# Zba Address generation instructions
ENABLE_Zba ?= 1
$(call set-feature, Zba)

# Zbb Basic bit-manipulation
ENABLE_Zbb ?= 1
$(call set-feature, Zbb)

# Zbc Carry-less multiplication
ENABLE_Zbc ?= 1
$(call set-feature, Zbc)

# Zbs Single-bit instructions
ENABLE_Zbs ?= 1
$(call set-feature, Zbs)

ENABLE_FULL4G ?= 0

# Experimental SDL oriented system calls
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ a focus on efficiency and readability.

Features:
* Fast interpreter for executing the RV32 ISA
* Comprehensive support for RV32I and M, A, F, C extensions
* Comprehensive support for RV32I and M, A, F, C, Zba, Zbb, Zbc, Zbs extensions
* Memory-efficient design
* Built-in ELF loader
* Implementation of commonly used newlib system calls
Expand Down Expand Up @@ -120,6 +120,10 @@ The image containing all the necessary tools for development and testing can be
* `ENABLE_EXT_A`: Standard Extension for Atomic Instructions
* `ENABLE_EXT_F`: Standard Extension for Single-Precision Floating Point Instructions
* `ENABLE_EXT_C`: Standard Extension for Compressed Instructions (RV32C.D excluded)
* `ENABLE_Zba`: Standard Extension for Address Generation Instructions
* `ENABLE_Zbb`: Standard Extension for Basic Bit-Manipulation Instructions
* `ENABLE_Zbc`: Standard Extension for Carry-Less Multiplication Instructions
* `ENABLE_Zbs`: Standard Extension for Single-Bit Instructions
* `ENABLE_Zicsr`: Control and Status Register (CSR)
* `ENABLE_Zifencei`: Instruction-Fetch Fence
* `ENABLE_GDBSTUB` : GDB remote debugging support
Expand Down Expand Up @@ -187,6 +191,10 @@ Current progress of this emulator in riscv-arch-test (RV32):
- `A`: Standard Extension for Atomic Instructions
- `F`: Standard Extension for Single-Precision Floating-Point
- `C`: Standard Extension for Compressed Instruction
- `Zba`: Standard Extension for Address Generation Instructions
- `Zbb`: Standard Extension for Basic Bit-Manipulation
- `Zbc`: Standard Extension for Carry-Less Multiplication
- `Zbs`: Standard Extension for Single-Bit Instructions
- `Zifencei`: Instruction-Fetch Fence
- `privilege`: RISCV Privileged Specification

Expand Down
57 changes: 57 additions & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,63 @@ static inline int rv_clz(uint32_t v)
}
#endif

#if defined(_MSC_VER)
#include <intrin.h>
static inline int rv_ctz(uint32_t v)
{
/* 0 is considered as undefined behavior */
assert(v);

uint32_t trailing_zero = 0;
_BitScanForward(&trailing_zero, v);
return trailing_zero;
}
#elif defined(__GNUC__) || defined(__clang__)
static inline int rv_ctz(uint32_t v)
{
/* https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */
/* 0 is considered as undefined behavior */
assert(v);

return __builtin_ctz(v);
}
#else /* generic implementation */
static inline int rv_ctz(uint32_t v)
{
/* 0 is considered as undefined behavior */
assert(v);

/* https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
*/

static const int mul_debruijn[32] = {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};

return mul_debruijn[((uint32_t) ((v & -v) * 0x077CB531U)) >> 27];
}
#endif

#if defined(__GNUC__) || defined(__clang__)
static inline int rv_popcount(uint32_t v)
{
/* https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */

return __builtin_popcount(v);
}
#else /* generic implementation */
static inline int rv_popcount(uint32_t v)
{
/* https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
*/

v -= (v >> 1) & 0x55555555;
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
v = (v + (v >> 4)) & 0x0f0f0f0f;
return (v * 0x01010101) >> 24;
}
#endif

/*
* Integer log base 2
*
Expand Down
179 changes: 179 additions & 0 deletions src/decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,42 @@ static inline bool op_op_imm(rv_insn_t *ir, const uint32_t insn)
ir->opcode = rv_insn_addi;
break;
case 1: /* SLLI: Shift Left Logical */
#if RV32_HAS(Zbb)
if (ir->imm == 0b011000000000) { /* clz */
ir->opcode = rv_insn_clz;
return true;
}
if (ir->imm == 0b011000000001) { /* ctz */
ir->opcode = rv_insn_ctz;
return true;
}
if (ir->imm == 0b011000000010) { /* cpop */
ir->opcode = rv_insn_cpop;
return true;
}
if (ir->imm == 0b011000000100) { /* sext.b */
ir->opcode = rv_insn_sextb;
return true;
}
if (ir->imm == 0b011000000101) { /* sext.h */
ir->opcode = rv_insn_sexth;
return true;
}
#endif
#if RV32_HAS(Zbs)
if (ir->imm >> 5 == 0b0100100) { /* bclri */
ir->opcode = rv_insn_bclri;
return true;
}
if (ir->imm >> 5 == 0b0110100) { /* binvi */
ir->opcode = rv_insn_binvi;
return true;
}
if (ir->imm >> 5 == 0b0010100) { /* bseti */
ir->opcode = rv_insn_bseti;
return true;
}
#endif
ir->opcode = rv_insn_slli;
if (unlikely(ir->imm & (1 << 5)))
return false;
Expand All @@ -478,6 +514,26 @@ static inline bool op_op_imm(rv_insn_t *ir, const uint32_t insn)
ir->opcode = rv_insn_xori;
break;
case 5:
#if RV32_HAS(Zbb)
if (ir->imm >> 5 == 0b0110000) { /* rori */
ir->opcode = rv_insn_rori;
return true;
}
if (ir->imm == 0b001010000111) { /* orc.b */
ir->opcode = rv_insn_orcb;
return true;
}
if (ir->imm == 0b011010011000) { /* rev8 */
ir->opcode = rv_insn_rev8;
return true;
}
#endif
#if RV32_HAS(Zbs)
if (ir->imm >> 5 == 0b0100100) { /* bexti */
ir->opcode = rv_insn_bexti;
return true;
}
#endif
/* SLL, SRL, and SRA perform logical left, logical right, and
* arithmetic right shifts on the value in register rs1.
*/
Expand Down Expand Up @@ -665,6 +721,117 @@ static inline bool op_op(rv_insn_t *ir, const uint32_t insn)
break;
#endif /* RV32_HAS(EXT_M) */

#if RV32_HAS(Zba)
/* inst funct7 rs2 rs1 funct3 rd opcode
* ------+-------+---+---+------+--+-------
* SH1ADD 0010000 rs2 rs1 010 rd 0110011
* SH2ADD 0010000 rs2 rs1 100 rd 0110011
* SH3ADD 0010000 rs2 rs1 110 rd 0110011
*/
case 0b0010000:
switch (funct3) {
case 0b010: /* sh1add */
ir->opcode = rv_insn_sh1add;
break;
case 0b100: /* sh2add */
ir->opcode = rv_insn_sh2add;
break;
case 0b110: /* sh3add */
ir->opcode = rv_insn_sh3add;
break;
default: /* illegal instruction */
return false;
}
break;
#endif /* RV32_HAS(Zba) */

#if RV32_HAS(Zbb) || RV32_HAS(Zbc)
/* inst funct7 rs2 rs1 funct3 rd opcode
* ------+-------+---+---+------+--+-------
* MAX 0000101 rs2 rs1 110 rd 0110011
* MIN 0000101 rs2 rs1 100 rd 0110011
* MAXU 0000101 rs2 rs1 111 rd 0110011
* MINU 0000101 rs2 rs1 101 rd 0110011
* ROL 0110000 rs2 rs1 001 rd 0110011
* ROR 0110000 rs2 rs1 101 rd 0110011
*/
case 0b0000101:
switch (funct3) {
#if RV32_HAS(Zbb)
case 0b110: /* max */
ir->opcode = rv_insn_max;
break;
case 0b100: /* min */
ir->opcode = rv_insn_min;
break;
case 0b111: /* maxu */
ir->opcode = rv_insn_maxu;
break;
case 0b101: /* minu */
ir->opcode = rv_insn_minu;
break;
#endif
#if RV32_HAS(Zbc)
case 0b001: /*clmul */
ir->opcode = rv_insn_clmul;
break;
case 0b011: /*clmulh */
ir->opcode = rv_insn_clmulh;
break;
case 0b010: /*clmulr */
ir->opcode = rv_insn_clmulr;
break;
#endif
default: /* illegal instruction */
return false;
}
break;
#endif
#if RV32_HAS(Zbb)
case 0b0110000:
switch (funct3) {
case 0b001: /* rol */
ir->opcode = rv_insn_rol;
break;
case 0b101: /* ror */
ir->opcode = rv_insn_ror;
break;
default: /* illegal instruction */
return false;
}
break;
case 0b0000100:
if (unlikely(ir->rs2))
return false;
ir->opcode = rv_insn_zexth;
break;
#endif /* RV32_HAS(Zbb) */

#if RV32_HAS(Zbs)
case 0b0100100:
switch (funct3) {
case 0b001: /* bclr */
ir->opcode = rv_insn_bclr;
break;
case 0b101: /* bext */
ir->opcode = rv_insn_bext;
break;
default: /* illegal instruction */
return false;
}
break;
case 0b0110100:
if (unlikely(funct3 != 0b001))
return false;
ir->opcode = rv_insn_binv;
break;
case 0b0010100:
if (unlikely(funct3 != 0b001))
return false;
ir->opcode = rv_insn_bset;
break;
#endif /* RV32_HAS(Zbs) */

case 0b0100000:
switch (funct3) {
case 0b000: /* SUB: Substract */
Expand All @@ -673,6 +840,18 @@ static inline bool op_op(rv_insn_t *ir, const uint32_t insn)
case 0b101: /* SRA: Shift Right Arithmetic */
ir->opcode = rv_insn_sra;
break;
#if RV32_HAS(Zbb)
case 0b111: /* ANDN */
ir->opcode = rv_insn_andn;
break;
case 0b110: /* ORN */
ir->opcode = rv_insn_orn;
break;
case 0b100: /* XNOR */
ir->opcode = rv_insn_xnor;
break;
#endif /* RV32_HAS(Zbb) */

default: /* illegal instruction */
return false;
}
Expand Down
44 changes: 44 additions & 0 deletions src/decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,50 @@ enum op_field {
_(csrrsi, 0, 4, 0, ENC(rs1, rd)) \
_(csrrci, 0, 4, 0, ENC(rs1, rd)) \
) \
/* RV32 Zba Standard Extension */ \
IIF(RV32_HAS(Zba))( \
_(sh1add, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(sh2add, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(sh3add, 0, 4, 0, ENC(rs1, rs2, rd)) \
) \
/* RV32 Zbb Standard Extension */ \
IIF(RV32_HAS(Zbb))( \
_(andn, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(orn, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(xnor, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(clz, 0, 4, 0, ENC(rs1, rd)) \
_(ctz, 0, 4, 0, ENC(rs1, rd)) \
_(cpop, 0, 4, 0, ENC(rs1, rd)) \
_(max, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(maxu, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(min, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(minu, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(sextb, 0, 4, 0, ENC(rs1, rd)) \
_(sexth, 0, 4, 0, ENC(rs1, rd)) \
_(zexth, 0, 4, 0, ENC(rs1, rd)) \
_(rol, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(ror, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(rori, 0, 4, 0, ENC(rs1, rd)) \
_(orcb, 0, 4, 0, ENC(rs1, rd)) \
_(rev8, 0, 4, 0, ENC(rs1, rd)) \
) \
/* RV32 Zbc Standard Extension */ \
IIF(RV32_HAS(Zbc))( \
_(clmul, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(clmulh, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(clmulr, 0, 4, 0, ENC(rs1, rs2, rd)) \
) \
/* RV32 Zbs Standard Extension */ \
IIF(RV32_HAS(Zbs))( \
_(bclr, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(bclri, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(bext, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(bexti, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(binv, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(binvi, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(bset, 0, 4, 0, ENC(rs1, rs2, rd)) \
_(bseti, 0, 4, 0, ENC(rs1, rs2, rd)) \
) \
/* RV32M Standard Extension */ \
IIF(RV32_HAS(EXT_M))( \
_(mul, 0, 4, 1, ENC(rs1, rs2, rd)) \
Expand Down
Loading

0 comments on commit f69d9dd

Please sign in to comment.