How to add delay statements to verilog code (e.g. #x)?

I tried to add a delay statement to a Verilog code block but it seems that the # synthax is not recognized.

It seems that in the most recent versions, Verilator supports delay statements.
To do so the argument “–timing” should be added to the Verilator command line argument.
Can this be done in Qspice?

Is there a way to implement a delay directly in C++?

I’ve never gotten Verilator to support timing. In fact, Verilog is usually presented as being capable of synthesizable Verilog, meaning no timing, even though behavior Verilog constructs are supported.

But the Verilator is one rev back from current, so I’ll be updating it and see if it can do timing. I’m hoping $monitor works now. It’s documented in the Verilator documentation, but from looking at the Verilator source code, it seems to be only partially implemented. It seems that some Verilator features are only supported if used in conjunction with SystemC. I’m bypassing SystemC and going straight to the metal in QSPICE.

–Mike

Thanks Mike. For the time being I have implemented it in C++ and it works greatly. New questions arose, but will post them separately.

Is there any update on the ability to use # delays in Verilog?

The ability to control timing and propagation delays would be very useful.

@Egrana, do you mind sharing how you implemented the delay in C++?

Following the thread.

When I tried similar to what @Egrana has in the first comment., I do not get syntax errors, however the delay is not executed

Are there any updates on the best way to implement delay in Verilog?

Sure, it is a long time I don’t work on the code but I remember it was working at the time.
The delay is embedded in this dead time generator block I built for gate driver’s logics.
Let me know if you need further assistance.

// Automatically generated C++ file on Thu Aug 17 09:26:31 2023
//
// To build with Digital Mars C++ Compiler:
//
//    dmc -mn -WD deatimegeneratorc.cpp kernel32.lib

#include <malloc.h>

union uData
{
   bool b;
   char c;
   unsigned char uc;
   short s;
   unsigned short us;
   int i;
   unsigned int ui;
   float f;
   double d;
   long long int i64;
   unsigned long long int ui64;
   char *str;
   unsigned char *bytes;
};

// int DllMain() must exist and return 1 for a process to load the .DLL
// See https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain for more information.
int __stdcall DllMain(void *module, unsigned int reason, void *reserved) { return 1; }

void bzero(void *ptr, unsigned int count)
{
   unsigned char *first = (unsigned char *) ptr;
   unsigned char *last  = first + count;
   while(first < last)
      *first++ = '\0';
}

// #undef pin names lest they collide with names in any header file(s) you might include.
#undef in
#undef out

struct sDEATIMEGENERATORC
{
   int state;
   double Tstart_RE;
   double Tstart_FE;
};

extern "C" __declspec(dllexport) void deatimegeneratorc(struct sDEATIMEGENERATORC **opaque, double t, union uData *data)
{
   bool  in  = data[0].b; // input
   double TDELAY_RE = data[1].d;
   double TDELAY_FE = data[2].d;
   bool &out = data[3].b; // output

   if(!*opaque)
   {
      *opaque = (struct sDEATIMEGENERATORC *) malloc(sizeof(struct sDEATIMEGENERATORC));
      bzero(*opaque, sizeof(struct sDEATIMEGENERATORC));
   }
   struct sDEATIMEGENERATORC *instance = *opaque;

switch(instance->state)
         {
            case  0:
              {
              if(in) // if in == 1
               {
               instance->Tstart_RE=t; //Save the time when it changes from 0 to 1
               instance->state=1;
               out=0;
               }
              }
              break;
            case 1:
               if(in && (t-instance->Tstart_RE)>TDELAY_RE) // if in still = 1 and if deltaT is higher then Tdelay (we waited the delay time)
               {
                instance->state=2;
                out=1;
               }
               if(!in) //if in == 0 safety measure to be sure that if the input changed before Tdelay we don't set the output to 1 but keeps it to 0
               {
               instance->state=0;
               out=0;
               }
              break;
             case 2:
              if(!in) //if in == 0 keeps the output to 1 but save the time of the falling edge
              {
              instance->Tstart_FE=t;
              instance->state=3;
              out=1;
              }
              break;
            case 3:
               if(!in && (t-instance->Tstart_FE)>TDELAY_FE) // if in still = 0 and if deltaT is higher then Tdelay (we waited the delay time)
               {
                instance->state=0;
                out=0;
               }
               if(in) //if in == 0 safety measure to be sure that if the input changed before Tdelay we don't set the output to 1 but keeps it to 0
               {
               instance->state=1;
               out=1;
               }

              break;



            default: // initiate the case value and the output
            instance->state=0;
            out=0;
            break;
        }

}

extern "C" __declspec(dllexport) void Trunc(struct sDEATIMEGENERATORC *inst, double t, union uData *data, double *timestep)
{ // limit the timestep to a tolerance if the circuit causes a change in struct sDEATIMEGENERATORC
   const double ttol = 1e-9;
   if(*timestep > ttol)
   {
      bool &out = data[3].b; // output

      // Save output vector
      const bool _out = out;

      struct sDEATIMEGENERATORC tmp = *inst;
      deatimegeneratorc(&(&tmp), t, data);
      if(tmp.state != inst->state) // implement a meaningful way to detect if the state has changed
         *timestep = ttol;

      // Restore output vector
      out = _out;
   }
}

extern "C" __declspec(dllexport) void Destroy(struct sDEATIMEGENERATORC *inst)
{
   free(inst);
}

Using a reference clock input, could you try something like

for(i=0;i<100;i=i+1) @(posedge clk)

Verilator does claim to support temporal operators

https://verilator.org/guide/latest/languages.html#time