Jump to content
    

Цифровая "синусойда"

закоментируйте один выход (reg)

дело не в том, что я не догадался закоментировать один из выходов, а в том, что он не влазит в мой ПЛИС из за нехватки макроячеек, я об этом написал.Поэтому и спросил про такой же ,но попроще.

Share this post


Link to post
Share on other sites

`timescale 1ns / 1ps

module sin(
input			clock, // input clock
output [15:0]	sin, // sin output: signed, 16-bit
output [15:0]	cos // cos output: signed, 16-bit
);

parameter width = 16;
parameter InitCos = 32640;

reg [width-1:0] sin1 = 0;
reg [width-1:0] cos1 = InitCos;



always @(posedge clock) begin
	sin1 <= sin;
	cos1 <= cos;
end

//the computator
assign sin = sin1 + {{7{cos1[15]}}, cos1[15:7]};
assign cos = cos1 - {{7{sin[15]}}, sin[15:7]};

endmodule

 

отмоделируйте. даёт отменный синус без всяких таблиц. :)

объясните, почему это работает?

 

ртл схематик

 

+ попытался переписать на VHDL

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity top is
port(    CLK:        in std_logic;
    SIN_OUT:        out std_logic_vector(15 downto 0);
    COS_OUT:        out std_logic_vector(15 downto 0)
);
end top;

architecture Behavioral of top is

constant width: integer := 16;
constant InitCos: integer := 32640;
signal sin : std_logic_vector(width - 1 downto 0):= CONV_STD_LOGIC_VECTOR(0,width);
signal cos : std_logic_vector(width - 1 downto 0):= CONV_STD_LOGIC_VECTOR(0,width);
signal sin1 : std_logic_vector(width - 1 downto 0):= CONV_STD_LOGIC_VECTOR(0,width);
signal cos1 : std_logic_vector(width - 1 downto 0):= CONV_STD_LOGIC_VECTOR(InitCos,width);

begin

process(CLK)
    begin
SIN_OUT <= sin;
COS_OUT <= cos;

if rising_edge(CLK) then
sin1 <= sin;
cos1 <= cos;
end if;

sin <= sin1 + (cos1(15) & cos1(15) & cos1(15) & cos1(15) & cos1(15) & cos1(15) & cos1(15) & cos1(15 downto 7));
cos <= cos1 - (sin(15)  & sin(15)  & sin(15)  & sin(15)  & sin(15)  & sin(15)  & sin(15)  & sin(15 downto 7));

end process;
end Behavioral;

результат отличается - синусоида ломанная, как-будто при генерации ломается знак. Я правильно переписал на VHDL строки assign sin = sin1 + {{7{cos1[15]}}, cos1[15:7]}; ?

post-53001-1303130449_thumb.png

Edited by PDA

Share this post


Link to post
Share on other sites

В принципе все коеффициенты для синусоиды можно "засунуть" в блочную память, а потом считывать (это обычный счетчик :)), а не хранить на распределенной логике. Возможно тогда произойдет уменьшение ресурсов. Сколько точек на период Вам необходимо? Или по другому какая точность/дискретизация получения синусоиды нужна? Какая ПЛИС у Вас?

 

кстати здесь, рассмотрен способ построения генератора на основе мат. формулы - там это подробно расписано. Как перейти с 8 бит на 16 бит или на 32 бита - это мне не понятно?

 

to PDA возможно это то что Вы хотели получить ....

Share this post


Link to post
Share on other sites

В принципе все коеффициенты для синусоиды можно "засунуть" в блочную память, а потом считывать (это обычный счетчик :)), а не хранить на распределенной логике. Возможно тогда произойдет уменьшение ресурсов. Сколько точек на период Вам необходимо? Или по другому какая точность/дискретизация получения синусоиды нужна? Какая ПЛИС у Вас?

у меня ПЛИС XC9572 44 пина. Если схема , которую PDA привёл правильная, то она в эту плиску не влазит. Тогда я попробовал заменить все 16 битные элементы на 8 битные, после некоторых манипуляций на выходе получился треугольник , но не с ровными гранями , а по фронту немного с изгибом в одну сторону, а по спаду в другую. Синусом там и не пахнет. Косинусную часть тоже пришлось отрезать.

а вот такой код заработал сходу

`timescale 1 ns / 1 ps

//{module {sin_generator}}      
    
module sin_generator ( clk,out );

output [15:0] out;
reg [15:0] out;
input clk;
wire clk;

