YosysHQ / yosys

Yosys Open SYnthesis Suite
https://yosyshq.net/yosys/
ISC License
3.3k stars 860 forks source link

Yosys seems to handle bit operations on empty strings inconsistently with the original design. #4395

Open WeneneW opened 1 month ago

WeneneW commented 1 month ago

Version

Yosys 0.39+165

On which OS did this happen?

Linux

Reproduction Steps

My Verilog original design is as follows:

module top  (y, clk, wire3, wire2, wire1, wire0);
  output wire [(32'h69):(32'h0)] y;
  input wire [(1'h0):(1'h0)] clk;
  input wire [(2'h2):(1'h0)] wire3;
  input wire signed [(3'h6):(1'h0)] wire2;
  input wire [(3'h4):(1'h0)] wire1;
  input wire [(3'h4):(1'h0)] wire0;
  wire signed [(3'h5):(1'h0)] wire16;
  reg [(3'h6):(1'h0)] reg19 = (1'h0);
  reg [(2'h2):(1'h0)] reg9 = (1'h0);
  reg [(2'h3):(1'h0)] reg4 = (1'h0);

  reg signed [(2'h3):(1'h0)] reg18 = (1'h0);

  assign wire16 = $signed(reg4[(2'h3):(2'h3)]);

   always
    @(posedge clk) begin
    if ({(wire2[(1'h1):(1'h0)] ^~ "")})
        begin
          reg19 <= (((wire16 - reg18) > (reg9 ? wire16 : (8'h9d))) == ({wire2} >> reg18[(2'h2):(2'h2)]));
          end
    end
  assign y = {wire16,reg19};
endmodule

The content of the testbench file is as follows:

`include "syn_identity.v"

module testbench  ;
  wire [(32'h69):(32'h0)] y;
  reg [(1'h0):(1'h0)] clk;
  reg [(2'h2):(1'h0)] wire3;
  reg signed [(3'h6):(1'h0)] wire2;
  reg [(3'h4):(1'h0)] wire1;
  reg [(3'h4):(1'h0)] wire0;
  top #() top1 (.y(y), .clk(clk), .wire3(wire3), .wire2(wire2), .wire1(wire1), .wire0(wire0));
  initial
    begin
      clk = (1'h0);
      {wire3, wire2, wire1, wire0} = (1'h0);
      #10 {wire3, wire2, wire1, wire0} = (255'h5666e7236ccb30a556a6817ea189368c30b906a1ffc187ed92dfc490f0eccdcc);
      #10 {wire3, wire2, wire1, wire0} = (255'h64e93f5f414839fbd286d8dc2eb8d6aa9526c16bb17249a21b5346d64d98a15c);
      #10 {wire3, wire2, wire1, wire0} = (254'h2a24554da65a67cd45f891a26f3ad683c9e09f99735a4c2da74fb67f8a71b41a);
      #10 {wire3, wire2, wire1, wire0} = (256'hb6e354d2450b5ba924f1e5897ff7f5703b32ef7708143c6c1568ef00e994fabc);
      #10 {wire3, wire2, wire1, wire0} = (254'h361ae0762d1d79966dcfbf16f4c94e200b6a950edd5cec4f5df5cad3beb8a63e);
      #10 {wire3, wire2, wire1, wire0} = (256'ha02c1bbfb33f8e0d37f07cd0c56e140dde5a71a4a47c744aa2f8e9167d02e1f6);
      #10 {wire3, wire2, wire1, wire0} = (255'h4d7405393f26214e85cde2ec12516286a64d8bd25ccc3375707092d5e47bd57b);
      #10 {wire3, wire2, wire1, wire0} = (254'h227a3c7d5bcaf0ac2d07c3f42c217038a3242fc9de5ca976786c4c7ee05d004b);
      #10 {wire3, wire2, wire1, wire0} = (254'h37860b4670a4b1a5207e18c0ae454d800fa22d330e9764ef32be7dbd3e4c1694);
      #10 {wire3, wire2, wire1, wire0} = (255'h5cb2e7496d0827c9fda48122a1d4b3a07da3b6465f969cc9655ce0357373e6a6);
      #10 {wire3, wire2, wire1, wire0} = (255'h52cd987b19dc33e1340b271d0eda7eba4e304a510a548fac3395e86c2008e1cf);
      #10 {wire3, wire2, wire1, wire0} = (253'h1f840181b6014fb4347d7f5fdc02755c6becb6c047c08b30330842aefe7f78c1);
      #10 {wire3, wire2, wire1, wire0} = (254'h22d09f56347517d3b9d6a79cfd9a305e9fa73f9227636766a54166cf4dc307e2);
      #10 {wire3, wire2, wire1, wire0} = (255'h4463539205682c2e34f536caa90f7403004e2b5afcc6b3a9c4eb0fa100c7ed2e);
      #10 {wire3, wire2, wire1, wire0} = (256'ha3eb3e1063bdb51d2a8176a662254bd7d23b05e3eca45b18db6620f006b35bb6);
      #10 {wire3, wire2, wire1, wire0} = (256'hd768bccee9759ad2cef5289ecd2e79eaa38b6280c68eed0ecdcc3112c2bbcdc6);
      #10 {wire3, wire2, wire1, wire0} = (256'hb04eb66647a4930991bf8f281f3ed159339e3d0f87b246cd23b8aabcace82f65);
      #10 {wire3, wire2, wire1, wire0} = (256'hb191b7b20490a42a7d320f7cfafed87ee212a28b387508d60912f3dcb871aa51);
      #10 {wire3, wire2, wire1, wire0} = (253'h1ba3887844d5adf4af88c06c7dafd34f9beabe64906575310e8acf4029077019);
      #10 {wire3, wire2, wire1, wire0} = (256'h9c2051556edc866f4a7a24fecc5e4bb92b95657730ee5b600ab96b5ba0a781a6);
      #10 $finish;
    end 
  always
    #5 clk = (~clk);
  always
    @(posedge clk) $strobe ("%b", y);
  initial
    begin
      $dumpfile("wave_yosys.vcd");
      $dumpvars(0, testbench);
    end
endmodule

For this design, I used the same testbench file to simulate the code before and after yosys synthesis. However, there were inconsistencies between the original design simulation and the synthesized simulation. 7b4e0706878bdfd0ea03417a4436d764 fe61ab69ac405782bd36382ca3bf33dc

However, when I change the if statement in the code fromif ({(wire2[(1'h1):(1'h0)] ^~ "")})toif ({(wire2[(1'h1):(1'h0)] ^~ "a")}), replacing the empty string with another string or a different binary number, this inconsistency disappears.

The directory structure of the compressed package is as follows: yosys_5_13.zip

rtl.v is the original design. Files with the syn_prefix represent the synthesized versions. You can compare the vvp.log files located in the identity and yosys directories. Additionally, you can rerun simulations on the synthesized files by executing bash run_sim.sh.

yosys_5_13
│
├── data
│   ├── cells_cmos.v
│   ├── cells_cyclone_v.v
│   ├── cells_verific.v
│   ├── cells_xilinx_7.v
│   ├── cells_yosys.v
│
├── identity
│   ├── icarus_stderr.log
│   ├── rtl.v
│   ├── identity_main
│   ├── vvp_identity.log
│   ├── wave_identity.vcd
│   ├── identity_testbench.v
│
├── vivado
│   ├── vivado_testbench.v
│   ├── rtl.v
│   ├── syn_vivado.v
│   ├── icarus_stderr.log
│   ├── vivado_main
│   ├── vvp_vivado.log
│   ├── wave_vivado.vcd
│
├── yosys
│   ├── yosys_testbench.v
│   ├── rtl.v
│   ├── syn_yosys.v
│   ├── icarus_stderr.log
│   ├── yosys_main
│   ├── vvp_yosys.log
│   ├── wave_yosys.vcd
│
├── .Xil
│
├── identity_testbench.v
├── rtl.v
├── run_sim.sh
├── syn_vivado.v
├── syn_yosys.v
├── vivado.jou
├── vivado.log
├── vivado_testbench.v
├── yosys_testbench.v

Expected Behavior

The simulation before and after synthesis is consistent, just like Vivado

image

Actual Behavior

Inconsistent output before and after synthesis

KrystalDelusion commented 1 month ago

If this is the underlying cause for #4369 can you close that issue since this is more clear about the exact problem. This is still not the best minimal example, but it is better.

Narrowing it down even further by calling read_verilog -dump_vlog1 rtl.v, I believe this may be related to the line case (|({(wire2[1'b 1:1'b 0])~^(0'b )})), where it is trying to xnor against a zero bit value, but the output of write_verilog is 1'b0 and that difference is being picked up down the line when the expression is evaluated. I will also note that when assigning to a wire first (and getting e.g. assign _15_ = (wire2[1:0])~^(0'b ); in the vlog1 dump) does not result in a difference in behaviour, so it is specific to the evaluation of the if and not the 0'b itself.

WeneneW commented 1 month ago

If this is the underlying cause for #4369 can you close that issue since this is more clear about the exact problem. This is still not the best minimal example, but it is better.

Narrowing it down even further by calling read_verilog -dump_vlog1 rtl.v, I believe this may be related to the line case (|({(wire2[1'b 1:1'b 0])~^(0'b )})), where it is trying to xnor against a zero bit value, but the output of write_verilog is 1'b0 and that difference is being picked up down the line when the expression is evaluated. I will also note that when assigning to a wire first (and getting e.g. assign _15_ = (wire2[1:0])~^(0'b ); in the vlog1 dump) does not result in a difference in behaviour, so it is specific to the evaluation of the if and not the 0'b itself.

Thank you very much for your reply. This is indeed the reduced test case version of my previous question, and I have already closed the previous question. Best wishes to you

nakengelhardt commented 1 month ago

Yeah, that's an error in the frontend: according to 1364-2005 5.2.3.3,

The null string ("") shall be considered equivalent to the ASCII NUL ("\0"), which has a value zero (0), which is different from a string "0".

Which would make it equivalent to 8'b0.

(cf #3103 fixing this in the backend, it seems the same misunderstanding is still present in the frontend)

WeneneW commented 1 month ago

'

Thank you for your reply