Implementing Type SB riscv instruction in C

298 views Asked by At

I am having some issues, I have made an attempt at decoding a 32 bit binary to a type SB instruction in RISCV Architecture set

I have shifted the opcode, imm, rs1,rs2 and rd values but have not managed to get the correct IMM values

For example for the following 32 bit binary

11111110000001111001101011100011

the immediate value is -12, however in my implementation

int type_sb_imm(unsigned char* p) {
    // Extract the 32-bit instruction from the byte array
    unsigned int opcode = (((unsigned int)p[0]) << 24) |
                          (((unsigned int)p[1]) << 16) |
                          (((unsigned int)p[2]) << 8) |
                          ((unsigned int)p[3]);

    // Extract the immediate value from the instruction (Type SB: branch)
    unsigned int uimm = ((opcode >> 7) & 0x1F) |            // imm[4:1]
                        ((opcode >> 20) & (0x7F << 5)) |    // imm[11:5]
                        ((opcode << 4) & (1 << 11)) |       // imm[12]
                        ((opcode << 19) & (0x1F << 12));    // imm[10:5]

    // Sign-extend the immediate value to 32 bits
    int imm = uimm;
    if (imm & 0x1000) {
        imm |= 0xFFFFE000;
    }

    return imm;
}

The immediate value returned is 4085, I am wondering where did I go wrong?

Please, modify my function with the correct shifts to extract the immediate value from a 32 bit binary.

1

There are 1 answers

9
Craig Estey On

Prefaced by the top comments ...

Create a function that uses the table defined bits (e.g.):

imm = bitmask(opcode,31,25,5) | bitmask(opcode,11,7,0);

Here is some sample code that I did quickly (because of your time pressure). I ran it under gdb and I think I got the mask generation correct--but no guarantees:

#include <stdio.h>

typedef unsigned int u32;

u32
bitmask(u32 opc,u32 bhi,u32 blo,u32 shfl)
{
    u32 val = opc >> blo;
    u32 mask = 0xFFFFFFFF >> 31 - (bhi - blo);
    val &= mask;
    val <<= shfl;
    return val;
}

int
main(void)
{

    u32 opc = 0xF0FFFF0A;
    u32 imm = 0;
    imm |= bitmask(opc,31,25,5);
    imm |= bitmask(opc,11,7,0);
    printf("%8.8X/%u\n",imm,imm);

    return 0;
}

Note that if you make the function inline and only pass constant bit values and shift, the compiler optimizer will generate efficient code.


UPDATE:

Will this return the immediate values with the signs? – HyperCoderSuperion

No, bitmask is deliberately unsigned so you can use it on many inst formats to get bit fields.

You can apply the code you already have to apply the signedness.

I'm doing this too fast. Here's my attempt, but it could easily be wrong--adjust to suit:

#include <stdio.h>

typedef unsigned int u32;

u32
bitmask(u32 opc,u32 bhi,u32 blo,u32 shfl)
{
    u32 val = opc >> blo;
    u32 mask = 0xFFFFFFFF >> 31 - (bhi - blo);
    val &= mask;
    val <<= shfl;
    return val;
}

int
main(void)
{

    u32 opc = 0xF0FFFF0A;

    u32 uimm = 0;
    uimm |= bitmask(opc,31,25,5);
    uimm |= bitmask(opc,11,7,0);

    printf("uimm=%8.8X/%u\n",uimm,uimm);

    int imm;
    if (uimm & 0x1000) {
        imm = uimm & 0xFFF;
        imm = -imm;
    }
    else
        imm = uimm;
    printf("imm=%8.8X/%d\n",imm,imm);

    return 0;
}

UPDATE #2:

I still don't understand, how do I pass in my instruction array that contains the instructions themselves? – HyperCoderSuperion

You've already got most of it in your existing code. After you get opcode, just replace your bit shifting code with the code I put into main. That is, use bitmask instead of the shifts you were doing.

Here's a quick hackup of your code.

Note: I used a different way to get imm from uimm in the second example. You'll have to experiment as to which way (yours or mine) is actually correct because I didn't have time to test much.

int
type_sb_imm(unsigned char *p)
{
    // Extract the 32-bit instruction from the byte array
    unsigned int opcode = (((unsigned int) p[0]) << 24) | (((unsigned int) p[1]) << 16) | (((unsigned int) p[2]) << 8) | ((unsigned int) p[3]);

    // Extract the immediate value from the instruction (Type SB: branch)
    u32 uimm = 0;
    uimm |= bitmask(opcode,31,25,5);
    uimm |= bitmask(opcode,11,7,0);

    // Sign-extend the immediate value to 32 bits
    int imm = uimm;

    if (imm & 0x1000) {
        imm |= 0xFFFFE000;
    }

    return imm;
}