reg [15:0] sin_90     = 32768;
reg [15:0] sin_101_25 = 39160;
reg [15:0] sin_112_5  = 45307;
reg [15:0] sin_123_75 = 50972;
reg [15:0] sin_135    = 55938;
reg [15:0] sin_146_25 = 60013;
reg [15:0] sin_157_5  = 63041;
reg [15:0] sin_168_75 = 65005;
reg [15:0] sin_180    = 65535;//16'b1111_1111_1111_1111;
reg [15:0] sin_191_25 = 64905;
reg [15:0] sin_202_5  = 63041;
reg [15:0] sin_213_75 = 60013;
reg [15:0] sin_225    = 55938;
reg [15:0] sin_236_25 = 50972;
reg [15:0] sin_247_5  = 45307;
reg [15:0] sin_258_75 = 39160;
reg [15:0] sin_270    = 32768;
reg [15:0] sin_281_25 = 26375;
reg [15:0] sin_292_5  = 20228;
reg [15:0] sin_303_75 = 14563;
reg [15:0] sin_315    = 9597;
reg [15:0] sin_326_25 = 5522;
reg [15:0] sin_337_5  = 2494;
reg [15:0] sin_348_75 = 982;
reg [15:0] sin_0      = 2;     //16'b0000_0000_0000_0010;
reg [15:0] sin_11_25  = 942;
reg [15:0] sin_22_5   = 2494;
reg [15:0] sin_33_75  = 5522;
reg [15:0] sin_45     = 9597;
reg [15:0] sin_56_25  = 14563;  
reg [15:0] sin_67_5   = 20228;
reg [15:0] sin_78_75  = 26375;


// Binary counter, 5-bits wide
reg [4:0] counter = 5'b0000;
always @(posedge clk) counter <= counter + 1;

always @(posedge clk) case(counter)

5'b00000:out<=sin_0;
5'b00001:out<=sin_11_25;
5'b00010:out<=sin_22_5;                
5'b00011:out<=sin_33_75;
5'b00100:out<=sin_45;
5'b00101:out<=sin_56_25;       
5'b00110:out<=sin_67_5;
5'b00111:out<=sin_78_75;
5'b01000:out<=sin_90;
5'b01001:out<=sin_101_25;
5'b01010:out<=sin_112_5;
5'b01011:out<=sin_123_75;
5'b01100:out<=sin_135;
5'b01101:out<=sin_146_25;
5'b01110:out<=sin_157_5;
5'b01111:out<=sin_168_75;
5'b10000:out<=sin_180;
5'b10001:out<=sin_191_25;
5'b10010:out<=sin_202_5;
5'b10011:out<=sin_213_75;
5'b10100:out<=sin_225;
5'b10101:out<=sin_236_25;
5'b10110:out<=sin_247_5;
5'b10111:out<=sin_258_75;
5'b11000:out<=sin_270;
5'b11001:out<=sin_281_25;
5'b11010:out<=sin_292_5;
5'b11011:out<=sin_303_75;
5'b11100:out<=sin_315;
5'b11101:out<=sin_326_25;
5'b11110:out<=sin_337_5;
5'b11111:out<=sin_348_75;


default:; 

endcase      

endmodule

немного значения подшаманили , макушки и ямки сравняли и получился нормальный синус, фотки я приводил .

Share this post


Link to post
Share on other sites

кстати здесь, рассмотрен способ построения генератора на основе мат. формулы - там это подробно расписано.

Вообще-то по cсылке код не совсем корректный, тк согласно алгоритму sin и cos сдвинуты отностительно друг-друга на полтакта --> доп. ошибка в модуле sqrt(sin^2+cos^2)). Пример более честного алгоритма:

module gen(
  input clk,
  output reg [10:0] s = 8, 
  output reg [10:0] c = 970
);
wire [10:0] cc = c - {s[10],s[10],s[10],s[10],s[10:4]};
wire [10:0] ss = s + {c[10],c[10],c[10],c[10],c[10:4]};    
always @(posedge clk) begin
  s <= s + {cc[10],cc[10],cc[10],cc[10:3]};
  c <= c - {ss[10],ss[10],ss[10],ss[10:3]};
end
endmodule

 

 

Большую точность можно получить, если сдвиг заменить целочисленным делением (еще лучше, если с округлением), см пример:

1 >> 10 = 0

-1 >> 10 = -1

-1 div 1024 = 0

 

Share this post


Link to post
Share on other sites

на основании чего берутся значения инициализации синуса и косинуса?

output reg [10:0] s = 8,

output reg [10:0] c = 970

output [15:0] sin, // sin output: signed, 16-bit

output [15:0] cos // cos output: signed, 16-bit

);

 

parameter width = 16;

parameter InitCos = 32640;

 

