顺序块中的位移失败,在组合中不是。为什么呢?

时间:2017-12-03 08:26:51

标签: verilog race-condition hardware-interface iverilog

我正在调试一段Verilog代码,特别是从FX2LP(赛普拉斯CY7C68016A)USB控制器发送和接收字节。在不涉及许多细节的情况下,数据在每个周期中以字节方式发送和传输。对于我的测试,我使用一个16字节的缓冲区,我先填充然后再传回(echo test)。

我的代码的重要部分如下:

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)两个版本都是相同的。

可能是什么原因?

1 个答案:

答案 0 :(得分:0)

首先,你应该发布一个MVCE。这几乎是一个MVCE,但它不包含一个测试平台,省略号[...]模糊了可能与从RX到TX的状态转换相关的事情。

从未如此接近,我建立了一个小型测试平台,并添加了一些最小的代码以使其运行。在我的情况下, NOT 在“模拟中完美地”工作。实际上,正如@greg指出的那样,移位寄存器中存在明确的错误。

在您之前的代码中,您正在向左移出寄存器,但正在传输低字节。第一个周期它们将是8'h11,之后将是8'h00或保持不变,具体取决于shiftBufTx何时为真,这取决于未发布的逻辑。有足够的帖子可以看到它不能正常工作。

    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在任何时钟边缘都是活动的:< / p>

    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的两个并行“写入启用”条件,并创建两个并行寄存器。第二个副本可能在设置为与症状匹配的8'h11之后从未移出。您应该在详细说明后检查实际输出电路以检测此类错误。不幸的是,并非所有这些都失败甚至发出明显的警告。在Xilinx Vivado中,您可以提取原理图。