website statistics

RAM64X1S as a sine wave ROMΒΆ

Xilinx FPGAs have a RAM64X1S primitive, which a simple 64-bit single-ported RAM. Because it maps directly to on-chip resources, it’s incredibly compact and fast. Here it is being used as a ROM lookup for an 8-bit sine wave table. The sine wave has 64 values, each is an 8-bit signed value (see Signed comparison in Verilog).

_images/sintab.png

The Verilog for the sine wave ROM looks like this:

wire [5:0] w;   // input wave counter 0-63
wire signed [7:0] sin; // Output sine value

RAM64X1S #(.INIT(64'b0000010010101100100110101001000000000100101011001001101010010000) /* 0 */
) sin0(.O(sin[0]), .A0(w[0]), .A1(w[1]), .A2(w[2]), .A3(w[3]), .A4(w[4]), .A5(w[5]), .D(0), .WCLK(clk), .WE(0));
RAM64X1S #(.INIT(64'b0000011001000001110000010011000000000010111011010101101110100000) /* 1 */
) sin1(.O(sin[1]), .A0(w[0]), .A1(w[1]), .A2(w[2]), .A3(w[3]), .A4(w[4]), .A5(w[5]), .D(0), .WCLK(clk), .WE(0));
RAM64X1S #(.INIT(64'b0101001000001011111010000010010101010100111001100011001110010101) /* 2 */
) sin2(.O(sin[2]), .A0(w[0]), .A1(w[1]), .A2(w[2]), .A3(w[3]), .A4(w[4]), .A5(w[5]), .D(0), .WCLK(clk), .WE(0));
RAM64X1S #(.INIT(64'b0110010010100111111100101001001100110010010010000000100100100110) /* 3 */
) sin3(.O(sin[3]), .A0(w[0]), .A1(w[1]), .A2(w[2]), .A3(w[3]), .A4(w[4]), .A5(w[5]), .D(0), .WCLK(clk), .WE(0));
RAM64X1S #(.INIT(64'b0010110110011111111111001101101001011011011100000000011101101101) /* 4 */
) sin4(.O(sin[4]), .A0(w[0]), .A1(w[1]), .A2(w[2]), .A3(w[3]), .A4(w[4]), .A5(w[5]), .D(0), .WCLK(clk), .WE(0));
RAM64X1S #(.INIT(64'b0001110001111111111111110001110001100011100000000000000011100011) /* 5 */
) sin5(.O(sin[5]), .A0(w[0]), .A1(w[1]), .A2(w[2]), .A3(w[3]), .A4(w[4]), .A5(w[5]), .D(0), .WCLK(clk), .WE(0));
RAM64X1S #(.INIT(64'b0000001111111111111111111110000001111100000000000000000000011111) /* 6 */
) sin6(.O(sin[6]), .A0(w[0]), .A1(w[1]), .A2(w[2]), .A3(w[3]), .A4(w[4]), .A5(w[5]), .D(0), .WCLK(clk), .WE(0));
RAM64X1S #(.INIT(64'b0000000000000000000000000000000001111111111111111111111111111111) /* 7 */
) sin7(.O(sin[7]), .A0(w[0]), .A1(w[1]), .A2(w[2]), .A3(w[3]), .A4(w[4]), .A5(w[5]), .D(0), .WCLK(clk), .WE(0));

Of course, generating this block of values would be a lot of work by hand. This Python script does it:

import math

sintab = [int(127 * math.sin(2 * math.pi * i / 64.)) for i in range(64)]
for b in range(8):
    print "RAM64X1S #(.INIT(64'b" + "".join([str((s >> b) & 1) for s in sintab]) + ") /* %d */" % b;
    print ") sin%d(.O(sin[%d]), .A0(w[0]), .A1(w[1]), .A2(w[2]), .A3(w[3]), .A4(w[4]), .A5(w[5]), .D(0), .WCLK(clk), .WE(0));" % (b,b)

More VHDL, Verilog and FPGA notes.