4

在这里使用伪代码。这些风格是否有优缺点:

假设您有一个可以执行加法、与、或和异或的 alu。是否最好让代码一直计算可能的答案,然后根据操作码选择答案(在这种情况下是一个热门):

alu_add = a + b;
alu_and = a & b;
alu_or  = a | b;
alu_xor = a ^ b;

...

if(opcode[0])      alu_out = alu_add;
else if(opcode[1]) alu_out = alu_and;
else if(opcode[2]) alu_out = alu_or;
else if(opcode[3]) alu_out = alu_xor;

另一种方法是这样编码:

if(opcode[0])      alu_out = a + b;
else if(opcode[1]) alu_out = a & b;
else if(opcode[2]) alu_out = a | b;
else if(opcode[3]) alu_out = a ^ b;

我也将其视为:

alu_add = a + b;
alu_and = a & b;
alu_or  = a | b;
alu_xor = a ^ b;

...

alu_out = 
  ( 8{opcode[0]} & alu_add ) |
  ( 8{opcode[1]} & alu_and ) | 
  ( 8{opcode[2]} & alu_or ) |
  ( 8{opcode[3]} & alu_xor );

这两种方法都有优点和缺点,还是最终结果差不多?

4

2 回答 2

8

从逻辑和可读性的角度考虑这一点。前两种形式在可读性方面很好,但都不必要地体现了优先级,并且会导致更多层次的逻辑。从这些指标中的任何一个来看,第三种形式也不是那么好。最后,没有明显的理由在这里使用单热编码而不是二进制编码。这是我的编码方式:

parameter ALU_ADD = 2'b00;
parameter ALU_AND = 2'b01;
parameter ALU_OR  = 2'b10;
parameter ALU_XOR = 2'b11;

reg [1:0]  opcode;  // 2 bits for binary coding vs. 4 for one-hot

// 之后,在你的 always 块中:

case (opcode)  // synopsys parallel_case
    ALU_ADD: alu_out = a + b;
    ALU_AND: alu_out = a & b;
    ALU_OR:  alu_out = a | b;
    ALU_XOR: alu_out = a ^ b;
endcase

在这里,我明确地为 ALU 操作码分配了值,避免了“幻数”,让其他人更容易理解正在发生的事情。我还使用了 case 语句并应用了一条指令,该指令告诉我的综合工具不能匹配多个表达式,因此不会推断出优先级编码器。我没有命名中间信号(alu_add 等),因为这些是微不足道的操作,但是当我想要方便地访问这些信号时我经常这样做(例如,在波形查看器中模拟后查看它们的值)。

您可以从优秀的Sunburst Design网站(无从属关系,只是以前的学生)的这篇文章中了解有关有效使用案例陈述的更多信息。

最后,关于您的问题,“最好让代码始终计算可能的答案,然后根据操作码选择答案”——请记住,Verilog 是一种硬件描述语言。无论如何,此页面上的所有实现都在计算所有内容。它们的不同之处在于逻辑和可读性的水平。看一下这个页面,它表明我的实现除了操作本身之外还有 1 级逻辑,其中 if-else 实现有 3 级附加逻辑。

于 2009-09-09T04:01:57.613 回答
2

前两个将给出相同的逻辑,但你会得到一个锁存器,alu_out因为你的if else块(你的优先级编码器)没有 final else。(无论如何,这对于verilog来说都是如此)。如果您的时间紧迫,您可能会遇到优先级编码器所暗示的长路径问题。

在第 3 版中,您将获得一个更“并行”的结构,这可能更适合计时。它不会闩锁。

在所有三个版本中,无论操作码是什么,这四个操作中的每一个都会发出咔哒声。这将产生权力影响。

我认为第一个版本更清晰,您可以在调试时在波形查看器中获得每个单独的操作。除非时序、面积或功率限制受到伤害,否则编写不那么容易阅读的内容是没有意义的。

于 2009-09-08T19:11:25.750 回答