nickg / nvc

VHDL compiler and simulator
https://www.nickg.me.uk/nvc/
GNU General Public License v3.0
636 stars 80 forks source link

Verilog: support primitives #937

Closed avelure closed 2 months ago

avelure commented 3 months ago

We have some projects that use recent Microsemi devices and they have stopped providing device libraries in VHDL. As I understand the intention of the verilog support was to be able to simulate with vendor libraries that are not provided in vhdl. Vendor device libraries often contain custom primitives that define their custom logic elements. It would help further testing of the verilog support if these were supported.

This is a snippet from the Microsemi polarfire library


//---------------------------------------------------------------------
// primitibe module (UDP_DFF) state table definition
// FUNCTION : POSITIVE EDGE TRIGGERED D FLIP-FLOP 
//                     WITH ACTIVE LOW ASYNCHRONOUS LOAD
//                     Q OUTPUT UDP.
//----------------------------------------------------------------------
primitive UDP_DFF (Q, AL, AD, SD, CLK);
  output Q;
  input AL, AD, SD, CLK;
  reg Q;
  table
  //AL    AD   SD   CLK  :  Qn  : Qn+1
     0     0    ?     ?  :  ?   :  0  ;   // Async Data
     0     1    ?     ?  :  ?   :  1  ;
     0     x    ?     ?  :  ?   :  x  ;    
    (?0)   0    ?     ?  :  ?   :  0  ;   // Update output for 
    (?0)   1    ?     ?  :  ?   :  1  ;   // Falling Edge
    (?0)   x    ?     ?  :  ?   :  x  ;
     0    (?0)  ?     ?  :  ?   :  0  ;   // Update output for 
     0    (?1)  ?     ?  :  ?   :  1  ;   // Changes in AD
     0    (?x)  ?     ?  :  ?   :  x  ;
    (?x)   0    ?     ?  :  0   :  0  ;   // Reducing Pessimism due to
    (?x)   1    ?     ?  :  1   :  1  ;   // transitions to X on AL
    (?x)   x    ?     ?  :  ?   :  x  ;                         // I can remove this line
    (?x)   ?    ?     ?  :  x   :  x  ;                         // I can remove this line
    (?x)   0    ?     ?  :  1   :  x  ;                         // I can remove this line
    (?x)   1    ?     ?  :  0   :  x  ;                         // I can remove this line
     x     x    ?     ?  :  ?   :  x  ;                         // I can remove this line
     x     0    ?     ?  :  1   :  x  ;                         // I can remove this line
     x     1    ?     ?  :  0   :  x  ;                         // I can remove this line
     1     ?    0   (01) :  ?   :  0  ;   // Sync Data
     1     ?    1   (01) :  ?   :  1  ;
     1     ?    x   (01) :  ?   :  x  ;
     1     ?    0   (x1) :  0   :  0  ;
     1     ?    1   (x1) :  1   :  1  ;
     1     ?    ?   (x1) :  x   :  x  ;                         // I can remove this line
     1     ?    x   (x1) :  ?   :  x  ;                         // I can remove this line
     1     ?    0   (x1) :  1   :  x  ;                         // I can remove this line
     1     ?    1   (x1) :  0   :  x  ;                         // I can remove this line
     x     1    1   (01) :  ?   :  1  ;
     x     0    0   (01) :  ?   :  0  ;
     x     ?    x   (01) :  ?   :  x  ;
     x     x    ?   (01) :  ?   :  x  ;
     x     1    0   (01) :  ?   :  x  ;
     x     0    1   (01) :  ?   :  x  ;
     x     0    0   (x1) :  0   :  0  ;
     x     1    1   (x1) :  1   :  1  ;
     x     ?    ?   (x1) :  x   :  x  ;
     x     ?    x   (x1) :  ?   :  x  ;
     x     x    ?   (x1) :  ?   :  x  ;
     x     ?    1   (x1) :  0   :  x  ;
    (?1)   ?    ?     ?  :  ?   :  -  ;   // Ignore Rising Edge on AL
     0     ?    ?     *  :  ?   :  -  ;   // Ignore Changes on CLK during Async
     ?     ?    ?   (?0) :  ?   :  -  ;   // Ignore CLK falling edge
     1     *    ?     ?  :  ?   :  -  ;   // Ignore changes on AD during Sync
     ?     ?    *     ?  :  ?   :  -  ;   // Ignore changes on SD during Sync
  endtable
endprimitive

//---------------------------------------------------------------------
// primitibe module (UDP_DL) state table definition
// FUNCTION : POSITIVE EDGE TRIGGERED D LATCH 
//                     WITH ACTIVE LOW ASYNCHRONOUS LOAD
//                     Q OUTPUT UDP.
//----------------------------------------------------------------------
primitive UDP_DL (Q, AL, AD, SD, CLK);
  output Q;
  input AL, AD, SD, CLK;
  reg Q;
  table
  //AL    AD   SD   CLK  :  Qn  : Qn+1
     0     0    ?     ?  :  ?   :  0  ;
     0     1    ?     ?  :  ?   :  1  ;
     0     x    ?     ?  :  ?   :  x  ;
    (?1)   ?    ?     ?  :  ?   :  -  ;
    (?0)   0    ?     ?  :  ?   :  0  ;
    (?0)   1    ?     ?  :  ?   :  1  ;
    (?0)   x    ?     ?  :  ?   :  x  ;
     0    (?0)  ?     ?  :  ?   :  0  ;
     0    (?1)  ?     ?  :  ?   :  1  ;
     0    (?x)  ?     ?  :  ?   :  x  ;
    (?x)   0    ?     ?  :  0   :  0  ;
    (?x)   1    ?     ?  :  1   :  1  ;
    (?x)   x    ?     ?  :  ?   :  x  ;
    (?x)   ?    ?     ?  :  x   :  x  ;
    (?x)   0    ?     ?  :  1   :  x  ;
    (?x)   1    ?     ?  :  0   :  x  ;
     x     0    ?     b  :  0   :  0  ;
     x     1    ?     b  :  1   :  1  ;
     x     x    ?     b  :  ?   :  x  ;
     x     ?    ?     b  :  x   :  x  ;
     x     0    ?     b  :  1   :  x  ;
     x     1    ?     b  :  0   :  x  ;
     1     ?    0     1  :  ?   :  0  ;
     1     ?    1     1  :  ?   :  1  ;
     1     ?    x     1  :  ?   :  x  ;
     1     ?   (?0)   1  :  ?   :  0  ;
     1     ?   (?1)   1  :  ?   :  1  ;
     1     ?   (?x)   1  :  ?   :  x  ;
     1     ?    0  (?1)  :  ?   :  0  ;
     1     ?    1  (?1)  :  ?   :  1  ;
     1     ?    x  (?1)  :  ?   :  x  ;
     1     ?    0  (?x)  :  0   :  0  ;
     1     ?    1  (?x)  :  1   :  1  ;
     1     ?    ?  (?x)  :  x   :  x  ;
     1     ?    x  (?x)  :  ?   :  x  ;
     1     ?    0  (?x)  :  1   :  x  ;
     1     ?    1  (?x)  :  0   :  x  ;
     1     ?    0     x  :  0   :  0  ;
     1     ?    1     x  :  1   :  1  ;
     1     ?    x     x  :  ?   :  x  ;
     1     ?    ?     x  :  x   :  x  ;
     1     ?    1     x  :  0   :  x  ;
     1     ?    0     x  :  1   :  x  ;
     x     1    1     x  :  1   :  1  ;
     x     0    0     x  :  0   :  0  ;
     0     ?    ?     *  :  ?   :  -  ;
     ?     ?    ?  (?0)  :  ?   :  -  ;
     1     *    ?     ?  :  ?   :  -  ;
     0     ?    *     ?  :  ?   :  -  ;
     1     ?    *     0  :  ?   :  -  ;
  endtable
endprimitive

//---------------------------------------------------------------------
// primitibe module (UDP_MUX2) state table definition
// FUNCTION : 2-to-1 MULTIPLEXER
//                     SL = 0  --> Q = A
//                     SL = 1  --> Q = B
//----------------------------------------------------------------------
primitive UDP_MUX2 (Q, A, B, SL);
  output Q;
  input A, B, SL;
  table
    //  A   B   SL  :   Q
    0   0   ?   :   0 ;
    1   1   ?   :   1 ;
    0   ?   0   :   0 ;
    1   ?   0   :   1 ;
    ?   0   1   :   0 ;
    ?   1   1   :   1 ;
    x   ?   0   :   x ;
    ?   x   1   :   x ;
    1   0   x   :   x ;
    0   1   x   :   x ;
  endtable
endprimitive

//---------------------------------------------------------------------
// primitibe module (UDP_GBLAT) state table definition
// FUNCTION : POSITIVE EDGE TRIGGERED D LATCH 
//                     Q OUTPUT UDP.
//----------------------------------------------------------------------
primitive UDP_GBLAT (Q, D, G);
  output Q;
  input D, G;
  reg Q;
  table
  //D    G   :  Qn  : Qn+1
    ?    0   :  ?   :  -  ;
    0    1   :  ?   :  0  ;
    1    1   :  ?   :  1  ;
    x    1   :  ?   :  x  ;
    0    x   :  0   :  0  ;
    1    x   :  1   :  1  ;
    *    0   :  ?   :  -  ;
    0  (01)  :  ?   :  0  ;
    1  (01)  :  ?   :  1  ;
    x  (01)  :  ?   :  x  ;
    ?  (?0)  :  ?   :  -  ;
    0  (?x)  :  0   :  0  ;
    1  (?x)  :  1   :  1  ;
  (?1)   1   :  ?   :  1  ;
  (?0)   1   :  ?   :  0  ;
  (?x)   1   :  ?   :  x  ;
  endtable
endprimitive

//---------------------------------------------------------------------
// primitibe module (UDP_GBLAT) state table definition
// FUNCTION : POSITIVE EDGE TRIGGERED D LATCH
//                     Q OUTPUT UDP.
//----------------------------------------------------------------------
primitive UDP_GBLAT_T (Q, D, G);
  output Q;
  input D, G;
  reg Q;

  initial
    Q = 1'b1;
  table
  //D    G   :  Qn  : Qn+1
    ?    0   :  ?   :  -  ;
    0    1   :  ?   :  0  ;
    1    1   :  ?   :  1  ;
    x    1   :  ?   :  x  ;
    0    x   :  0   :  0  ;
    1    x   :  1   :  1  ;
    *    0   :  ?   :  -  ;
    0  (01)  :  ?   :  0  ;
    1  (01)  :  ?   :  1  ;
    x  (01)  :  ?   :  x  ;
    ?  (?0)  :  ?   :  -  ;
    0  (?x)  :  0   :  0  ;
    1  (?x)  :  1   :  1  ;
  (?1)   1   :  ?   :  1  ;
  (?0)   1   :  ?   :  0  ;
  (?x)   1   :  ?   :  x  ;
  endtable
endprimitive

//---------------------------------------------------------------------
// primitibe module (UDP_BUFF) state table definition
// FUNCTION : BUFF
//----------------------------------------------------------------------
primitive UDP_BUFF (Y, A);
  output Y;
  input  A;

  table
  //A     :  Y
    0  :  0;
    1  :  1;
    x  :  x;
  endtable
endprimitive

/*--------------------------------------------------------------------
NAME : SLE_Prim
TYPE : FF/Latch
EQN  : Q = FF/LATCH
---------------------------------------------------------------------*/
`suppress_faults
`enable_portfaults
`celldefine
`delay_mode_path
`timescale 1 ns / 10 ps
module SLE_Prim (output Q,
                 input ADn,
                 input ALn,
                 input CLK,
                 input D,
                 input LAT,
                 input SD,
                 input EN,
                 input SLn);
  wire ALn_int;

  assign ALn_int = ALn;

  UDP_MUX2 mux_0(SYNC, SD, D, SLn);
  UDP_MUX2 mux_1(DATA, Q, SYNC, EN);

  UDP_DFF  DFF_0(QFF, ALn_int, ADn_, DATA, CLK);
  UDP_DL   DL_1(QL, ALn_int, ADn_, DATA, CLK);
  UDP_MUX2 mux_2(Q, QFF, QL, LAT);  

  not  U1(ADn_, ADn);
endmodule
`endcelldefine
`disable_portfaults
`nosuppress_faults

Here is the complete file and the corresponding vhdl component file, but I guess there might be more things the parser fails on once the primitive support is added. For instance handling of timing and setup/hold checks is not really neceseary at the present time as long as the logic operates correctly. polarfire.zip

nickg commented 3 months ago

I made a start on this so now the following simple example works:

test/regress/mixed2.v test/regress/mixed2.vhd

Interestingly Questa doesn't allow instantiation of UDPs directly from VHDL:

# ** Error: /home/nick/nvc/test/regress/mixed2.vhd(16): (vopt-3352) VHDL instantiation of Verilog UDP 'uut' is not allowed.
#         Region: /mixed2
Blebowski commented 3 months ago

I made a start on this so now the following simple example works:

test/regress/mixed2.v test/regress/mixed2.vhd

Interestingly Questa doesn't allow instantiation of UDPs directly from VHDL:

# ** Error: /home/nick/nvc/test/regress/mixed2.vhd(16): (vopt-3352) VHDL instantiation of Verilog UDP 'uut' is not allowed.
#         Region: /mixed2

I think this comes from use-cases of mixed-language simulation in a "typical commercial design". Likely, there is VHDL / Verilog design with some IPs in the other language. So you only need to instantiate "modules/entities" in the other language. UDPs are most of the time used as part of standard cell models, so they end-up instantiated in Verilog only. To get this use-case, one would need to have VHDL cell models using UDPs. I have not seen such approach.

What is surprising, is that Questa does not support it. Out of the big-3 simulators, it has the best mixed language support when it comes to external names / verilog dot notation and VPI/VHPI referencing constructs in the other language... That being said, I definitely like that NVC will support it.

avelure commented 3 months ago

