Это IP-ядро просто генерирует синусоидальную волну в соответствии с файлом .mem. Требуется указать глубину ROM, равную количеству синусоидальных точек, файла инициализации и размеру данных, содержащихся в файле. Сдвиг фазы и частота используются в качестве сигналов управления.
В нем также находится модуль ПЗУ, а также стенды для тестирования IP-ядра и ПЗУ. Скрипты tcl позволяют запускать Modelsim с консоли из-за скриптов bash.
https://www.daycounter.com/Calculators/Sine-Generator-Calculator.phtml — точки синусоидального LUT-калькулятора.
dds.v
`timescale 1ns / 1ps
module dds #
(
parameter integer DATA_WIDTH = 16 ,
parameter integer ROM_DEPTH = 1024 ,
parameter INIT_FILE = "" ,
parameter integer FCW_WIDTH = $clog2(ROM_DEPTH) + 14,
parameter integer PHASE_OFFSET_WIDTH = 10
)
(
input wire clk_i ,
input wire s_rst_n_i ,
input wire en_i ,
input wire [FCW_WIDTH - 1 : 0] fcw_i ,
input wire [PHASE_OFFSET_WIDTH - 1 : 0] phase_offset_i , //for instance: ROM_DEPTH / 2 == (2 * pi) / 2 == pi
input wire phase_offset_wr_i,
output wire [DATA_WIDTH - 1 : 0] sinus_o
);
localparam integer LOOKUP_TABLE_INDEX_WIDTH = $clog2(ROM_DEPTH);
localparam integer ACCUMULATOR_WIDTH = FCW_WIDTH;
wire [LOOKUP_TABLE_INDEX_WIDTH - 1 : 0] lookup_table_index;
rom_distributed #
(
.DATA_WIDTH (DATA_WIDTH ),
.ROM_DEPTH (ROM_DEPTH ),
.INIT_FILE (INIT_FILE ),
.ADDRESS_WIDTH (LOOKUP_TABLE_INDEX_WIDTH)
)
rom_distributed_inst
(
.address_i (lookup_table_index),
.data_o (sinus_o )
);
dds_generator #
(
.LOOKUP_TABLE_INDEX_WIDTH (LOOKUP_TABLE_INDEX_WIDTH),
.ACCUMULATOR_WIDTH (ACCUMULATOR_WIDTH ),
.PHASE_OFFSET_WIDTH (PHASE_OFFSET_WIDTH ),
.FCW_WIDTH (FCW_WIDTH )
)
dds_generator_inst
(
.clk_i (clk_i ),
.s_rst_n_i (s_rst_n_i ),
.en_i (en_i ),
.phase_offset_wr_i (phase_offset_wr_i ),
.phase_offset_i (phase_offset_i ),
.fcw_i (fcw_i ),
.lookup_table_index_o (lookup_table_index)
);
endmodule
dds_tb.v
`timescale 1ns / 1ps
//The paths need to be changed
`define MEM_FILE_0 "../dds_0.mem"
`define MEM_FILE_2 "../dds_2.mem"
module dds_tb;
localparam integer CLOCK_PERIOD = 100 ;
localparam integer ITERATION_NUM = 4 * 1000000 ;
localparam integer DATA_WIDTH = 16 ;
localparam integer LUT_INDEX_REST = 14 ;
localparam integer ROM_DEPTH_0 = 1024 ;
localparam integer PHASE_OFFSET_WIDTH_0 = 10 ;
localparam integer FCW_WIDTH_0 = PHASE_OFFSET_WIDTH_0 + LUT_INDEX_REST;
localparam [FCW_WIDTH_0 - 1 : 0] PI_0 = 10'h200 ;
localparam [FCW_WIDTH_0 - 1 : 0] SINE_FREQ_0 = {FCW_WIDTH_0{1'h0}} + 9'h100 ;
localparam [PHASE_OFFSET_WIDTH_0 - 1 : 0] PHASE_OFFSET_0 = {PHASE_OFFSET_WIDTH_0{1'h0}} ;
localparam [PHASE_OFFSET_WIDTH_0 - 1 : 0] PHASE_OFFSET_1 = {PHASE_OFFSET_WIDTH_0{1'h0}} + PI_0 ;
localparam integer ROM_DEPTH_2 = 512 ;
localparam integer PHASE_OFFSET_WIDTH_2 = 9 ;
localparam integer FCW_WIDTH_2 = PHASE_OFFSET_WIDTH_2 + LUT_INDEX_REST;
localparam [FCW_WIDTH_0 - 1 : 0] PI_2 = 9'h100 ;
localparam [FCW_WIDTH_0 - 1 : 0] SINE_FREQ_2 = {FCW_WIDTH_0{1'h0}} + 13'h1000 ;
localparam [PHASE_OFFSET_WIDTH_0 - 1 : 0] PHASE_OFFSET_2 = {PHASE_OFFSET_WIDTH_2{1'h0}} ;
localparam [PHASE_OFFSET_WIDTH_0 - 1 : 0] PHASE_OFFSET_3 = {PHASE_OFFSET_WIDTH_2{1'h0}} + PI_2 ;
wire [DATA_WIDTH - 1 : 0] sinus_0;
wire [DATA_WIDTH - 1 : 0] sinus_1;
wire [DATA_WIDTH - 1 : 0] sinus_2;
wire [DATA_WIDTH - 1 : 0] sinus_3;
reg clk = 1'h0;
reg s_rst_n = 1'h0;
reg en = 1'h0;
reg phase_offset_wr_0 = 1'h0;
reg phase_offset_wr_1 = 1'h0;
reg phase_offset_wr_2 = 1'h0;
reg phase_offset_wr_3 = 1'h0;
reg [FCW_WIDTH_0 - 1 : 0] fcw_0 = SINE_FREQ_0;
reg [FCW_WIDTH_0 - 1 : 0] fcw_1 = SINE_FREQ_0;
reg [FCW_WIDTH_2 - 1 : 0] fcw_2 = SINE_FREQ_2;
reg [FCW_WIDTH_2 - 1 : 0] fcw_3 = SINE_FREQ_2;
reg [PHASE_OFFSET_WIDTH_0 - 1 : 0] phase_offset_0 = PHASE_OFFSET_0;
reg [PHASE_OFFSET_WIDTH_0 - 1 : 0] phase_offset_1 = PHASE_OFFSET_1;
reg [PHASE_OFFSET_WIDTH_2 - 1 : 0] phase_offset_2 = PHASE_OFFSET_2;
reg [PHASE_OFFSET_WIDTH_2 - 1 : 0] phase_offset_3 = PHASE_OFFSET_3;
dds #
(
.DATA_WIDTH (DATA_WIDTH ),
.ROM_DEPTH (ROM_DEPTH_0 ),
.INIT_FILE (`MEM_FILE_0 ),
.FCW_WIDTH (FCW_WIDTH_0 ),
.PHASE_OFFSET_WIDTH (PHASE_OFFSET_WIDTH_0)
)
sinus_generator_dut_0
(
.clk_i (clk ),
.s_rst_n_i (s_rst_n ),
.en_i (en ),
.fcw_i (fcw_0 ),
.phase_offset_i (phase_offset_0 ),
.phase_offset_wr_i (phase_offset_wr_0),
.sinus_o (sinus_0 )
);
dds #
(
.DATA_WIDTH (DATA_WIDTH ),
.ROM_DEPTH (ROM_DEPTH_0 ),
.INIT_FILE (`MEM_FILE_0 ),
.FCW_WIDTH (FCW_WIDTH_0 ),
.PHASE_OFFSET_WIDTH (PHASE_OFFSET_WIDTH_0)
)
sinus_generator_dut_1
(
.clk_i (clk ),
.s_rst_n_i (s_rst_n ),
.en_i (en ),
.fcw_i (fcw_1 ),
.phase_offset_i (phase_offset_1 ),
.phase_offset_wr_i (phase_offset_wr_1),
.sinus_o (sinus_1 )
);
dds #
(
.DATA_WIDTH (DATA_WIDTH ),
.ROM_DEPTH (ROM_DEPTH_2 ),
.INIT_FILE (`MEM_FILE_2 ),
.FCW_WIDTH (FCW_WIDTH_2 ),
.PHASE_OFFSET_WIDTH (PHASE_OFFSET_WIDTH_2)
)
sinus_generator_dut_2
(
.clk_i (clk ),
.s_rst_n_i (s_rst_n ),
.en_i (en ),
.fcw_i (fcw_2 ),
.phase_offset_i (phase_offset_2 ),
.phase_offset_wr_i (phase_offset_wr_2),
.sinus_o (sinus_2 )
);
dds #
(
.DATA_WIDTH (DATA_WIDTH ),
.ROM_DEPTH (ROM_DEPTH_2 ),
.INIT_FILE (`MEM_FILE_2 ),
.FCW_WIDTH (FCW_WIDTH_2 ),
.PHASE_OFFSET_WIDTH (PHASE_OFFSET_WIDTH_2)
)
sinus_generator_dut_3
(
.clk_i (clk ),
.s_rst_n_i (s_rst_n ),
.en_i (en ),
.fcw_i (fcw_3 ),
.phase_offset_i (phase_offset_3 ),
.phase_offset_wr_i (phase_offset_wr_3),
.sinus_o (sinus_3 )
);
initial begin
forever begin
#(CLOCK_PERIOD / 2) clk = !clk;
end
end
initial begin
@(posedge clk);
s_rst_n <= 1'h1;
en <= 1'h1;
@(posedge clk);
repeat(ITERATION_NUM) begin
@(posedge clk);
end
phase_offset_wr_0 <= 1'h1;
phase_offset_0 <= PHASE_OFFSET_1;
phase_offset_wr_2 <= 1'h1;
phase_offset_2 <= PHASE_OFFSET_3;
@(posedge clk);
phase_offset_wr_0 <= 1'h0;
phase_offset_wr_2 <= 1'h0;
repeat(ITERATION_NUM) begin
@(posedge clk);
end
$stop();
end
endmodule
rom_distributed.v
`timescale 1ns / 1ps
module rom_distributed #
(
parameter integer DATA_WIDTH = 8 ,
parameter integer ROM_DEPTH = 256 ,
parameter INIT_FILE = "",
parameter integer ADDRESS_WIDTH = $clog2(ROM_DEPTH)
)
(
input wire [ADDRESS_WIDTH - 1 : 0] address_i,
output wire [DATA_WIDTH - 1 : 0] data_o
);
reg [DATA_WIDTH - 1 : 0] rom_memory [ROM_DEPTH - 1 : 0];
initial begin
if ("" != INIT_FILE) begin
$readmemh(INIT_FILE, rom_memory);
end
else begin
$error("A rom init file is not specified.");
end
end
assign data_o = rom_memory[address_i];
endmodule
rom_distributed_tb.v
`timescale 1ns / 1ps
//The path needs to be changed
`define INIT_FILE "../rom_async.mem"
module rom_distributed_tb;
localparam integer DATA_WIDTH = 16 ;
localparam integer ROM_DEPTH = 1024 ;
localparam integer ADDRESS_WIDTH = $clog2(ROM_DEPTH);
wire [DATA_WIDTH - 1 : 0] dut_value;
reg [DATA_WIDTH - 1 : 0] file_value;
reg [ADDRESS_WIDTH - 1 : 0] address ;
integer file = 0;
integer errors = 0;
rom_distributed #
(
.DATA_WIDTH (DATA_WIDTH ),
.ROM_DEPTH (ROM_DEPTH ),
.INIT_FILE (`INIT_FILE ),
.ADDRESS_WIDTH (ADDRESS_WIDTH)
)
rom_distributed_dut
(
.address_i (address ),
.data_o (dut_value)
);
initial begin
file_value = 0;
address = 0;
file = $fopen(`INIT_FILE, "r");
if (0 != file) begin
repeat(ROM_DEPTH) begin
$fscanf(file, "%h", file_value);
#100 if (file_value !== dut_value) begin
errors = errors + 1;
end
address = address + 1;
end
if (0 == errors) begin
$display("The test passed.n");
end
else begin
$display("The test failed with %d errors.n", errors);
end
end
else begin
$display("A descripter of a '%s' file is 0.n", `INIT_FILE);
end
$stop();
end
endmodule
dds_generator.v
`timescale 1ns / 1ps
module dds_generator #
(
parameter integer LOOKUP_TABLE_INDEX_WIDTH = 10,
parameter integer ACCUMULATOR_WIDTH = 24,
parameter integer PHASE_OFFSET_WIDTH = 10,
parameter integer FCW_WIDTH = 8
)
(
input wire clk_i ,
input wire s_rst_n_i ,
input wire en_i ,
input wire phase_offset_wr_i ,
input wire [PHASE_OFFSET_WIDTH - 1 : 0] phase_offset_i ,
input wire [FCW_WIDTH - 1 : 0] fcw_i ,
output wire [LOOKUP_TABLE_INDEX_WIDTH - 1 : 0] lookup_table_index_o
);
localparam integer ACCUMULATOR_WIDTH_LSB = ACCUMULATOR_WIDTH - LOOKUP_TABLE_INDEX_WIDTH;
reg [ACCUMULATOR_WIDTH - 1:0] accumulator;
always @ (posedge clk_i) begin
if(1'h0 == s_rst_n_i ) begin
accumulator[ACCUMULATOR_WIDTH_LSB - 1 : 0] <= 0;
accumulator[ACCUMULATOR_WIDTH : ACCUMULATOR_WIDTH_LSB] <= phase_offset_i;
end
else if (1'h1 == phase_offset_wr_i) begin
accumulator[ACCUMULATOR_WIDTH_LSB - 1 : 0] <= 0;
accumulator[ACCUMULATOR_WIDTH : ACCUMULATOR_WIDTH_LSB] <= phase_offset_i;
end else if (1'h1 == en_i) begin
if (({10{1'h1}} - 2) == accumulator[ACCUMULATOR_WIDTH : ACCUMULATOR_WIDTH_LSB]) begin
accumulator[ACCUMULATOR_WIDTH : ACCUMULATOR_WIDTH_LSB] <= 0;
end
else begin
accumulator <= accumulator + fcw_i;
end
end
end
assign lookup_table_index_o = accumulator[ACCUMULATOR_WIDTH : ACCUMULATOR_WIDTH_LSB];
endmodule
dds_tb
#!/bin/bash
#change the vivado_path to yours if required.
modelsim_path=/opt/modelsim/modelsim_ase/bin
tb_tcl_file_name=dds_tb.tcl
$modelsim_path/vsim -do $tb_tcl_file_name
dds_tb.tcl
transcript on
vlib work
vmap work work
# variables---------
set dut ../../hdl/dds.v
set var_1 ../../hdl/dds_generator.v
set var_2 ../../hdl/rom_distributed.v
set tb dds_tb.v
# ------------------
vlog $dut $tb $var_1 $var_2
vsim -t 100ns -voptargs="+acc" dds_tb
# waves ---------
add wave /dds_tb/clk
add wave -radix hex -format analog-step -max 65535 -min -65535 -height 100 /dds_tb/sinus_0
add wave -radix hex -format analog-step -max 65535 -min -65535 -height 100 /dds_tb/sinus_1
add wave -radix hex -format analog-step -max 32767 -min -32767 -height 100 /dds_tb/sinus_2
add wave -radix hex -format analog-step -max 32767 -min -32767 -height 100 /dds_tb/sinus_3
add wave -radix hex /dds_tb/fcw_0
add wave -radix hex /dds_tb/phase_offset_0
add wave -radix hex /dds_tb/phase_offset_wr_0
add wave -radix hex /dds_tb/fcw_1
add wave -radix hex /dds_tb/phase_offset_1
add wave -radix hex /dds_tb/phase_offset_wr_1
add wave -radix hex /dds_tb/fcw_2
add wave -radix hex /dds_tb/phase_offset_2
add wave -radix hex /dds_tb/phase_offset_wr_2
add wave -radix hex /dds_tb/fcw_3
add wave -radix hex /dds_tb/phase_offset_3
add wave -radix hex /dds_tb/phase_offset_wr_3
# ------------------
configure wave -timelineunits us
run -all
wave zoom full
rom_distributed_tb
#!/bin/bash
#change the vivado_path to yours if required.
modelsim_path=/opt/modelsim/modelsim_ase/bin
tb_tcl_file_name=rom_distributed_tb.tcl
$modelsim_path/vsim modelsim.ini -do $tb_tcl_file_name
rom_distributed_tb.tcl
transcript on
vlib work
vmap work work
# variables---------
set dut ../../hdl/rom_distributed.v
set tb rom_distributed_tb.v
# ------------------
vlog $dut $tb
vsim -t 100ns -voptargs="+acc" rom_distributed_tb
# waves ---------
add wave -radix hex /rom_distributed_tb/dut_value
add wave -radix hex /rom_distributed_tb/file_value
add wave -radix hex /rom_distributed_tb/errors
# ------------------
configure wave -timelineunits us
run -all
wave zoom full
Чисто
#!/bin/bash
rm transcript modelsim.ini *vsim.wlf -r work