顺序块中的位移位失败,组合中则不会。为什么?
Bit shifting in sequential block fails, in combinational not. Why?
几天来我一直在调试一段 Verilog 代码,尤其是从 FX2LP (Cypress CY7C68016A) USB 控制器发送和接收字节。无需赘述,数据在每个周期中按字节发送和传输。对于我的测试,我使用了一个 16 字节的缓冲区,我首先填充它然后传回(回声测试)。
我的代码的重要部分如下所示:
reg [127:0] dataBuf; // 16 byte buffer for USB data
reg [7:0] cntByte; // counter for number of bytes
reg [7:0] nextCntByte;
reg shiftBufRx, shiftBufTx; // flags whether buffer should be shifted
reg [7:0] currentByte; // current read byte
// in transmit cycle, byte is read from USB_DATAOUT
assign USB_DATAOUT = dataBuf[7:0];
always @(posedge FIFO_CLK) begin
// update state variables
CurrentState <= NextState;
cntByte <= nextCntByte;
if(shiftBufRx) begin // cycle was a receive
dataBuf <= { currentByte , dataBuf[127:8] };
end
if(shiftBufTx) begin // cycle was a transmit
dataBuf <= { dataBuf[127-8:0] , 8'h00 };
end
end
always @(*) begin
// avoid race conditions
NextState = CurrentState;
nextCntByte = cntByte;
nextDataBuf = dataBuf;
currentByte = 0;
shiftBufRx = 0;
shiftBufTx = 0;
case(CurrentState)
[...]
STATE_USBRX: begin
if(cntByte < 16) begin
nextCntByte = cntByte + 1;
currentByte = USB_DATAIN; // contains received byte in receive cycle
shiftBufRx = 1; // shift buffer after this cycle
end
[...]
end
STATE_USBTX: begin
if(cntByte < 15) begin
shiftBufTx = 1; // shift buffer after this cycle
nextCntByte = cntByte + 1;
end
[...]
end
[...]
endcase
end
此代码在仿真 (iVerilog) 中完美运行。但是在 Altera Cyclone 上综合和执行时,我会遇到非常奇怪的错误。例如,大多数情况下,每个字节都会读取传输到 FPGA 的第一个字节。例如,发送 11 22 33 44 55 66 ...
将收到 11 11 11 11 11 11 ...
.
现在当我引入一个新变量时:
reg [127:0] nextDataBuf;
并将顺序 always @(posedge FIFO_CLK)
块中的部分替换为:
if(shiftBufRx) begin
dataBuf <= nextDataBuf;
end
if(shiftBufTx) begin
dataBuf <= nextDataBuf;
end
在组合部分:
STATE_USBRX: begin
if(cntByte < 16) begin
nextCntByte = cntByte + 1;
//currentByte = FIFO_DATAIN;
nextDataBuf = { dataBuf[127-8:0] , FIFO_DATAIN };
shiftBufRx = 1;
end
[...]
end
STATE_USBTX: begin
if(cntByte < 15) begin
shiftBufTx = 1;
nextCntByte = cntByte + 1;
nextDataBuf = { 8'h00 , dataBuf[127:8] };
end
[...]
end
那就成功了!
也就是说:我所做的只是将寄存器的移位从顺序块移动到组合块。
我在我的代码和模拟 (iVerilog) 中没有看到任何竞争条件,两个版本是相同的。
可能是什么原因?
首先你绝对应该post一个MVCE。这几乎是一个 MVCE,但它不包含测试平台,省略号 [...] 掩盖了可能相关的内容,例如从 RX 到 TX 的状态转换。
尽管如此,因为它很接近,所以我构建了一个小型测试平台并添加了一些最少的代码来获得它 运行。在我的例子中,它 NOT 工作 "perfectly in simulation"。事实上,正如@greg所指出的,移位寄存器存在一定的错误。
在您之前的代码中,您正在向左移出寄存器,但正在传输低字节。第一个周期为 8'h11,之后为 8'h00 或保持不变,具体取决于 shiftBufTx 何时为真,这取决于未 posted 的逻辑。有足够的 posted 可以看出它不可能按原样正常工作。
assign USB_DATAOUT = dataBuf[7:0];
...
if(shiftBufTx) begin // cycle was a transmit
dataBuf <= { dataBuf[127-8:0] , 8'h00 };
end
在后面的代码中,您正在 右移 这就是它起作用的原因:
nextDataBuf = { 8'h00 , dataBuf[127:8] };
由于类似的原因,sim 可能与合成不匹配。代码看起来相当可综合。一些建议:
- 没有理由为 Tx 和 Rx 设置独立的标志
- 正如@serge指出的那样,如果它们发生冲突会怎样?这可能没问题,但不能在硬件中工作。
- 您可以只使用一个标志并将其设置在每个状态的输出中。
如果您需要多个条件,则必须有一个 else 以确保只有 一个 路径可以修改对 var 的每个非阻塞赋值在任何时钟边沿激活:
if(shiftBufRx) begin // cycle was a receive
dataBuf <= { currentByte , dataBuf[127:8] };
end
**else** if(shiftBufTx) begin // cycle was a transmit
dataBuf <= **{ 8'h00 , dataBuf[127:8] }**;
end
为什么它似乎在 sim 中工作? 除了小错别字或假设相同代码的换位之外,sim 非常宽容多次写入同一个 var(稍后将"win").
但是综合工具必须将您的意图直觉化到寄存器中。根据逻辑的复杂程度,它可能无法知道两个标志不能同时为真。有时它会将这样的事情解释为 dataBuf 的两个并行 "write enable" 条件并创建两个并行寄存器。第二个副本在设置为与症状相符的 8'h11 后可能从未移回。您应该在详细说明后检查实际输出电路以检测此类错误。不幸的是,并不是所有的都失败了,甚至都给出了明显的警告。在 Xilinx Vivado 中,您可以拉出原理图。
几天来我一直在调试一段 Verilog 代码,尤其是从 FX2LP (Cypress CY7C68016A) USB 控制器发送和接收字节。无需赘述,数据在每个周期中按字节发送和传输。对于我的测试,我使用了一个 16 字节的缓冲区,我首先填充它然后传回(回声测试)。
我的代码的重要部分如下所示:
reg [127:0] dataBuf; // 16 byte buffer for USB data
reg [7:0] cntByte; // counter for number of bytes
reg [7:0] nextCntByte;
reg shiftBufRx, shiftBufTx; // flags whether buffer should be shifted
reg [7:0] currentByte; // current read byte
// in transmit cycle, byte is read from USB_DATAOUT
assign USB_DATAOUT = dataBuf[7:0];
always @(posedge FIFO_CLK) begin
// update state variables
CurrentState <= NextState;
cntByte <= nextCntByte;
if(shiftBufRx) begin // cycle was a receive
dataBuf <= { currentByte , dataBuf[127:8] };
end
if(shiftBufTx) begin // cycle was a transmit
dataBuf <= { dataBuf[127-8:0] , 8'h00 };
end
end
always @(*) begin
// avoid race conditions
NextState = CurrentState;
nextCntByte = cntByte;
nextDataBuf = dataBuf;
currentByte = 0;
shiftBufRx = 0;
shiftBufTx = 0;
case(CurrentState)
[...]
STATE_USBRX: begin
if(cntByte < 16) begin
nextCntByte = cntByte + 1;
currentByte = USB_DATAIN; // contains received byte in receive cycle
shiftBufRx = 1; // shift buffer after this cycle
end
[...]
end
STATE_USBTX: begin
if(cntByte < 15) begin
shiftBufTx = 1; // shift buffer after this cycle
nextCntByte = cntByte + 1;
end
[...]
end
[...]
endcase
end
此代码在仿真 (iVerilog) 中完美运行。但是在 Altera Cyclone 上综合和执行时,我会遇到非常奇怪的错误。例如,大多数情况下,每个字节都会读取传输到 FPGA 的第一个字节。例如,发送 11 22 33 44 55 66 ...
将收到 11 11 11 11 11 11 ...
.
现在当我引入一个新变量时:
reg [127:0] nextDataBuf;
并将顺序 always @(posedge FIFO_CLK)
块中的部分替换为:
if(shiftBufRx) begin
dataBuf <= nextDataBuf;
end
if(shiftBufTx) begin
dataBuf <= nextDataBuf;
end
在组合部分:
STATE_USBRX: begin
if(cntByte < 16) begin
nextCntByte = cntByte + 1;
//currentByte = FIFO_DATAIN;
nextDataBuf = { dataBuf[127-8:0] , FIFO_DATAIN };
shiftBufRx = 1;
end
[...]
end
STATE_USBTX: begin
if(cntByte < 15) begin
shiftBufTx = 1;
nextCntByte = cntByte + 1;
nextDataBuf = { 8'h00 , dataBuf[127:8] };
end
[...]
end
那就成功了!
也就是说:我所做的只是将寄存器的移位从顺序块移动到组合块。
我在我的代码和模拟 (iVerilog) 中没有看到任何竞争条件,两个版本是相同的。
可能是什么原因?
首先你绝对应该post一个MVCE。这几乎是一个 MVCE,但它不包含测试平台,省略号 [...] 掩盖了可能相关的内容,例如从 RX 到 TX 的状态转换。
尽管如此,因为它很接近,所以我构建了一个小型测试平台并添加了一些最少的代码来获得它 运行。在我的例子中,它 NOT 工作 "perfectly in simulation"。事实上,正如@greg所指出的,移位寄存器存在一定的错误。
在您之前的代码中,您正在向左移出寄存器,但正在传输低字节。第一个周期为 8'h11,之后为 8'h00 或保持不变,具体取决于 shiftBufTx 何时为真,这取决于未 posted 的逻辑。有足够的 posted 可以看出它不可能按原样正常工作。
assign USB_DATAOUT = dataBuf[7:0];
...
if(shiftBufTx) begin // cycle was a transmit
dataBuf <= { dataBuf[127-8:0] , 8'h00 };
end
在后面的代码中,您正在 右移 这就是它起作用的原因:
nextDataBuf = { 8'h00 , dataBuf[127:8] };
由于类似的原因,sim 可能与合成不匹配。代码看起来相当可综合。一些建议:
- 没有理由为 Tx 和 Rx 设置独立的标志
- 正如@serge指出的那样,如果它们发生冲突会怎样?这可能没问题,但不能在硬件中工作。
- 您可以只使用一个标志并将其设置在每个状态的输出中。
如果您需要多个条件,则必须有一个 else 以确保只有 一个 路径可以修改对 var 的每个非阻塞赋值在任何时钟边沿激活:
if(shiftBufRx) begin // cycle was a receive dataBuf <= { currentByte , dataBuf[127:8] }; end **else** if(shiftBufTx) begin // cycle was a transmit dataBuf <= **{ 8'h00 , dataBuf[127:8] }**; end
为什么它似乎在 sim 中工作? 除了小错别字或假设相同代码的换位之外,sim 非常宽容多次写入同一个 var(稍后将"win").
但是综合工具必须将您的意图直觉化到寄存器中。根据逻辑的复杂程度,它可能无法知道两个标志不能同时为真。有时它会将这样的事情解释为 dataBuf 的两个并行 "write enable" 条件并创建两个并行寄存器。第二个副本在设置为与症状相符的 8'h11 后可能从未移回。您应该在详细说明后检查实际输出电路以检测此类错误。不幸的是,并不是所有的都失败了,甚至都给出了明显的警告。在 Xilinx Vivado 中,您可以拉出原理图。