Andy111 0 March 3, 2015 Posted March 3, 2015 · Report post Здравствуйте. В общем стоит задача написать модуль на Verilog, который бы рассчитывал arctg(y/x) с помощью CORDIC алгоритма. Я в veriloge не очень силен, поэтому покопался в интернете и нашел вот такую реализацию: `define activehigh 1 `define procline posedge clk `define proclineg posedge clk `define proclineSlow posedge clkSlow `define proclineFast posedge clkFast module CordicRotator (clk, rst, en, xi, yi, zi, xo, yo, zo); // { parameter smi = 1; parameter bitWidth = 16; parameter angBitWidth = 16; parameter angFracWidth = 16; parameter rotMode = 1; input clk; input rst; input en; input signed [bitWidth-1:0] xi; input signed [bitWidth-1:0] yi; input signed [angBitWidth-1:0] zi; output reg signed [bitWidth-1:0] xo; output reg signed [bitWidth-1:0] yo; output reg signed [angBitWidth-1:0] zo; function signed [angFracWidth-1:0] appr; input signed [53:0] atf; begin if (angFracWidth>54) begin appr = 0; appr[angFracWidth-1:angFracWidth-54] = atf; end else appr = atf[53:54-angFracWidth]; end endfunction // appr wire signed [bitWidth-1:0] sx,sy; assign sx = xi >>> smi; assign sy = yi >>> smi; reg signed [53:0] atf; reg signed [angFracWidth-1:0] at; always @(`procline) begin if (rst==`activehigh) begin xo <= 0; yo <= 0; zo <= 0; end else if (en) begin case (smi) 0: atf = 54'b010000000000000000000000000000000000000000000000000000; //0.25 1: atf = 54'b001001011100100000001010001110110011101111100110000100; 2: atf = 54'b000100111111011001110000101101101011110111000111001111; 3: atf = 54'b000010100010001000100011101010000011101110111011001101; 4: atf = 54'b000001010001011000011010100001100001110010110001001101; 5: atf = 54'b000000101000101110101111110000101011001000001000110001; 6: atf = 54'b000000010100010111101100001111001011100001010000010011; 7: atf = 54'b000000001010001011111000101010100010001110101000100001; 8: atf = 54'b000000000101000101111100101001101000110110100001100001; 9: atf = 54'b000000000010100010111110010111010111011001100001010101; 10: atf = 54'b000000000001010001011111001100000000000100100011011101; 11: atf = 54'b000000000000101000101111100110000010100101010000000110; 12: atf = 54'b000000000000010100010111110011000001100110111111110110; 13: atf = 54'b000000000000001010001011111001100000110110000010111001; 14: atf = 54'b000000000000000101000101111100110000011011010101110100; 15: atf = 54'b000000000000000010100010111110011000001101101101011101; 16: atf = 54'b000000000000000001010001011111001100000110110111000010; 17: atf = 54'b000000000000000000101000101111100110000011011011100100; 18: atf = 54'b000000000000000000010100010111110011000001101101110010; 19: atf = 54'b000000000000000000001010001011111001100000110110111001; 20: atf = 54'b000000000000000000000101000101111100110000011011011100; 21: atf = 54'b000000000000000000000010100010111110011000001101101110; 22: atf = 54'b000000000000000000000001010001011111001100000110110111; 23: atf = 54'b000000000000000000000000101000101111100110000011011011; 24: atf = 54'b000000000000000000000000010100010111110011000001101101; 25: atf = 54'b000000000000000000000000001010001011111001100000110110; 26: atf = 54'b000000000000000000000000000101000101111100110000011011; 27: atf = 54'b000000000000000000000000000010100010111110011000001101; 28: atf = 54'b000000000000000000000000000001010001011111001100000110; 29: atf = 54'b000000000000000000000000000000101000101111100110000011; 30: atf = 54'b000000000000000000000000000000010100010111110011000001; 31: atf = 54'b000000000000000000000000000000001010001011111001100000; 32: atf = 54'b000000000000000000000000000000000101000101111100110000; 33: atf = 54'b000000000000000000000000000000000010100010111110011000; 34: atf = 54'b000000000000000000000000000000000001010001011111001100; 35: atf = 54'b000000000000000000000000000000000000101000101111100110; 36: atf = 54'b000000000000000000000000000000000000010100010111110011; 37: atf = 54'b000000000000000000000000000000000000001010001011111001; 38: atf = 54'b000000000000000000000000000000000000000101000101111100; 39: atf = 54'b000000000000000000000000000000000000000010100010111110; 40: atf = 54'b000000000000000000000000000000000000000001010001011111; 41: atf = 54'b000000000000000000000000000000000000000000101000101111; 42: atf = 54'b000000000000000000000000000000000000000000010100010111; 43: atf = 54'b000000000000000000000000000000000000000000001010001011; 44: atf = 54'b000000000000000000000000000000000000000000000101000101; 45: atf = 54'b000000000000000000000000000000000000000000000010100010; 46: atf = 54'b000000000000000000000000000000000000000000000001010001; 47: atf = 54'b000000000000000000000000000000000000000000000000101000; 48: atf = 54'b000000000000000000000000000000000000000000000000010100; 49: atf = 54'b000000000000000000000000000000000000000000000000001010; 50: atf = 54'b000000000000000000000000000000000000000000000000000101; 51: atf = 54'b000000000000000000000000000000000000000000000000000010; 52: atf = 54'b000000000000000000000000000000000000000000000000000001; default: atf = 0; endcase at = appr(atf); if (rotMode==1) if (zi >= 0) begin xo <= xi - sy; yo <= yi + sx; zo <= zi - at; end else begin xo <= xi + sy; yo <= yi - sx; zo <= zi + at; end // if (zi... else if (yi >=0) begin xo <= xi + sy; yo <= yi - sx; zo <= zi + at; end else begin xo <= xi - sy; yo <= yi + sx; zo <= zi - at; end // if(yi... end // if(en) end // always endmodule // CordicRotator } ///////////////////////////////////////////////////////////////////// module CordicSeq (clk, rst, en, xi, yi, zi, xo, yo, zo); // { parameter depth = 15; parameter bitWidth = 16; parameter angBitWidth = 16; parameter angFracWidth = 16; parameter rotMode = 1; input clk, rst, en; input signed [bitWidth-1:0] xi, yi; input signed [angBitWidth-1:0] zi; output signed [bitWidth-1:0] xo, yo; output signed [angBitWidth-1:0] zo; wire signed [bitWidth-1:0] x [0:depth]; wire signed [bitWidth-1:0] y [0:depth]; wire signed [angBitWidth-1:0] z [0:depth]; assign x[0] = xi; assign y[0] = yi; assign z[0] = zi; generate genvar i; for (i = 0; i < depth; i=i+1) begin : gen_seq CordicRotator #( .smi(i), .bitWidth(bitWidth), .angBitWidth(angBitWidth), .angFracWidth(angFracWidth), .rotMode(rotMode)) cr (clk, rst, en, x[i], y[i], z[i], x[i+1], y[i+1], z[i+1]); end endgenerate //gen_seq assign xo = x[depth]; assign yo = y[depth]; assign zo = z[depth]; endmodule // CordicSeq } module synCordicAtan (clk, rst, en, x, y, atanOut); // { parameter depth=15; parameter bitWidth=16; parameter angBitWidth=16; input clk, rst, en; input [bitWidth-1:0] x, y; output reg [angBitWidth-1:0] atanOut; reg [1:0] quadrant [0:depth-1]; reg signed [bitWidth+1:0] xi=0, yi=0; wire signed [bitWidth-1:0] tmpx, tmpy; reg signed [angBitWidth-1:0] zi=0; wire signed [bitWidth+1:0] xo, yo; wire signed [angBitWidth-1:0] zo; wire signed [angBitWidth-1:0] negzo; wire [angBitWidth-2:0] tmpzero = 0; reg [5:0] rcnt; integer i; assign negzo = -zo; assign tmpx = -x; assign tmpy = -y; CordicSeq #( .depth(depth-1), .bitWidth(bitWidth+2), .angBitWidth(angBitWidth), .angFracWidth(angBitWidth-1), .rotMode(0)) cs (clk, rst, en, xi, yi, zi, xo, yo, zo); always @(`procline) begin if (rst==`activehigh) begin rcnt <= 0; xi <= 0; yi <= 0; atanOut <= 0; for (i=0;i<depth;i=i+1) quadrant[i] <= 2'b00; end else if (en) begin if(rcnt!=depth) rcnt <= rcnt+1; if (x[bitWidth-1] == 0) xi <= {x[bitWidth-1],x[bitWidth-1],x}; else xi <= {tmpx[bitWidth-1],tmpx[bitWidth-1],tmpx}; if (y[bitWidth-1] == 0) yi <= {y[bitWidth-1],y[bitWidth-1],y}; else yi <= {tmpy[bitWidth-1],tmpy[bitWidth-1],tmpy}; quadrant[0][0] <= x[bitWidth-1]; quadrant[0][1] <= y[bitWidth-1]; for (i=0;i<depth-1;i=i+1) quadrant[i+1] <= quadrant[i]; if(rcnt==depth) case (quadrant[depth-1]) 2'b00 : atanOut <= zo; 2'b01 : atanOut <= {1'b1, tmpzero} - $unsigned(zo); 2'b10 : atanOut <= negzo; // use intermediate to force sizing default : atanOut <= {1'b1, tmpzero} + $unsigned(zo); endcase end // if end // always endmodule // } Промоделировал в Quartus. Вроде работает (через 15 тактов выдает результат). Но не могу разобраться в каком формате задавать входные данные x и y (с фиксированной точкой или целое, знаковое или беззнаковое и т. п.). И как интерпретировать результат (т. е. в каком формате выдается результат, в градусах или радианах, сколько разрядов на дробную часть, сколько на целую и т. д.). Quote Share this post Link to post Share on other sites More sharing options...
ElrondTelvanni 0 March 3, 2015 Posted March 3, 2015 · Report post Я тоже где-то с полгода назад разбирался в кордике, в итоге всё написал сам, и разобрался, как оно работает. Если интересно, могу выложить код. Тут реализация похожа на мою, но стиль написания довольно корявый. К тому же, это только ядро CORDIC, а для того чтобы вычислять арктангенс, нужны ещё препроцессор и постпроцессор. На вход должны идти значения синуса и косинуса в формате (s16,15), а на выходе - зависит от реализации постпроцессора. Я сделал выходы в формате u16,16 и для угла, и для фазы. Quote Share this post Link to post Share on other sites More sharing options...
Andy111 0 March 3, 2015 Posted March 3, 2015 · Report post Я тоже где-то с полгода назад разбирался в кордике, в итоге всё написал сам, и разобрался, как оно работает. Если интересно, могу выложить код. Тут реализация похожа на мою, но стиль написания довольно корявый. К тому же, это только ядро CORDIC, а для того чтобы вычислять арктангенс, нужны ещё препроцессор и постпроцессор. На вход должны идти значения синуса и косинуса в формате (s16,15), а на выходе - зависит от реализации постпроцессора. Я сделал выходы в формате u16,16 и для угла, и для фазы. Буду премного благодарен если скинете свою версию:) Quote Share this post Link to post Share on other sites More sharing options...
ElrondTelvanni 0 March 3, 2015 Posted March 3, 2015 (edited) · Report post Ядро CORDIC // ================================================================================ ========================== // Project : FPGA DSP Library // File : cordic_core.sv // Title : CORDIC core module // Dependencies : - // // Description : This module implements iterative CORDIC algorhytm with parameterized precision. // Calculation started when request detected. 'rdy' flag asserted when result is ready. // Output data remain valid until the next request. // ================================================================================ ========================== // REVISION HISTORY // Version Date Author Change Description // 1.0 24.11.14 Noskov A.M. Initial creation; // ================================================================================ ========================== module cordic_core #( parameter SIG_W = 18, parameter ACC_W = 20 )( input logic rst_n, input logic clk, input logic req, input logic ccwr, input logic signed [sIG_W:0] x_i, input logic signed [sIG_W:0] y_i, input logic signed [ACC_W+1:0] a_i, output logic rdy, output logic busy, output logic signed [sIG_W:0] x_o, output logic signed [sIG_W:0] y_o, output logic signed [ACC_W+1:0] a_o ); `define ANG_SCALE(s) (ACC_W < 32) ? \ (ACC_W+2)'(unsigned'(``s`` >>> (32-ACC_W)) + unsigned'(1'(``s`` >> (32-ACC_W-1)))) : \ (ACC_W+2)'(``s``) localparam logic signed [ACC_W+1:0] ANG_TABLE[31] = '{ `ANG_SCALE(32'sd536870912), `ANG_SCALE(32'sd316933406), `ANG_SCALE(32'sd167458907), `ANG_SCALE(32'sd85004756), `ANG_SCALE(32'sd42667331), `ANG_SCALE(32'sd21354465), `ANG_SCALE(32'sd10679838), `ANG_SCALE(32'sd5340245), `ANG_SCALE(32'sd2670163), `ANG_SCALE(32'sd1335087), `ANG_SCALE(32'sd667544), `ANG_SCALE(32'sd333772), `ANG_SCALE(32'sd166886), `ANG_SCALE(32'sd83443), `ANG_SCALE(32'sd41722), `ANG_SCALE(32'sd20861), `ANG_SCALE(32'sd10430), `ANG_SCALE(32'sd5215), `ANG_SCALE(32'sd2608), `ANG_SCALE(32'sd1304), `ANG_SCALE(32'sd652), `ANG_SCALE(32'sd326), `ANG_SCALE(32'sd163), `ANG_SCALE(32'sd81), `ANG_SCALE(32'sd41), `ANG_SCALE(32'sd20), `ANG_SCALE(32'sd10), `ANG_SCALE(32'sd5), `ANG_SCALE(32'sd3), `ANG_SCALE(32'sd1), `ANG_SCALE(32'sd1) }; `undef ANG_SCALE localparam CNT_W = $clog2(ACC_W+1); localparam [CNT_W-1:0] LAST_CNT = (CNT_W)'(ACC_W) - 1'b1; logic busy_t1; logic [CNT_W-1:0] i; logic signed [sIG_W:0] x_s, y_s; assign x_s = signed'(x_o >>> i); assign y_s = signed'(y_o >>> i); assign busy = (i < LAST_CNT); assign rdy = (~busy & busy_t1); always_ff @(posedge clk or negedge rst_n) begin if(!rst_n) begin i <= '1; x_o <= 0; y_o <= 0; a_o <= 0; busy_t1 <= 0; end else begin busy_t1 <= busy; if(req & ~busy) begin i <= 0; x_o <= x_i; y_o <= y_i; a_o <= a_i; end if(busy) begin i <= i + 1'b1; x_o <= (ccwr) ? (x_o - y_s) : (x_o + y_s); y_o <= (ccwr) ? (y_o + x_s) : (y_o - x_s); a_o <= (ccwr) ? (a_o + ANG_TABLE[i]) : (a_o - ANG_TABLE[i]); end end end endmodule Пре/пост-процессор для арктангенса // ================================================================================ ========================== // Project : FPGA DSP Library // File : cart2pol.sv // Title : Cartesian-to-Polar Vector Transform // Dependencies : cordic_core.sv // // Description : This module performs transformation of vector from Cartesian to Polar coordinate system. // Transformation is based on the iterative implementation of the CORDIC algorhytm. // Vector precision parameterized for both coordinate systems separately; // Calculation started when request detected. 'rdy' flag asserted when result is ready. // Output data remain valid until the next request. // ================================================================================ ========================== // REVISION HISTORY // Version Date Author Change Description // 1.0 25.11.14 Noskov A.M. Initial creation; // ================================================================================ ========================== module cart2pol #( parameter SIG_W = 16, parameter ACC_W = 16 )( input logic rst_n, input logic clk, input logic req, input logic signed [sIG_W-1:0] cos, input logic signed [sIG_W-1:0] sin, output logic rdy, output logic busy, output logic [ACC_W-1:0] ang, output logic [sIG_W-1:0] hyp ); localparam [sIG_W-1:0] X_SCALE = (2**SIG_W-1) * 0.60723; logic ccwr; logic signed [sIG_W:0] x_i, y_i; logic signed [sIG_W:0] x_o, y_o; logic signed [ACC_W+1:0] a_o, a_i; cordic_core #( .SIG_W (SIG_W), .ACC_W (ACC_W)) dds_core ( .rst_n, .clk, .req, .ccwr, .x_i, .y_i, .a_i, .rdy, .busy, .x_o, .y_o, .a_o ); // Clockwise / Counter-clockwise rotate assign ccwr = y_o[sIG_W]; // Quadrant selector always_comb begin : quadrant_sel x_i = cos; y_i = -sin; a_i = '0; if(cos[sIG_W-1]) begin // X < 0 a_i[ACC_W-:2] = 2'b01; // + 180 deg. x_i = -cos; y_i = sin; end else if(sin[sIG_W-1]) // Y < 0 a_i[ACC_W-:2] = 2'b10; // + 360 deg. end : quadrant_sel function logic [sIG_W-1:0] calc_hyp(logic signed [sIG_W:0] i_x); logic [sIG_W*2+1:0] mult; mult = unsigned'(i_x) * X_SCALE; calc_hyp = mult[sIG_W*2-1:SIG_W]; endfunction // Output assignments assign ang = a_o[ACC_W-1:0]; assign hyp = calc_hyp(x_o); endmodule Edited March 3, 2015 by ~Elrond~ Quote Share this post Link to post Share on other sites More sharing options...
serjj1333 0 March 4, 2015 Posted March 4, 2015 · Report post Могу свою реализацию подкинуть (под cyclone), может кому пригодится. Способ применения понятен из тестбенча. Перед сборкой нужно поставить флажок __SYNTHESIS__. cordic_light.zip Quote Share this post Link to post Share on other sites More sharing options...
Krys 2 March 5, 2015 Posted March 5, 2015 · Report post Выложите ещё пожалуйста те доки (или ссылки) по которым удалось разобраться, как это работает. Quote Share this post Link to post Share on other sites More sharing options...
serjj1333 0 March 5, 2015 Posted March 5, 2015 · Report post Тысячи их, вот например: cordic____.pdf Matlab1 Matlab2 Quote Share this post Link to post Share on other sites More sharing options...
Lerk 0 March 5, 2015 Posted March 5, 2015 (edited) · Report post Выложите ещё пожалуйста те доки (или ссылки) по которым удалось разобраться, как это работает. Если дружить с математикой нет желания, то просто и доступно есть вот такой гайд: CORDIC for dummies Edited March 5, 2015 by Lerk Quote Share this post Link to post Share on other sites More sharing options...
ElrondTelvanni 0 March 5, 2015 Posted March 5, 2015 · Report post Krys Я разбирался и писал свою версию исключительно по CORDIC for dummies. Крайне полезный и доступный для понимания материал, есть исходники. Оставалось только перевести в HDL и параметризовать. В аттаче моя версия вместе с тестбенчем. atan2.zip Quote Share this post Link to post Share on other sites More sharing options...
Krys 2 March 10, 2015 Posted March 10, 2015 · Report post Большое спасибо всем отозвавшимся! Quote Share this post Link to post Share on other sites More sharing options...
Abo 0 November 25, 2017 Posted November 25, 2017 · Report post Krys Я разбирался и писал свою версию исключительно по CORDIC for dummies. Крайне полезный и доступный для понимания материал, есть исходники. Оставалось только перевести в HDL и параметризовать. В аттаче моя версия вместе с тестбенчем. Мне тоже потребовалось сделать кордик для расчета арктангенса. Воспользовался этим кодом. Благодарю автора. Со своей стороны прикладываю немного ускоренный вариант с хранением сдвинутого предыдущего значения в отдельном регистре. До изменения на 10M50 максимальная тактовая была не более 119МГц, после выросла до 137МГц. Мне нужно было чтобы при 120МГц работало. module cordic_core #( parameter SIG_W = 18, parameter ACC_W = 20 )( input rst, input clk, input req, input ccwr, input signed [SIG_W:0] x_i, input signed [SIG_W:0] y_i, input signed [ACC_W+1:0] a_i, output rdy, output busy, output reg signed [SIG_W:0] x_o, output reg signed [SIG_W:0] y_o, output reg signed [ACC_W+1:0] a_o ); `define ANG_SCALE(s) (ACC_W < 32) ? \ (ACC_W+2)'(unsigned'(``s`` >>> (32-ACC_W)) + unsigned'(1'(``s`` >> (32-ACC_W-1)))) : \ (ACC_W+2)'(``s``) localparam logic signed [ACC_W+1:0] ANG_TABLE[31] = '{ `ANG_SCALE(32'sd536870912), `ANG_SCALE(32'sd316933406), `ANG_SCALE(32'sd167458907), `ANG_SCALE(32'sd85004756), `ANG_SCALE(32'sd42667331), `ANG_SCALE(32'sd21354465), `ANG_SCALE(32'sd10679838), `ANG_SCALE(32'sd5340245), `ANG_SCALE(32'sd2670163), `ANG_SCALE(32'sd1335087), `ANG_SCALE(32'sd667544), `ANG_SCALE(32'sd333772), `ANG_SCALE(32'sd166886), `ANG_SCALE(32'sd83443), `ANG_SCALE(32'sd41722), `ANG_SCALE(32'sd20861), `ANG_SCALE(32'sd10430), `ANG_SCALE(32'sd5215), `ANG_SCALE(32'sd2608), `ANG_SCALE(32'sd1304), `ANG_SCALE(32'sd652), `ANG_SCALE(32'sd326), `ANG_SCALE(32'sd163), `ANG_SCALE(32'sd81), `ANG_SCALE(32'sd41), `ANG_SCALE(32'sd20), `ANG_SCALE(32'sd10), `ANG_SCALE(32'sd5), `ANG_SCALE(32'sd3), `ANG_SCALE(32'sd1), `ANG_SCALE(32'sd1) }; `undef ANG_SCALE localparam CNT_W = $clog2(ACC_W+1); localparam [CNT_W-1:0] LAST_CNT = (CNT_W)'(ACC_W) - 1'b1; logic busy_t1; logic [4:0] i; // Изначально разрядность была настраиваемой [CNT_W-1:0], изменил чтобы квартус не предупреждал. logic signed [SIG_W:0] x_s, y_s; assign busy = (i < LAST_CNT); assign rdy = (~busy & busy_t1); wire signed [SIG_W:0] next_x_o = (ccwr) ? (x_o - y_s) : (x_o + y_s); wire signed [SIG_W:0] next_y_o = (ccwr) ? (y_o + x_s) : (y_o - x_s); wire signed [SIG_W:0] next_x_s = signed'(next_x_o >>> (i+1'b1)); wire signed [SIG_W:0] next_y_s = signed'(next_y_o >>> (i+1'b1)); always_ff @(posedge clk, posedge rst) begin if(rst) begin busy_t1 <= 0; i <= '1; x_o <= 0; y_o <= 0; x_s <= 0; y_s <= 0; a_o <= 0; end else begin busy_t1 <= busy; if(req | busy) if(req) begin i <= 0; x_o <= x_i; y_o <= y_i; x_s <= x_i; y_s <= y_i; a_o <= a_i; end else begin i <= i + 1'b1; x_o <= next_x_o; //(ccwr) ? (x_o - y_s) : (x_o + y_s); y_o <= next_y_o; //(ccwr) ? (y_o + x_s) : (y_o - x_s); x_s <= next_x_s; y_s <= next_y_s; a_o <= a_o + (ccwr ? ANG_TABLE[i] : -ANG_TABLE[i]); //(ccwr) ? (a_o + ANG_TABLE[i]) : (a_o - ANG_TABLE[i]); end end end endmodule Quote Share this post Link to post Share on other sites More sharing options...