什么可能是 C 中 SystemVerilog 的 casex 语句的等效方法

What could be an equivalent approach to a casex statement from SystemVerilog in C

一些背景信息:我目前正在编写一个模拟器。我正在尝试实现具有类似行为的解码器,例如 SystemVerilog 上的 'casex' 语句。

为了举例说明,这些是一些说明。

BNE_ENCODING        0b100011xx
BRK_ENCODING        0b10101100
CMP_ENCODING        0b01110000
DEC_ENCODING        0b1001xxxx

我尝试了什么:我尝试用零替换 x。但是那没有用。

我不明白如何在不创建冲突的 case 语句的情况下忽略 C 语言的最后一位。 x 代表忽略的位。我试图通过查看 Quartus 生成的 RTL 输出来了解一些实现(我知道 RTL 与 C 无关,它只是让我了解我可以实现的布尔表达式)。译码器是用或非门实现的。

System Verilog 上的部分解码器。

casex(op)
        8'b100011xx: /* Do something */;
        8'b10101100: /* Do something */;
        8'b01110000: /* Do something */;
        8'b1001xxxx: /* Do something */;
endcase

问题:用 C 语言实现这种解码器的正确方法是什么?我所说的解码器是指与 Verilog casex 语句具有相似行为的 switch 语句。

忽略某些位可以通过按位与操作屏蔽它们来完成。

if ((op & 0xFC) == 0x8C) {
    /* 8'b100011xx */
} else if ((op & 0xFF) == 0xAC) {
    /* 8'b10101100 */
} else if ((op & 0xFF) == 0x70) {
    /* 8'b01110000 */
} else if ((op & 0xF0) == 0x90) {
    /* 8'b1001xxxx */
}

如果你坚持使用switch语句,你可以使用多级的。

switch (op & 0xF0) {
    case 0x80: /* 8'b1000xxxx */
        switch (op & 0x0C) {
            case 0x0C: /* 8'bxxxx11xx (8'b100011xx) */
                break;
        }
        break;
    case 0xA0: /* 8'b1010xxxx */
        switch (op & 0x0F) {
            case 0x0C: /* 8'bxxxx1100 (8'b10101100) */
                break;
        }
        break;
    case 0x70: /* 8'b0111xxxx */
        switch (op & 0x0F) {
            case 0x00: /* 8'bxxxx0000 (8'b01110000) */
                break;
        }
        break;
    case 0x90: /* 8'b1001xxxx */
        break;
}

由于您需要实现通配符,您可以使用 if-else 链,在其中以某种方式屏蔽输入,使比较忽略 不关心 位:

if((op & 0xFC) == 0x8C) // 0x8C = 8'b10001100
{
    /* Do something */;
}
else if(op == 0xAC) // 0xAC = 8'b10101100
{
   /* Do something */;
}
else if(op == 0x70) // 0x70 = 8'b01110000
{
    /* Do something */;
}
else if((op & 0xF0) == 0x90) // 0x90 = 8'b1001xxxx
{
    /* ... */
}
  • 在第一个条件中,你将输入 op 与 0xFC,即 b11111100 进行运算,以便将两个 LSB 设置为 0,并将其与你输入的值进行比较 casex 无关位设置为 0 (0x8C)
  • 第二个和第三个条件是直接比较
  • 在最后一个条件中,你将输入op与0xF0,即b11110000进行AND运算,以便将四个LSB设置为0,并将其与你输入的值进行比较casex 无关位设置为 0 (0x90)

您可以使用右移运算符 (>>)。

  int x = 10; // 1010 in binary
  switch(x >> 1) // this would have switched 101, getting rid of the 0 at the end
  switch(x >> 2) // this would have switched 10, getting rid of the 10 at the end

如果您使用 gcc 系列编译器(gccclangicc 等),您可以使用 switch ... case 中的范围,这将准确地执行操作你想要:

void foo(uint8_t val)
{
    switch(val)
    {
    case 0b10001100 ... 0b10001111:
        /* do something */
        break;
    case 0b10101100:
        /* do something */
        break;
    case 0b01110000:
        /* do something */
        break;
    case 0b10010000 ... 0b10011111:
        /* do something */
        break;
    default:
        /* do something */
        break;
    }
}

https://godbolt.org/z/5f7bd9

作为额外的奖励,gcc 也有二进制常量扩展。