reg [width-1:0] sin1 = 0;

reg [width-1:0] cos1 = InitCos;

 

 

Share this post


Link to post
Share on other sites

Свои взял методом тыка - программой перебора (пишется ~5 минут, перебирает ~5 секунд).

Share this post


Link to post
Share on other sites

Вообще-то по cсылке код не совсем корректный, тк согласно алгоритму sin и cos сдвинуты отностительно друг-друга на полтакта --> доп. ошибка в модуле sqrt(sin^2+cos^2)). Пример более честного алгоритма:

module gen(
  input clk,
  output reg [10:0] s = 8, 
  output reg [10:0] c = 970
);
wire [10:0] cc = c - {s[10],s[10],s[10],s[10],s[10:4]};
wire [10:0] ss = s + {c[10],c[10],c[10],c[10],c[10:4]};    
always @(posedge clk) begin
  s <= s + {cc[10],cc[10],cc[10],cc[10:3]};
  c <= c - {ss[10],ss[10],ss[10],ss[10:3]};
end
endmodule

 

 

Большую точность можно получить, если сдвиг заменить целочисленным делением (еще лучше, если с округлением), см пример:

1 >> 10 = 0

-1 >> 10 = -1

-1 div 1024 = 0

 

а можно ссылку на мат. часть почитать хочу. Просто интересно как Вы перешли с 16 бит на 11 бит? Откуда эти формулы?

Просто не понимаю :(

 

 

Свои взял методом тыка - программой перебора (пишется ~5 минут, перебирает ~5 секунд).

а можно эту программу? ;) Или если не сложно поясните ее работу, плиз.

Share this post


Link to post
Share on other sites

Откуда эти формулы?

Из головы. Интерполяция по первой и второй производной.

 

а можно эту программу?

Вообще-то можно, но непричесанные не люблю выкладывать. Так что позже, если получится. Там просто цикл по начальным значениям, и оценка разброса модуля вектора.

 

Share this post


Link to post
Share on other sites

#include <math.h>
main(){
   
    long s0, s, ss, c0, c, cc, i, d, m, max, min;    

    long s1 = -100;
    long s2 =  100;
    long c1 =   500;
    long c2 = 1000;
    long imax=10000;    
    
//    s0 = 35;
//    c0 = 504;    
    for(c0=c1; c0<=c2; c0++)     
    for(s0=s1; s0<=s2; s0++)
    {
        s=s0; c=c0; max=0; min=1000000000;  
        for(i=0; i<=imax; i++) 
        {            
//            m = (long)sqrt((double)(s*s + c*c));       
//            printf("i=%5d  s=%4d  c=%4d  m=%d\n",i,s,c,m);        
                
            d=s*s+c*c;
            if(d<min) min=d;
            if(d>max) max=d;
      
            cc = c - (s >> 4);
            ss = s + (c >> 4);
            s = s + (cc >> 3);     
            c = c - (ss >> 3); 
        }
        
        d = (long)sqrt((double)max) - (long)sqrt((double)min);
        if(d<=20)
        { 
            for(i=0; i<d; i++) printf("*");
            printf("%d",d);
            for(i=d; i<40; i++) printf(".");
            printf("  %d, %d\n",s0,c0);
        }    
    }        
}

 

Вывод в файл назначать, конечно.

 

Кстати, можно заменить арифметический сдвиг на целочисленное деление, и посмотреть, насколько увеличится достижимая точность ;) :

 cc = c - (s / 16);
ss = s + (c / 16);
s = s + (cc / 8);     
c = c - (ss / 8);

 

Share this post


Link to post
Share on other sites

Не вижу в программе Leka синуса.

Расскажите, как эта программа работает.

А, что-то типа радиуса окружности вычисляете?

 

P.S. имел в виду "в последней программе, проверочной"

Share this post


Link to post
Share on other sites

s - синус

c - косинус

Как работает - интерполяция по первой и второй производной:

y(dx) = y(0) + y'(0) * dx + 1/2 * y"(0) * dx^2,

расписываете и получаете, в данном случае шаг dx = 1/(1 << n).

 

Конкретно синус-косинус для выбранных начальных условий, например:

main(){
    long s, ss, c, cc, i, imax;    

    s = 35;
    c = 504;    
    imax = 100;

    for(i=0; i<=imax; i++) 
        {            
            printf("i=%4d  sin=%4d  cos=%4d \n",i,s,c);        
      
            cc = c - (s >> 4);
            ss = s + (c >> 4);
            s = s + (cc >> 3);     
            c = c - (ss >> 3); 
        }
}

 

А предыдущая - для поиска начальных условий.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...