Great! In the vendor libraries the UDPs seem to always be wrapped in some other cells as far as I have seen, even for ASIC libraries. Like a generic D-flipflop from the polarfire library is DFN1->SLE->SLE_PRIM->UDP_DFF

nickg commented 2 months ago

The snippet you posted above can be compiled now, here's the VHDL I'm using to test it.

library ieee;
use ieee.std_logic_1164.all;

entity polarfire_test is
end entity;

architecture test of polarfire_test is
    component SLE_Prim is
        port ( Q : out std_logic;
               ADn, ALn, CLK, D, LAT, SD, EN, SLn : in std_logic );
    end component;

    signal Q                                  : std_logic;
    signal ADn, ALn, CLK, D, LAT, SD, EN, SLn : std_logic := '1';
begin

    u: component SLE_Prim
        port map (Q, ADn, ALn, CLK, D, LAT, SD, EN, SLn);

    CLK <= not CLK after 5 ns;

    check: process is
    begin
        wait until falling_edge(CLK);
        wait until falling_edge(CLK);
        assert Q = '1';
        wait until falling_edge(CLK);
        D <= '0';
        wait until falling_edge(CLK);
        assert Q = '0';
        D <= '1';
        wait until falling_edge(CLK);
        assert Q = '1';
        D <= '0';
        EN <= '0';
        wait until falling_edge(CLK);
        assert Q = '1';
        wait;
    end process;

end architecture;

image

There's likely to be bugs, but I get the same output in Questa. The full zip file you attached doesn't compile yet and supporting all the different constructs in there is quite a way off. I think it would be best to raise separate tickets for other devices in that file as you need them with reduced test cases, similar to this one.

Blebowski commented 2 months ago

Hi @nickg ,

what about adding to the documentation a table with Verilog standard listed chapter-by-chapter, and indication if each feature is supported ? Maybe something similar to: SLANG Language support I don't mean breakdown for SV 2017 as slang, I mean break-down for Verilog 95. The "new" features e.g. in Verilog 2001 could be then documented in the same way as VHDL 2008 and VHDL 2019 are now.

Maybe something similar would be also use-full for VHDL 93 standard since currently manual only says:

NVC supports almost all of IEEE 1076-1993 and 1076-2002. Please report any missing or incorrectly implemented features.

I could do this step-by-step (It would serve me as a push to slowly read the whole Verilog standard step-by-step), but I don't want to do that without consulting first...

Btw. similar thing could be done for VHPI features and PSL supported constructs.

nickg commented 2 months ago

I'm not sure whether it's worth it as this point because almost everything is unsupported. A feature table for PSL might be more useful though.

For Verilog there's also https://chipsalliance.github.io/sv-tests-results/ which has the benefit of being automated. I don't know how difficult it would be to set that up to run with NVC.

FWIW I've actually switched to using the SystemVerilog LRM rather than Verilog 95/2001 as I find it easier to read (e.g. all the grammar productions come from there). AFAIK it includes all the Verilog 95 features as a subset and it makes it easier to adopt some SV features in the future if needed.

Blebowski commented 2 months ago

I'm not sure whether it's worth it as this point because almost everything is unsupported. A feature table for PSL might be more useful though.

I agree the "all-red" table is almost use-less for users when it is "all-red". OTOH, keeping track of what is implemented may be easier then. With each new feature coming, its just clicking a check-box, like you do with VHDL 2019 now. PSL table would be definitely usefull.

For Verilog there's also https://chipsalliance.github.io/sv-tests-results/ which has the benefit of being automated. I don't know how difficult it would be to set that up to run with NVC.

I have seen this couple of times. I don't know the effort to bring-it up though. Still, I think having a table in own documentation makes better impression for user and helps to keep the features "organized". This is what I lack in GHDL. To find out if it supports your code, you need to try to use it. That may take longer time than just searching docs. But maybe its just me being pedant about documentation.

FWIW I've actually switched to using the SystemVerilog LRM rather than Verilog 95/2001 as I find it easier to read (e.g. all the grammar productions come from there). AFAIK it includes all the Verilog 95 features as a subset and it makes it easier to adopt some SV features in the future if needed.

I see. AFAIK, SV is backwards compatible. A tricky thing might then be to distinguish "what shall be supported" when you set certain --std option. Once you implement most according to SV LRM, then introducing --std=v1995 or --std=v2001 may be pain to get right.

avelure commented 2 months ago

Great work! I'll try to figure out the parts of the library file I need. It is for a FIFO, so might not be the easiest starting point.

It doesn't seem overly complicated to add a new tool to sv-tests. A short run script is needed like this https://github.com/chipsalliance/sv-tests/blob/master/tools/runners/Icarus.py and a few lines in a makefile to fetch and build https://github.com/chipsalliance/sv-tests/blob/master/tools/runners.mk