├── images └── Cordic.excalidraw.png ├── src ├── get_angle_angle.py ├── get_angle.py ├── testbench │ ├── tb_angle_to_radian.v │ ├── tb_radian_to_angle.v │ └── tb_atan.v ├── ROM_16.v ├── ROM_16_angle.v ├── ROM_28.v ├── CordicAtan_28.v ├── CordicAtan.v └── CordicAtan_angle.v └── README.md /images/Cordic.excalidraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bowen-0x00/cordic-algorithm-verilog/HEAD/images/Cordic.excalidraw.png -------------------------------------------------------------------------------- /src/get_angle_angle.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | for i in range(0, 22): 4 | angle = math.atan(2**(-i)) * 180 / math.pi 5 | shift = int(angle * 2**16) 6 | # print(shift) 7 | s = bin(shift)[2:] 8 | # print(s) 9 | print('b' + '0' * ((16 + 6) - len(s)) + s) -------------------------------------------------------------------------------- /src/get_angle.py: -------------------------------------------------------------------------------- 1 | import math 2 | precision = 28 3 | for i in range(0, precision): 4 | angle = math.atan(2**(-i)) 5 | shift = int(angle * 2**precision) 6 | # print(shift) 7 | s = bin(shift)[2:] 8 | # print(s) 9 | print('b' + '0' * (precision - len(s)) + s) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cordic-algorithm-verilog 2 | 3 | 使用Cordic算法函数运算,在资源受限的设备上运行(如资源较少的FPGA、嵌入式MCU),避免了浮点运算、乘法、除法,只用移位和加法函数的计算。 4 | 5 | 6 | ## atan2 7 | 8 | 经过权衡,目前atan的误差 < 0.006° 9 | 10 | 代码使用Verilog编写,查表数据使用python脚本生成。 11 | 12 | 使用简单的SystemVerilog进行了[0, 360°]测试。 13 | 14 | 15 | ### 原理 16 | 17 | ![](images/Cordic.excalidraw.png) -------------------------------------------------------------------------------- /src/testbench/tb_angle_to_radian.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/1ns 2 | `include "includes/defines.v" 3 | `include "Trigonometric/angle_to_radian.v" 4 | 5 | module tb_angle_to_radian; 6 | 7 | reg [8:0] angle; 8 | wire [31:0] radian; 9 | 10 | angle_to_radian u_angle_to_radian ( 11 | .angle(angle), 12 | .radian(radian) 13 | ); 14 | 15 | 16 | initial begin 17 | $dumpfile("tb_angle_to_radian.vcd"); 18 | $dumpvars(0, tb_angle_to_radian); 19 | end 20 | 21 | integer i; 22 | integer golden; 23 | initial begin 24 | angle = 0; 25 | #3; 26 | for (i = 0; i <= 360; i++) begin 27 | angle <= i; 28 | golden <= i * (1<<16) * (3.141592653589 / 180.0); 29 | #1 30 | if (radian - golden <= 10 || golden - radian <= 10) begin//0.00015 31 | end else begin 32 | $display("fail at: %d", angle); 33 | $finish; 34 | end 35 | #1; 36 | 37 | 38 | end 39 | $display("success"); 40 | 41 | #100 $finish; 42 | end 43 | 44 | endmodule -------------------------------------------------------------------------------- /src/testbench/tb_radian_to_angle.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/1ns 2 | `include "includes/defines.v" 3 | `include "Trigonometric/angle_to_radian.v" 4 | `include "Trigonometric/radian_to_angle.v" 5 | 6 | module tb_radian_to_angle; 7 | 8 | reg [8:0] angle; 9 | wire [8:0] angle_o; 10 | reg [31:0] radian; 11 | 12 | 13 | 14 | angle_to_radian u_angle_to_radian ( 15 | .angle(angle), 16 | .radian(radian) 17 | ); 18 | 19 | radian_to_angle u_radian_to_angle ( 20 | .radian(radian), 21 | .angle(angle_o) 22 | ); 23 | 24 | initial begin 25 | $dumpfile("tb_radian_to_angle.vcd"); 26 | $dumpvars(0, tb_radian_to_angle); 27 | end 28 | 29 | integer i; 30 | integer golden; 31 | initial begin 32 | angle = 0; 33 | #3; 34 | for (i = 0; i <= 360; i++) begin 35 | #1 36 | if (radian - angle_o <= 1 || golden - angle_o <= 1) begin//0.00015 37 | end else begin 38 | $display("fail at: %d", angle); 39 | $finish; 40 | end 41 | #1; 42 | end 43 | $display("success"); 44 | 45 | #100 $finish; 46 | end 47 | 48 | endmodule -------------------------------------------------------------------------------- /src/ROM_16.v: -------------------------------------------------------------------------------- 1 | `ifndef M_get_radian 2 | `define M_get_radian 3 | 4 | module get_radian ( 5 | input [4:0] address, 6 | output reg [15:0] data 7 | ); 8 | always @(*) begin 9 | case (address) 10 | 4'd0: data = 16'b1100100100001111; 11 | 4'd1: data = 16'b0111011010110001; 12 | 4'd2: data = 16'b0011111010110110; 13 | 4'd3: data = 16'b0001111111010101; 14 | 4'd4: data = 16'b0000111111111010; 15 | 4'd5: data = 16'b0000011111111111; 16 | 4'd6: data = 16'b0000001111111111; 17 | 4'd7: data = 16'b0000000111111111; 18 | 4'd8: data = 16'b0000000011111111; 19 | 4'd9: data = 16'b0000000001111111; 20 | 4'd10: data = 16'b0000000000111111; 21 | 4'd11: data = 16'b0000000000011111; 22 | 4'd12: data = 16'b0000000000001111; 23 | 4'd13: data = 16'b0000000000000111; 24 | 4'd14: data = 16'b0000000000000011; 25 | 4'd15: data = 16'b0000000000000001; 26 | default: data = 0; 27 | endcase 28 | end 29 | endmodule 30 | 31 | `endif -------------------------------------------------------------------------------- /src/ROM_16_angle.v: -------------------------------------------------------------------------------- 1 | `ifndef M_get_radian 2 | `define M_get_radian 3 | 4 | module get_radian ( 5 | input [4:0] address, 6 | output reg [21:0] data 7 | ); 8 | always @(*) begin 9 | case (address) 10 | 5'd0: data = 22'b1011010000000000000000; 11 | 5'd1: data = 22'b0110101001000010100111; 12 | 5'd2: data = 22'b0011100000100101000111; 13 | 5'd3: data = 22'b0001110010000000000001; 14 | 5'd4: data = 22'b0000111001001110001010; 15 | 5'd5: data = 22'b0000011100101000110111; 16 | 5'd6: data = 22'b0000001110010100101010; 17 | 5'd7: data = 22'b0000000111001010010110; 18 | 5'd8: data = 22'b0000000011100101001011; 19 | 5'd9: data = 22'b0000000001110010100101; 20 | 5'd10: data = 22'b0000000000111001010010; 21 | 5'd11: data = 22'b0000000000011100101001; 22 | 5'd12: data = 22'b0000000000001110010100; 23 | 5'd13: data = 22'b0000000000000111001010; 24 | 5'd14: data = 22'b0000000000000011100101; 25 | 5'd15: data = 22'b0000000000000001110010; 26 | 5'd16: data = 22'b0000000000000000111001; 27 | 5'd17: data = 22'b0000000000000000011100; 28 | 5'd18: data = 22'b0000000000000000001110; 29 | 5'd19: data = 22'b0000000000000000000111; 30 | 5'd20: data = 22'b0000000000000000000011; 31 | 5'd21: data = 22'b0000000000000000000001; 32 | default: data = 0; 33 | endcase 34 | end 35 | endmodule 36 | 37 | `endif -------------------------------------------------------------------------------- /src/testbench/tb_atan.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns/1ns 2 | `include "includes/defines.v" 3 | `include "Trigonometric/CordicAtan_angle.v" 4 | 5 | module tb_atan; 6 | 7 | reg clk; 8 | reg rst_n; 9 | reg signed [8:0] angle; 10 | wire valid; 11 | reg [15:0] x_in; 12 | reg [15:0] y_in; 13 | wire [31:0] theta; 14 | 15 | CordicAtan u_CordicAtan( 16 | .clk(clk), 17 | .rst_n(rst_n), 18 | .valid(valid), 19 | .x_in(x_in), 20 | .y_in(y_in), 21 | .theta(theta) 22 | ); 23 | initial begin 24 | $dumpfile("tb_atan.vcd"); 25 | $dumpvars(0, tb_atan); 26 | end 27 | 28 | always begin 29 | #10 30 | clk = ~clk; 31 | end 32 | 33 | integer i; 34 | integer golden; 35 | real radian; 36 | initial begin 37 | clk <= 0; 38 | rst_n <= 0; 39 | x_in <= 16'd10000; 40 | y_in <= 16'd0; 41 | i <= 0; 42 | golden <= 0; 43 | #13 44 | rst_n <= 1; 45 | 46 | #1000000 47 | $finish; 48 | end 49 | 50 | always @(posedge valid) begin 51 | if (i >= 360) begin 52 | $display("success"); 53 | $finish; 54 | end 55 | #4 56 | if (theta - golden <= 9'h185|| golden - theta <= 9'h185) begin//0.006 57 | // $display((theta > golden ? theta - golden : golden - theta) * 0.0000152587890625); 58 | end else begin 59 | $display("fail at: %d", i); 60 | $finish; 61 | end 62 | 63 | i = i + 1; 64 | rst_n <= 0; 65 | radian = i * 3.1415926535897932384626433832795 / 180.0; 66 | x_in <= 16'd10000 * $cos(radian); 67 | y_in <= 16'd10000 * $sin(radian); 68 | golden <= i << 16; 69 | #4 70 | rst_n <= 1; 71 | end 72 | 73 | 74 | // initial begin 75 | // clk <= 0; 76 | // rst_n <= 0; 77 | // x_in <= 16'd10000; 78 | // y_in <= 16'd1763; 79 | // #13 80 | 81 | // rst_n <= 1; 82 | // #100000 83 | // $finish; 84 | // end 85 | 86 | 87 | endmodule -------------------------------------------------------------------------------- /src/ROM_28.v: -------------------------------------------------------------------------------- 1 | `ifndef M_get_radian 2 | `define M_get_radian 3 | 4 | module get_radian ( 5 | input [5:0] address, 6 | output reg [27:0] data 7 | ); 8 | always @(*) begin 9 | case (address) 10 | 5'd0: data = 28'b1100100100001111110110101010; 11 | 5'd1: data = 28'b0111011010110001100111000001; 12 | 5'd2: data = 28'b0011111010110110111010111111; 13 | 5'd3: data = 28'b0001111111010101101110101001; 14 | 5'd4: data = 28'b0000111111111010101011011101; 15 | 5'd5: data = 28'b0000011111111111010101010110; 16 | 5'd6: data = 28'b0000001111111111111010101010; 17 | 5'd7: data = 28'b0000000111111111111111010101; 18 | 5'd8: data = 28'b0000000011111111111111111010; 19 | 5'd9: data = 28'b0000000001111111111111111111; 20 | 5'd10: data = 28'b0000000000111111111111111111; 21 | 5'd11: data = 28'b0000000000011111111111111111; 22 | 5'd12: data = 28'b0000000000001111111111111111; 23 | 5'd13: data = 28'b0000000000000111111111111111; 24 | 5'd14: data = 28'b0000000000000011111111111111; 25 | 5'd15: data = 28'b0000000000000001111111111111; 26 | 5'd16: data = 28'b0000000000000000111111111111; 27 | 5'd17: data = 28'b0000000000000000011111111111; 28 | 5'd18: data = 28'b0000000000000000001111111111; 29 | 5'd19: data = 28'b0000000000000000000111111111; 30 | 5'd20: data = 28'b0000000000000000000011111111; 31 | 5'd21: data = 28'b0000000000000000000001111111; 32 | 5'd22: data = 28'b0000000000000000000000111111; 33 | 5'd23: data = 28'b0000000000000000000000011111; 34 | 5'd24: data = 28'b0000000000000000000000001111; 35 | 5'd25: data = 28'b0000000000000000000000000111; 36 | 5'd26: data = 28'b0000000000000000000000000011; 37 | 5'd27: data = 28'b0000000000000000000000000010; 38 | default: data = 0; 39 | endcase 40 | end 41 | endmodule 42 | 43 | `endif -------------------------------------------------------------------------------- /src/CordicAtan_28.v: -------------------------------------------------------------------------------- 1 | 2 | `ifndef M_CordicAtan 3 | `define M_CordicAtan 4 | `include "includes/defines.v" 5 | `include "Trigonometric/ROM_28.v" 6 | 7 | module CordicAtan #( 8 | parameter DW = 32 9 | ) ( 10 | input clk, 11 | input rst_n, 12 | output reg valid, 13 | input signed [15:0] x_in, 14 | input signed [15:0] y_in, 15 | output reg signed [31:0] theta 16 | ); 17 | reg [5:0] cnt; 18 | 19 | always @(posedge clk or negedge rst_n) begin 20 | if (!rst_n) 21 | cnt <= 6'b111111; 22 | else if (!valid) 23 | cnt <= cnt + 1; 24 | end 25 | 26 | reg signed [31:0] x_iter; 27 | reg signed [31:0] y_iter; 28 | reg signed [31:0] theta_acc; 29 | reg [31:0] theta_step; 30 | assign theta_step[31:28] = 0; 31 | 32 | get_radian u_get_radian ( 33 | .address(cnt), 34 | .data(theta_step[27:0]) 35 | ); 36 | 37 | always @(posedge clk or negedge rst_n) begin 38 | if (!rst_n) begin 39 | valid <= 0; 40 | x_iter <= x_in; 41 | y_iter <= y_in; 42 | theta_acc <= 0; 43 | end else begin 44 | if (y_iter == 0) begin 45 | x_iter <= x_iter; 46 | y_iter <= y_iter; 47 | theta_acc <= theta_acc; 48 | theta <= (theta_acc >> 12); 49 | valid <= 1; 50 | end else begin 51 | if (cnt == 6'b111111) begin 52 | x_iter <= x_in; 53 | y_iter <= y_in; 54 | theta_acc <= 0; 55 | end else begin 56 | //x_2 = x1 - y1 * di * tan 57 | //y_2 = y1 + x1 * di * tan 58 | if (y_iter[31] == 1'b0) begin// > 0 clockwise 59 | x_iter <= x_iter + (y_iter >>> cnt); 60 | y_iter <= y_iter - (x_iter >>> cnt); 61 | theta_acc <= theta_acc + theta_step; 62 | end else begin// > 0 counterclockwise di = -1 63 | x_iter <= x_iter - (y_iter >>> cnt); 64 | y_iter <= y_iter + (x_iter >>> cnt); 65 | theta_acc <= theta_acc - theta_step; 66 | end 67 | end 68 | end 69 | end 70 | end 71 | 72 | endmodule 73 | 74 | `endif -------------------------------------------------------------------------------- /src/CordicAtan.v: -------------------------------------------------------------------------------- 1 | 2 | `ifndef M_CordicAtan 3 | `define M_CordicAtan 4 | `include "includes/defines.v" 5 | `include "Trigonometric/ROM_16.v" 6 | 7 | module CordicAtan #( 8 | parameter DW = 32 9 | ) ( 10 | input clk, 11 | input rst_n, 12 | output reg valid, 13 | input signed [15:0] x_in, 14 | input signed [15:0] y_in, 15 | output reg signed [31:0] theta 16 | ); 17 | reg [4:0] cnt; 18 | 19 | always @(posedge clk or negedge rst_n) begin 20 | if (!rst_n) 21 | cnt <= 5'b11111; 22 | else if (!valid) 23 | cnt <= cnt + 1; 24 | end 25 | 26 | reg signed [31:0] x_iter; 27 | reg signed [31:0] y_iter; 28 | reg [31:0] theta_acc; 29 | reg [15:0] theta_step; 30 | 31 | get_radian u_get_radian ( 32 | .address(cnt), 33 | .data(theta_step) 34 | ); 35 | 36 | always @(posedge clk or negedge rst_n) begin 37 | if (!rst_n) begin 38 | valid <= 0; 39 | x_iter <= x_in; 40 | y_iter <= y_in; 41 | theta_acc <= 0; 42 | end else begin 43 | if (y_iter == 0) begin 44 | x_iter <= x_iter; 45 | y_iter <= y_iter; 46 | theta_acc <= theta_acc; 47 | theta <= theta_acc; 48 | valid <= 1; 49 | end else begin 50 | if (cnt == 5'b11111) begin 51 | case ({x_in[15], y_in[15]}) 52 | 2'00: begin 53 | x_iter <= x_in; 54 | y_iter <= y_in; 55 | theta_acc <= 0; 56 | end 57 | 2'01: begin 58 | x_iter <= -x_in; 59 | y_iter <= y_in; 60 | theta_acc <= 90 << 16; 61 | end 62 | 2'11: begin 63 | x_iter <= x_in; 64 | y_iter <= y_in; 65 | theta_acc <= 0; 66 | end 67 | 2'10: begin 68 | end 69 | 70 | endcase 71 | 72 | end else begin 73 | //x_2 = x1 - y1 * di * tan 74 | //y_2 = y1 + x1 * di * tan 75 | if (y_iter[31] == 1'b0) begin// > 0 clockwise 76 | x_iter <= x_iter + (y_iter >>> cnt); 77 | y_iter <= y_iter - (x_iter >>> cnt); 78 | theta_acc <= theta_acc + theta_step; 79 | end else begin// > 0 counterclockwise di = -1 80 | x_iter <= x_iter - (y_iter >>> cnt); 81 | y_iter <= y_iter + (x_iter >>> cnt); 82 | theta_acc <= theta_acc - theta_step; 83 | end 84 | end 85 | end 86 | end 87 | end 88 | 89 | endmodule 90 | 91 | `endif -------------------------------------------------------------------------------- /src/CordicAtan_angle.v: -------------------------------------------------------------------------------- 1 | 2 | `ifndef M_CordicAtan 3 | `define M_CordicAtan 4 | `include "includes/defines.v" 5 | `include "Trigonometric/ROM_16_angle.v" 6 | 7 | module CordicAtan #( 8 | parameter DW = 32 9 | ) ( 10 | input clk, 11 | input rst_n, 12 | output reg valid, 13 | input signed [15:0] x_in, 14 | input signed [15:0] y_in, 15 | output reg signed [31:0] theta 16 | ); 17 | reg [4:0] cnt; 18 | wire signed [31:0] x_extended = {{8{x_in[15]}}, x_in, 8'd0}; 19 | wire signed [31:0] y_extended = {{8{y_in[15]}}, y_in, 8'd0}; 20 | 21 | always @(posedge clk or negedge rst_n) begin 22 | if (!rst_n) 23 | cnt <= 5'b11111; 24 | else if (!valid) 25 | cnt <= cnt + 1; 26 | end 27 | 28 | reg signed [31:0] x_iter; 29 | reg signed [31:0] y_iter; 30 | reg signed [31:0] theta_acc; 31 | reg [31:0] theta_step; 32 | assign theta_step[31:22] = 0; 33 | 34 | get_radian u_get_radian ( 35 | .address(cnt), 36 | .data(theta_step[21:0]) 37 | ); 38 | task automatic convert_to_quadrant_one(); 39 | case ({x_in[15], y_in[15]}) 40 | 2'b00: begin 41 | x_iter <= x_extended; 42 | y_iter <= y_extended; 43 | theta_acc <= 0; 44 | end 45 | 2'b10: begin 46 | x_iter <= y_extended; 47 | y_iter <= -x_extended; 48 | theta_acc <= 90 << 16; 49 | end 50 | 2'b11: begin 51 | x_iter <= -x_extended; 52 | y_iter <= -y_extended; 53 | theta_acc <= 180 << 16; 54 | end 55 | 2'b01: begin 56 | x_iter <= -y_extended; 57 | y_iter <= x_extended; 58 | theta_acc <= 270 << 16; 59 | end 60 | endcase 61 | endtask //automatic 62 | always @(posedge clk or negedge rst_n) begin 63 | if (!rst_n) begin 64 | valid <= 0; 65 | convert_to_quadrant_one(); 66 | end else begin 67 | if (y_iter[31:8] == 0 || cnt == 5'b11110) begin 68 | x_iter <= x_iter; 69 | y_iter <= y_iter; 70 | theta_acc <= theta_acc; 71 | theta <= theta_acc; 72 | valid <= 1; 73 | end else begin 74 | if (cnt == 5'b11111) begin 75 | convert_to_quadrant_one(); 76 | end else begin 77 | //x_2 = x1 - y1 * di * tan 78 | //y_2 = y1 + x1 * di * tan 79 | if (y_iter[31] == 1'b0) begin// > 0 clockwise 80 | x_iter <= x_iter + (y_iter >>> cnt); 81 | y_iter <= y_iter - (x_iter >>> cnt); 82 | theta_acc <= theta_acc + theta_step; 83 | end else begin// > 0 counterclockwise di = -1 84 | x_iter <= x_iter - (y_iter >>> cnt); 85 | y_iter <= y_iter + (x_iter >>> cnt); 86 | theta_acc <= theta_acc - theta_step; 87 | end 88 | end 89 | end 90 | end 91 | end 92 | 93 | endmodule 94 | 95 | `endif --------------------------------------------------------------------------------