Это типичный случай, когда генератор фазы фазы используется для генерации адреса ПЗУ для вывода нового образца.
Примерно так: здесь накопитель фазы имеет ширину 14 бит, и мыиспользуют 6 старших разрядов, чтобы получить синусоидальную волну из 64 выборок.Выходная частота такой синусоидальной волны составляет CLK * freq / 16384
. На самом деле, ПЗУ имеет всего 16 ячеек, так как ему требуется только одна четверть синусоидальной волны.Биты с 3 по 0 из вычисленного адреса из фазового аккумулятора используются для адресации ПЗУ, а биты с 5 по 4 используются для определения того, в каком квадранте мы находимся, поэтому реальный адрес может потребоваться инвертировать и / или фактический выходможет потребоваться отрицать.
module FunctionGenerator (
input wire clk,
input wire [7:0] freq, // frequency of output signal is: clk * freq / 16384
output reg signed [9:0] out
);
reg signed [9:0] quartersin[0:15];
// Generate initial values with this little C program:
// #include <stdio.h>
// #include <math.h>
//
// #define PI 3.141592654
//
// int main()
// {
// int i;
//
// for (i=0;i<16;i++)
// {
// printf (" quartersin[%2d] = %d;\n", i, (int)(511*sin(i*2*PI/64.0)));
// }
//
// return 0;
// }
initial begin
quartersin[ 0] = 0;
quartersin[ 1] = 50;
quartersin[ 2] = 99;
quartersin[ 3] = 148;
quartersin[ 4] = 195;
quartersin[ 5] = 240;
quartersin[ 6] = 283;
quartersin[ 7] = 324;
quartersin[ 8] = 361;
quartersin[ 9] = 395;
quartersin[10] = 424;
quartersin[11] = 450;
quartersin[12] = 472;
quartersin[13] = 488;
quartersin[14] = 501;
quartersin[15] = 508;
end
reg [13:0] phaseacum = 14'h0000;
reg [3:0] indx;
always @(posedge clk) begin
phaseacum <= phaseacum + {6'b000000, freq};
if (phaseacum[13] == 1'b0) // if in quadrants 0 or 1, out as is
out <= quartersin[indx];
else
out <= -quartersin[indx]; // if in quadrants 2 or 3, negate out
end
always @* begin
if (phaseacum[12] == 1'b0) // which quadrant am I ?
indx = phaseacum[11:8]; // if in quadrant 0 or 2
else
indx = ~phaseacum[11:8]; // else, in quadrant 1 or 3
endcase
end
endmodule
Можно использовать минимальный тестовый стенд, чтобы позволить этому генератору функций работать около тысячи тактовых циклов, а затем перенести вывод в GTKWave и отобразить его как аналоговый выход.:
`timescale 1ns / 1ns
module tb;
reg clk;
reg [7:0] freq;
wire [9:0] out;
FunctionGenerator uut (
.clk(clk),
.freq(freq),
.out(out)
);
initial begin
$dumpfile("dump.vcd");
$dumpvars(1,uut);
clk = 1'b0;
freq = 8'd16; // aprox. 1 kHz sine wave for a 1 MHz clk
repeat (2000) begin
@(posedge clk);
end
$finish;
end
always begin
clk = #500 ~clk; // a 1 MHz clock
end
endmodule
Это результат, смоделированный с iverilog / GTKWave: