不幸的是,Verilog 的位选择和部分选择功能是表达式操作数的一部分。它们不是 Verilog 运算符(参见 Verilog 2005 标准文档的第 5.2.1 节,IEEE 标准 1364-2005),因此不能应用于任意表达式,而只能直接应用于寄存器或连线。
有多种方法可以做你想做的事,但我建议使用临时 64 位变量:
wire [31:0] A, B;
reg [63:0] tmp;
reg [31:0] ab_lsb, ab_msb;
always @(posedge clk) begin
tmp = A*B;
ab_lsb <= tmp[31:0];
ab_msb <= tmp[63:32];
end
(对 ab_lsb 和 ab_msb 的赋值可以是有条件的。否则,一个简单的“{ab_msb, ab_lsb} <= A*B;”当然也可以解决问题。)
请注意,我正在使用阻塞分配来分配“tmp”,因为我需要以下两行中的值。这也意味着从这个 always 块外部访问 'tmp' 是不安全的。
另请注意,此处不需要串联黑客 {A*B},因为 A*B 分配给 64 位寄存器。这也符合 IEEE Std 1364-2005 的 Sec 5.4.1 中的建议:
通过将结果分配给足够宽以容纳它的东西,可以在不丢失任何溢出位的情况下执行乘法。
但是,您说:“这里的重点是我无法访问 64 位寄存器”。
所以我将描述一个不使用任何 Verilog 64 位寄存器的解决方案。然而,这不会对生成的硬件产生任何影响。它只会在 Verilog 代码中看起来不同。
这个想法是通过移动 A*B 的结果来访问 MSB 位。以下天真的版本将不起作用:
ab_msb <= (A*B) >> 32; // Don't do this -- it won't work!
这不起作用的原因是 A*B 的宽度由赋值的左侧决定,即 32 位。因此 A*B 的结果将只包含结果的低 32 位。
使操作的位宽自定的一种方法是使用连接运算符:
ab_msb <= {A*B} >> 32; // Don't do this -- it still won't work!
现在乘法的结果宽度由最大值确定。其操作数的宽度。不幸的是,两个操作数都是 32 位的,因此我们仍然有 32 位乘法。所以我们需要将一个操作数扩展为 64 位,例如通过附加零(我假设是无符号操作数):
ab_msb <= {{32'd0, A}*B} >> 32;
访问 lsb 位很容易,因为无论如何这是默认行为:
ab_lsb <= A*B;
所以我们最终得到以下替代代码:
wire [31:0] A, B;
reg [31:0] ab_lsb, ab_msb;
always @(posedge clk) begin
ab_lsb <= A*B;
ab_msb <= {{32'd0, A}*B} >> 32;
end
Xilinx XST 14.2 为两个版本生成相同的 RTL 网表。我强烈推荐第一个版本,因为它更容易阅读和理解。如果只使用“ab_lsb”或“ab_msb”,综合工具会自动丢弃“tmp”中未使用的位。所以真的没有区别。
如果这不是您要查找的信息,您可能应该澄清为什么以及如何“无权访问 64 位寄存器”。毕竟,您也尝试在代码中访问 64 位值的位 [63:32]。如果不执行低 32 位所需的几乎所有计算,就无法计算乘积 A*B 的高 32 位,因此您可能会要求一些不可能的东西。