Сгенерируйте синусоидальную волну

Это 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

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *