[英]Bit shifting in sequential block fails, in combinational not. Why?
I am debugging a piece of Verilog code for days, particularly sending and receiving bytes from an FX2LP (Cypress CY7C68016A) USB controller. 我要调试几天的Verilog代码,尤其是从FX2LP(Cypress CY7C68016A)USB控制器发送和接收字节的过程。 Without going into many details, data is sent and transmitted byte-wise in each cycle.
无需赘述,数据在每个周期中按字节发送和传输。 For my test, I use a 16 byte buffer which I first fill and then transmit back (echo test).
对于我的测试,我使用一个16字节的缓冲区,该缓冲区首先填充然后传回(回声测试)。
The significant part of my code looks like: 我的代码的重要部分如下所示:
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
This code works perfectly in simulation (iVerilog). 该代码在模拟(iVerilog)中可完美运行。 But when synthesizing and executing on an Altera Cyclone, I get very strange errors.
但是当在Altera Cyclone上进行合成和执行时,我得到了非常奇怪的错误。 For example, most of the time the first byte transmitted to the FPGA is read for each byte.
例如,大多数情况下,每个字节都读取传输到FPGA的第一个字节。 For example, sending
11 22 33 44 55 66 ...
would receive 11 11 11 11 11 11 ...
. 例如,发送
11 22 33 44 55 66 ...
将接收11 11 11 11 11 11 ...
Now when I instead introduce a new variable: 现在,当我改为引入一个新变量时:
reg [127:0] nextDataBuf;
and replace the part in the sequential always @(posedge FIFO_CLK)
block with: 并用以下命令替换顺序
always @(posedge FIFO_CLK)
块中的部分:
if(shiftBufRx) begin
dataBuf <= nextDataBuf;
end
if(shiftBufTx) begin
dataBuf <= nextDataBuf;
end
and in the combinational part: 在组合部分:
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
Then it works! 然后就可以了!
That means: All I am doing is to move the shifting of the register from the sequential block to the combinational block. 这意味着:我要做的就是将寄存器的移位从顺序块移至组合块。
I do not see any race conditions in my code and in simulation (iVerilog) both versions are identical. 在我的代码和仿真(iVerilog)中,我看不到任何竞争条件,这两个版本是相同的。
What could be the reason? 可能是什么原因?
First of all you should definitely post a MVCE . 首先,您绝对应该发布MVCE 。 This is almost an MVCE, but it doesn't contain a testbench and the ellipsis [...] obscures things that could be relevant like the state transitions from RX to TX.
daccess-ods.un.org daccess-ods.un.org这几乎是一个MVCE,但是它不包含测试平台,并且省略号掩盖了可能相关的事情,例如从RX到TX的状态转换。
Never the less, as it was close, I built a small test bench and added some minimal code to get it running. 一直以来,我都建立了一个小型测试台,并添加了一些最少的代码来使其运行。 In my case it did NOT work "perfectly in simulation".
在我的情况下,它不能 “完全在模拟中”工作。 In fact, as @greg pointed out, there is a definite error in the shift register.
实际上,正如@greg指出的那样,移位寄存器中存在确定的错误。
In your before code, you are shifting left out of register, but are transmitting the low bytes. 在您的之前代码中,您将移出寄存器,但正在传输低字节。 The first cycle they will be 8'h11 and afterward will either be 8'h00 or stay the same, depending on when shiftBufTx is true, and this depends on logic that isn't posted.
它们的第一个周期为8'h11,之后为8'h00或保持不变,具体取决于shiftBufTx何时为真,这取决于未发布的逻辑。 There is enough posted to see that it cannot possibly work correctly as is.
有足够的帖子显示它可能无法正常工作。
assign USB_DATAOUT = dataBuf[7:0];
...
if(shiftBufTx) begin // cycle was a transmit
dataBuf <= { dataBuf[127-8:0] , 8'h00 };
end
In the after code, you are shifting right and this is why it works: 在后面的代码中,您正在向右移动,这就是它起作用的原因:
nextDataBuf = { 8'h00 , dataBuf[127:8] };
It is possible that the sim does not match synthesis for a similar reason. 由于类似原因,sim卡可能与合成不匹配。 The code looks fairly synthesizable.
该代码看起来可以合成。 Some suggestions:
一些建议:
If you need more than one condition, you must have an else that insures that only one path can modify each non-blocking assignment to a var is active at any clock edge: 如果您需要多个条件,则必须具有另一个条件,以确保只有一个路径可以修改每个无阻塞分配给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
Why did it appear to work in sim? 为什么它似乎在sim中工作? Beyond small typos or transpositions of supposedly identical code, the sim is very forgiving of multiple writes to the same var (the later will "win").
除了一些小的错别字或本应相同代码的转置之外,sim卡还是非常宽容地对同一变量进行多次写入(后者会“获胜”)。
But the synthesis tool must intuit your intent into registers. 但是综合工具必须将您的意图引入寄存器。 Depending on how complex the logic is, it might not be able to know that both flags can't be true at the same time.
根据逻辑的复杂程度,它可能无法知道两个标志不能同时为真。 Sometimes it will interpret things like this as two parallel "write enable" conditions for dataBuf and create two parallel registers.
有时它将这样的事情解释为dataBuf的两个并行“写使能”条件,并创建两个并行寄存器。 The 2nd copy probably just never shifted back out after being set to 8'h11 which matches the symptoms.
将第二个副本设置为匹配症状的8'h11之后,可能再也不会移回该副本。 You should examine the actual output circuit after elaboration to detect this kind of error.
详细说明后,您应该检查实际的输出电路以检测这种错误。 Unfortunately not all of them fail or even give obvious warnings.
不幸的是,并非所有人都失败了,甚至没有给出明显的警告。 In Xilinx Vivado you can pull up the schematic.
在Xilinx Vivado中,您可以拉起原理图。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.