Skip to content

Instantly share code, notes, and snippets.

@pavel-kirienko
Created August 6, 2025 17:17
Show Gist options
  • Save pavel-kirienko/e5fa3ad59dfe67ab8a2ed91023f187de to your computer and use it in GitHub Desktop.
Save pavel-kirienko/e5fa3ad59dfe67ab8a2ed91023f187de to your computer and use it in GitHub Desktop.
10-bit sin(x) lookup table in Verilog based on a 256x9-bit ROM cell
/// Stateless, purely combinational sin(x) look-up table. Can be used as the phase-to-amplitude converter of an NCO.
/// It stores 256 9-bit items in a look-up table representing the first quarter of the wave.
/// The input x is expected to be normalized into [0, 1024) that maps to [0, 2 pi).
/// The signed output is normalized into [-511, +511] (sic! -512 not used).
module sine_lookup_1024(
input wire [9:0] x,
output wire signed [9:0] out
);
reg [8:0] entry; // Not an actual register, just a wire assignable from the always block.
assign out = x[9] ? -$signed({1'b0, entry}) : $signed({1'b0, entry}); // The MSb represents the half-wave index.
wire [7:0] index = x[8] ? ~x[7:0] : x[7:0]; // The second-to-MSb is set if the quarter needs to be inverted.
always @* begin // always_comb
// The LUT was generated in Python:
// half_period_table = [round(math.sin(math.pi * 0.5 * (x / 256)) * 511) for x in range(256)]
// for i,e in enumerate(half_period_table):
// print(f"{i:<4d}: entry = {e:4d};")
case(index)
0 : entry = 0;
1 : entry = 3;
2 : entry = 6;
3 : entry = 9;
4 : entry = 13;
5 : entry = 16;
6 : entry = 19;
7 : entry = 22;
8 : entry = 25;
9 : entry = 28;
10 : entry = 31;
11 : entry = 34;
12 : entry = 38;
13 : entry = 41;
14 : entry = 44;
15 : entry = 47;
16 : entry = 50;
17 : entry = 53;
18 : entry = 56;
19 : entry = 59;
20 : entry = 63;
21 : entry = 66;
22 : entry = 69;
23 : entry = 72;
24 : entry = 75;
25 : entry = 78;
26 : entry = 81;
27 : entry = 84;
28 : entry = 87;
29 : entry = 90;
30 : entry = 94;
31 : entry = 97;
32 : entry = 100;
33 : entry = 103;
34 : entry = 106;
35 : entry = 109;
36 : entry = 112;
37 : entry = 115;
38 : entry = 118;
39 : entry = 121;
40 : entry = 124;
41 : entry = 127;
42 : entry = 130;
43 : entry = 133;
44 : entry = 136;
45 : entry = 139;
46 : entry = 142;
47 : entry = 145;
48 : entry = 148;
49 : entry = 151;
50 : entry = 154;
51 : entry = 157;
52 : entry = 160;
53 : entry = 163;
54 : entry = 166;
55 : entry = 169;
56 : entry = 172;
57 : entry = 175;
58 : entry = 178;
59 : entry = 181;
60 : entry = 184;
61 : entry = 187;
62 : entry = 190;
63 : entry = 193;
64 : entry = 196;
65 : entry = 198;
66 : entry = 201;
67 : entry = 204;
68 : entry = 207;
69 : entry = 210;
70 : entry = 213;
71 : entry = 216;
72 : entry = 218;
73 : entry = 221;
74 : entry = 224;
75 : entry = 227;
76 : entry = 230;
77 : entry = 233;
78 : entry = 235;
79 : entry = 238;
80 : entry = 241;
81 : entry = 244;
82 : entry = 246;
83 : entry = 249;
84 : entry = 252;
85 : entry = 255;
86 : entry = 257;
87 : entry = 260;
88 : entry = 263;
89 : entry = 265;
90 : entry = 268;
91 : entry = 271;
92 : entry = 273;
93 : entry = 276;
94 : entry = 279;
95 : entry = 281;
96 : entry = 284;
97 : entry = 286;
98 : entry = 289;
99 : entry = 292;
100 : entry = 294;
101 : entry = 297;
102 : entry = 299;
103 : entry = 302;
104 : entry = 304;
105 : entry = 307;
106 : entry = 309;
107 : entry = 312;
108 : entry = 314;
109 : entry = 317;
110 : entry = 319;
111 : entry = 322;
112 : entry = 324;
113 : entry = 327;
114 : entry = 329;
115 : entry = 331;
116 : entry = 334;
117 : entry = 336;
118 : entry = 338;
119 : entry = 341;
120 : entry = 343;
121 : entry = 345;
122 : entry = 348;
123 : entry = 350;
124 : entry = 352;
125 : entry = 355;
126 : entry = 357;
127 : entry = 359;
128 : entry = 361;
129 : entry = 364;
130 : entry = 366;
131 : entry = 368;
132 : entry = 370;
133 : entry = 372;
134 : entry = 374;
135 : entry = 377;
136 : entry = 379;
137 : entry = 381;
138 : entry = 383;
139 : entry = 385;
140 : entry = 387;
141 : entry = 389;
142 : entry = 391;
143 : entry = 393;
144 : entry = 395;
145 : entry = 397;
146 : entry = 399;
147 : entry = 401;
148 : entry = 403;
149 : entry = 405;
150 : entry = 407;
151 : entry = 409;
152 : entry = 410;
153 : entry = 412;
154 : entry = 414;
155 : entry = 416;
156 : entry = 418;
157 : entry = 420;
158 : entry = 421;
159 : entry = 423;
160 : entry = 425;
161 : entry = 427;
162 : entry = 428;
163 : entry = 430;
164 : entry = 432;
165 : entry = 433;
166 : entry = 435;
167 : entry = 437;
168 : entry = 438;
169 : entry = 440;
170 : entry = 441;
171 : entry = 443;
172 : entry = 445;
173 : entry = 446;
174 : entry = 448;
175 : entry = 449;
176 : entry = 451;
177 : entry = 452;
178 : entry = 454;
179 : entry = 455;
180 : entry = 456;
181 : entry = 458;
182 : entry = 459;
183 : entry = 461;
184 : entry = 462;
185 : entry = 463;
186 : entry = 465;
187 : entry = 466;
188 : entry = 467;
189 : entry = 468;
190 : entry = 470;
191 : entry = 471;
192 : entry = 472;
193 : entry = 473;
194 : entry = 474;
195 : entry = 476;
196 : entry = 477;
197 : entry = 478;
198 : entry = 479;
199 : entry = 480;
200 : entry = 481;
201 : entry = 482;
202 : entry = 483;
203 : entry = 484;
204 : entry = 485;
205 : entry = 486;
206 : entry = 487;
207 : entry = 488;
208 : entry = 489;
209 : entry = 490;
210 : entry = 491;
211 : entry = 492;
212 : entry = 492;
213 : entry = 493;
214 : entry = 494;
215 : entry = 495;
216 : entry = 496;
217 : entry = 496;
218 : entry = 497;
219 : entry = 498;
220 : entry = 499;
221 : entry = 499;
222 : entry = 500;
223 : entry = 501;
224 : entry = 501;
225 : entry = 502;
226 : entry = 502;
227 : entry = 503;
228 : entry = 503;
229 : entry = 504;
230 : entry = 505;
231 : entry = 505;
232 : entry = 505;
233 : entry = 506;
234 : entry = 506;
235 : entry = 507;
236 : entry = 507;
237 : entry = 508;
238 : entry = 508;
239 : entry = 508;
240 : entry = 509;
241 : entry = 509;
242 : entry = 509;
243 : entry = 509;
244 : entry = 510;
245 : entry = 510;
246 : entry = 510;
247 : entry = 510;
248 : entry = 510;
249 : entry = 511;
250 : entry = 511;
251 : entry = 511;
252 : entry = 511;
253 : entry = 511;
254 : entry = 511;
255 : entry = 511;
default: entry = 9'bxxxxxxxxx; // Sentry to ensure we don't infer a latch.
endcase
end
endmodule
/// iverilog -Wall -Wno-timescale -y. sine_lookup_1024_tb.v && vvp a.out > sine.tab
/// Then analyze the dump using Jupyter or whatever.
module sine_lookup_1024_tb;
reg [9:0] x;
wire signed [9:0] out;
sine_lookup_1024 sine (x, out);
integer i;
initial begin
$dumpfile("sine_lookup_1024.vcd");
$dumpvars();
for (i = 0; i < 1024; i++) begin
# 1 x = i[9:0];
# 1
$display(x, " ", out);
end
# 1 $finish();
end
endmodule
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment