Skip to content

Add bitmanip extension support #525

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading