Computing sine and cosine on FPGA typically involves CORDIC. However, James Bowman coded some efficient implementations of the transcendental functions using Chebyshev polynomial approximation. As opposed to CORDIC which takes several iterations (typically the result converge of one bit per iteration), this design uses only combinatorial statements.

The isin Verilog module computes 16-bit signed output from a 16-bit signed input. Its use can be demonstrated on the TinyFPGA BX:

module top (
    input CLK,    // 16MHz clock
    output USBPU,  // USB pull-up resistor
    output PIN_1,
    output PIN_2,
    output PIN_3,
    output PIN_4,
    output PIN_5,
    output PIN_6,
    output PIN_7,
    output PIN_8
);
    // drive USB pull-up resistor to '0' to disable USB
    assign USBPU = 0;

    ////////
    // generate a sine wave
    ////////

    reg [23:0] f_counter = 0;
    always @(posedge CLK) begin
        f_counter <= f_counter + 1;
    end

    wire [15:0] sin;
    isin mySin (
        .x(f_counter[23:8]),
        .s(sin)
    );

    assign {PIN_8, PIN_7, PIN_6, PIN_5, PIN_4, PIN_3, PIN_2, PIN_1} = {~sin[15],sin[14:8]};

endmodule

This generates a digital signal on PIN_1~8. Since isin produces 16-bit signed results (from -32768 to 32767), the output is shifted to the positive scale (from 0 to 65535). This operation is simply done by inverting the MSB ({~sin[15],sin[14:8]}).

glitched sin wave

Due to two's complement representation, using signed values produce this funny-looking wave.

Using a simple testbench is helpful to validate (even simple) designs. As shown below, the result of the simulation can be exported to GTKWave. The design behaves as expected.

gtkwave

The next step is to convert this signal to the analog domain and analyze it using an oscilloscope. Since the TinyFPGA's ICE40LP8K doesn't integrate a DAC, we can use a discrete DAC or a R-2R resistor ladder network like this:

r2r circuit

4610X-R2R-103LF

While it is fairly easy to make a resistor ladder from a bunch of resistors, I used a 8-bit R2R conveniently packaged in SIP.

I hooked everything on a breadboard. Since ground pin is next to PIN_1 on the TinyFPGA BX, there is no need for any wiring (PIN_9 analog output can be left floating on the FPGA).

tinyfpga

Below is the final wave as shown on a cheap portable oscilloscope:

sin wave

If you prefer to describe your digital designs using Python, I reimplemented the isin/ìcos module using Migen. A possible upgrade of this module would be to support arbitrary precision—which would be harder to achieve in pure Verilog.