Create a Sine Wave Generator Using SystemVerilog

Two capabilities in SystemVerilog allow for the creation of a module that can produce a sine wave as an output: the ability to pass real values through port connections and DPI.

Obviously, to produce a sine wave, you need access to the sin function. This is where DPI is handy to add the math functions to your simulation. Here is an example of a package I created to contain the math functions:

package math_pkg;

//import dpi
task C Name = SV function name

import
"DPI" pure function real cos (input real rTheta);

import
"DPI" pure function real sin (input real rTheta);

import
"DPI" pure function real log (input real rVal);

import
"DPI" pure function real log10 (input real rVal);

endpackage : math_pkg

The import"DPI" construct defines a new function that you can use in your code that refers to a C function. In the case of the math functions listed above, they already exist in the libmath.so library built into Linux and so there is no additional code required.
Now that I have my math functions, I can create my module.

module sine_wave(output real
sine_out);

import math_pkg::*;

parameter
sampling_time = 5;

const real pi =
3.1416;

real time_us, time_s ;

bit sampling_clock;

real freq = 20;

real offset = 2.5;

real ampl = 2.5;

always sampling_clock
= #(sampling_time) ~sampling_clock;

always
@(sampling_clock) begin

time_us =
$time/1000;

time_s =
time_us/1000000;

end

assign sine_out =
offset + (ampl * sin(2*pi*freq*time_s));

endmodule

Here I have used import in a different context. In this case import is used to make the code in my package available to the scope in which I import it. Now when I call the sinn function, it will use the DPI code from math_pkg to execute the function.

The sine_wave module also shows the use of passing a real value through a port. The output sine_out is of type real and is computed using the sin function.

SystemVerilog allows a real variable to be used as a port. The limitation is that a real variable can only be driven by a single driver. If that is a problem, you can make the module a Verilog AMS module and define the real variable as a wreal (real wire). By using wreal, you can have multiple drivers and use a variety of resolution types to solve any conflicts.

If we are at it already, here is the COS function, Root of 2, Natural base LOG, LOG 2 and LOG 10 (best to have all these and the SIN function together in the same file since they may call on each other):