在BLE的物理層實現中,會發現在調製解調的過程中上變頻,下變頻以及混頻等操作需要將訊號和正弦訊號(餘弦訊號)做乘積,這就需要在晶片裡實現一個數字訊號發生器模組(DDS),假設這些部分都透過模擬來實現,在BLE receiver端還需要根據I Q訊號來計算相位值,即進行反三角函式運算(arctan)。在一些不怎麼關係memory資源的片子中,查表法是一個很實用的方法,效率高且簡單。但是有些片子的memory資源不是那麼的豐富,這就需要利用CORDIC演算法來實現了。

CORDIC演算法的核心原理是透過將

旋轉角度 #FormatImgID_1# 分成多個連續的小偏轉角,透過逐次搖擺逼近目標旋轉角度的過程

。如圖所示:

BLE 學習筆記_1(CORDIC演算法)

目標旋轉過程

從向量(x1,y1)旋轉到(x2,y2)的過程可以表示為:

\begin{equation} \left[ \begin{array}{c}    x_2\\    y_2\\  \end{array} \right] = \left[ \begin{array}{ccc}     cos\theta & -sin\theta  \\     sin\theta & cos\theta  \\  \end{array} \right] \left[ \begin{array}{c}     x_{1} \\      y_{2} \\  \end{array} \right] \end{equation}

(1)

對上式提取公因式:

\begin{equation} \left[ \begin{array}{c}    x_2\\    y_2\\  \end{array} \right] =cos\theta \left[ \begin{array}{ccc}      1 & -tan\theta  \\     tan\theta & 1  \\  \end{array} \right] \left[ \begin{array}{c}     x_{1} \\      y_{2} \\  \end{array} \right] \end{equation}

(2)

CORDIC演算法將目標的旋轉角度變為如下形式:

\theta=\sum_{i=0}^{N}{d_{i}\theta_{i}},d_{i}={-1,1}

(3)

上式中的

d_i

表示旋轉的方向,另外為了硬體的實現簡單,令:

tan\theta_{i}=2^{-i}

則公式(2)可以表示為:

\begin{equation} \left[ \begin{array}{c}    x_2\\    y_2\\  \end{array} \right] =\prod_{i=0}^{N}1/\sqrt{1+2^{-2i}} \left[ \begin{array}{ccc}      1 & -d_{i}2^{-i} \\     d_{i}2^{-i} & 1  \\  \end{array} \right] \left[ \begin{array}{c}     x_{1} \\      y_{2} \\  \end{array} \right] \end{equation}

(4)

上式中

\prod_{i=0}^{N}1/\sqrt{1+2^{-2i}}

稱為縮放因子,N趨於無窮大的時候,該值接近於0。607253。因此在硬體上實現的時候可以採用常數表示。因此,CORDIC演算法在硬體上實現的核心公式如下所示:

\begin{equation} \left\{ \begin{array}{c}     x_{i+1}=x_{i}-d_{i}2^{-i} \\     y_{i+1}=y_{i}+d_{i}2^{-i} \\        z_{i+1}=z_{i}-d_{i}\theta_{i} \end{array} \right. \end{equation}

(5)

至此,我們就可以編寫程式碼來驗證CORDIC演算法了,這裡我用matlab編寫了基於CORDIC的訊號發生器程式碼和相位計算程式碼。

首先驗證DDS產生一個餘弦波:

BLE 學習筆記_1(CORDIC演算法)

DDS

在看下相位計算:

BLE 學習筆記_1(CORDIC演算法)

arctan cal

其matlab程式碼分別如下:

dds:

function

cosa

=

dds_cordic

angle,Iterate

if

angle

<

-

pi

/

2

||

angle

>

pi

/

2

if

angle

<

0

cosa

=

dds_cordic

angle

+

pi

Iterate

);

else

cosa

=

dds_cordic

angle

-

pi

Iterate

);

end

cosa

=

-

cosa

% v = -v; % flip the sign for second or third quadrant

return

end

% Iterate = 16;%迭代次數

x

=

zeros

Iterate

+

1

1

);

y

=

zeros

Iterate

+

1

1

);

z

=

zeros

Iterate

+

1

1

);

x

1

=

0。607253

%初始設定

z

1

=

angle

%待求角度θ

for

i

=

1

Iterate

if

z

i

>

=

0

d

=

1

else

d

=

-

1

end

x

i

+

1

=

x

i

-

d

*

y

i

*

2

^

-

i

-

1

)));

y

i

+

1

=

y

i

+

d

*

x

i

*

2

^

-

i

-

1

)));

z

i

+

1

=

z

i

-

d

*

atan

2

^

-

i

-

1

)));

end

cosa

=

x

Iterate

+

1

);

end

arctan:

function

arctan

=

arctan_cordic

sin_data,cos_data,Iterate

% Iterate = 16;%迭代次數

x

=

zeros

Iterate

+

1

1

);

y

=

zeros

Iterate

+

1

1

);

z

=

zeros

Iterate

+

1

1

);

x

1

=

cos_data

y

1

=

sin_data

k

=

0。607253

for

i

=

1

Iterate

if

y

i

>

=

0

d

=

-

1

else

d

=

1

end

x

i

+

1

=

x

i

-

d

*

y

i

*

2

^

-

i

-

1

)));

y

i

+

1

=

y

i

+

d

*

x

i

*

2

^

-

i

-

1

)));

z

i

+

1

=

z

i

-

d

*

atan

2

^

-

i

-

1

)));

end

arctan

=

z

Iterate

+

1

);

end

要注意的是arctan函式的輸入必須限制在第一和第四象限,如果不在的話要旋轉變換過來。

編寫完matlab程式碼後我們就可以考慮用FPGA去實現了。我這裡編寫了一個pipeline實現的CORDIC演算法來實現DDS的功能,程式碼如下:

`timescale

1

ns

/

1

ps

//////////////////////////////////////////////////////////////////////////////////

// Company:

// Engineer:

//

// Create Date: 2020/06/19 15:22:59

// Design Name:

// Module Name: cordic_dds_pipe_line

// Project Name:

// Target Devices:

// Tool Versions:

// Description:

//

// Dependencies:

//

// Revision:

// Revision 0。01 - File Created

// Additional Comments:

//

//////////////////////////////////////////////////////////////////////////////////

module

cordic_dds_pipe_line

input

clk

input

rst_n

input

phase_vaild

input

signed

32

-

1

0

phase

output

reg

cordic_vaild

output

signed

32

-

1

0

cordic_cos_data

);

//51471 30385 16054 8149 4090 2047 1023 511 255 127 63 31 15 7 3 1

`define arctan1 32‘d51471

// floor(arctan(2^(0)) * 2^16)

`define arctan2 32’d30385

// floor(arctan(2^(-1)) * 2^16)

`define arctan3 32‘d16054

// floor(arctan(2^(-2)) * 2^16)

`define arctan4 32’d8149

// floor(arctan(2^(-3)) * 2^16)

`define arctan5 32‘d4090

// floor(arctan(2^(-4)) * 2^16)

`define arctan6 32’d2047

// floor(arctan(2^(-5)) * 2^16)

`define arctan7 32‘d1023

// floor(arctan(2^(-6)) * 2^16)

`define arctan8 32’d511

// floor(arctan(2^(-7)) * 2^16)

`define arctan9 32‘d255

// floor(arctan(2^(-8)) * 2^16)

`define arctan10 32’d127

// floor(arctan(2^(-9)) * 2^16)

`define arctan11 32‘d63

// floor(arctan(2^(-10)) * 2^16)

`define arctan12 32’d31

// floor(arctan(2^(-11)) * 2^16)

`define arctan13 32‘d15

// floor(arctan(2^(-12)) * 2^16)

`define arctan14 32’d7

// floor(arctan(2^(-13)) * 2^16)

`define arctan15 32‘d3

// floor(arctan(2^(-14)) * 2^16)

`define arctan16 32’d1

// floor(arctan(2^(-15)) * 2^16)

parameter

K

=

32

‘d39796

//K=floor(0。607253*2^16)

reg

signed

32

-

1

0

x_0

y_0

z_0

reg

signed

32

-

1

0

x_1

y_1

z_1

reg

signed

32

-

1

0

x_2

y_2

z_2

reg

signed

32

-

1

0

x_3

y_3

z_3

reg

signed

32

-

1

0

x_4

y_4

z_4

reg

signed

32

-

1

0

x_5

y_5

z_5

reg

signed

32

-

1

0

x_6

y_6

z_6

reg

signed

32

-

1

0

x_7

y_7

z_7

reg

signed

32

-

1

0

x_8

y_8

z_8

reg

signed

32

-

1

0

x_9

y_9

z_9

reg

signed

32

-

1

0

x_10

y_10

z_10

reg

signed

32

-

1

0

x_11

y_11

z_11

reg

signed

32

-

1

0

x_12

y_12

z_12

reg

signed

32

-

1

0

x_13

y_13

z_13

reg

signed

32

-

1

0

x_14

y_14

z_14

reg

signed

32

-

1

0

x_15

y_15

z_15

reg

signed

32

-

1

0

x_16

y_16

z_16

reg

17

-

1

0

cordic_en

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

cordic_en

<=

17

’d0

end

else

begin

cordic_en

<=

{

cordic_en

17

-

2

0

],

phase_vaild

};

end

end

reg

32

-

1

0

phase_r

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

phase_r

<=

32

‘d0

end

else

begin

phase_r

<=

phase

end

end

// 0次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_0

<=

32

’d0

y_0

<=

32

‘d0

z_0

<=

32

’d0

end

else

if

cordic_en

0

])

begin

x_0

<=

K

y_0

<=

32

‘d0

z_0

<=

phase_r

end

end

// 1次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_1

<=

32

’d0

y_1

<=

32

‘d0

z_1

<=

32

’d0

end

else

if

cordic_en

1

])

begin

x_1

<=

z_0

32

-

1

x_0

-

y_0

))

x_0

+

y_0

));

y_1

<=

z_0

32

-

1

y_0

+

x_0

))

y_0

-

x_0

));

z_1

<=

z_0

32

-

1

z_0

-

`arctan1

z_0

+

`arctan1

);

end

end

// 2次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_2

<=

32

‘d0

y_2

<=

32

’d0

z_2

<=

32

‘d0

end

else

if

cordic_en

2

])

begin

x_2

<=

z_1

32

-

1

x_1

-

y_1

>>

1

))

x_1

+

y_1

>>

1

));

y_2

<=

z_1

32

-

1

y_1

+

x_1

>>

1

))

y_1

-

x_1

>>

1

));

z_2

<=

z_1

32

-

1

z_1

-

`arctan2

z_1

+

`arctan2

);

end

end

// 3次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_3

<=

32

’d0

y_3

<=

32

‘d0

z_3

<=

32

’d0

end

else

if

cordic_en

3

])

begin

x_3

<=

z_2

32

-

1

x_2

-

y_2

>>

2

))

x_2

+

y_2

>>

2

));

y_3

<=

z_2

32

-

1

y_2

+

x_2

>>

2

))

y_2

-

x_2

>>

2

));

z_3

<=

z_2

32

-

1

z_2

-

`arctan3

z_2

+

`arctan3

);

end

end

// 4次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_4

<=

32

‘d0

y_4

<=

32

’d0

z_4

<=

32

‘d0

end

else

if

cordic_en

4

])

begin

x_4

<=

z_3

32

-

1

x_3

-

y_3

>>

3

))

x_3

+

y_3

>>

3

));

y_4

<=

z_3

32

-

1

y_3

+

x_3

>>

3

))

y_3

-

x_3

>>

3

));

z_4

<=

z_3

32

-

1

z_3

-

`arctan4

z_3

+

`arctan4

);

end

end

// 5次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_5

<=

32

’d0

y_5

<=

32

‘d0

z_5

<=

32

’d0

end

else

if

cordic_en

5

])

begin

x_5

<=

z_4

32

-

1

x_4

-

y_4

>>

4

))

x_4

+

y_4

>>

4

));

y_5

<=

z_4

32

-

1

y_4

+

x_4

>>

4

))

y_4

-

x_4

>>

4

));

z_5

<=

z_4

32

-

1

z_4

-

`arctan5

z_4

+

`arctan5

);

end

end

// 6次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_6

<=

32

‘d0

y_6

<=

32

’d0

z_6

<=

32

‘d0

end

else

if

cordic_en

6

])

begin

x_6

<=

z_5

32

-

1

x_5

-

y_5

>>

5

))

x_5

+

y_5

>>

5

));

y_6

<=

z_5

32

-

1

y_5

+

x_5

>>

5

))

y_5

-

x_5

>>

5

));

z_6

<=

z_5

32

-

1

z_5

-

`arctan6

z_5

+

`arctan6

);

end

end

// 7次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_7

<=

32

’d0

y_7

<=

32

‘d0

z_7

<=

32

’d0

end

else

if

cordic_en

7

])

begin

x_7

<=

z_6

32

-

1

x_6

-

y_6

>>

6

))

x_6

+

y_6

>>

6

));

y_7

<=

z_6

32

-

1

y_6

+

x_6

>>

6

))

y_6

-

x_6

>>

6

));

z_7

<=

z_6

32

-

1

z_6

-

`arctan7

z_6

+

`arctan7

);

end

end

// 8次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_8

<=

32

‘d0

y_8

<=

32

’d0

z_8

<=

32

‘d0

end

else

if

cordic_en

8

])

begin

x_8

<=

z_7

32

-

1

x_7

-

y_7

>>

7

))

x_7

+

y_7

>>

7

));

y_8

<=

z_7

32

-

1

y_7

+

x_7

>>

7

))

y_7

-

x_7

>>

7

));

z_8

<=

z_7

32

-

1

z_7

-

`arctan8

z_7

+

`arctan8

);

end

end

// 9次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_9

<=

32

’d0

y_9

<=

32

‘d0

z_9

<=

32

’d0

end

else

if

cordic_en

9

])

begin

x_9

<=

z_8

32

-

1

x_8

-

y_8

>>

8

))

x_8

+

y_8

>>

8

));

y_9

<=

z_8

32

-

1

y_8

+

x_8

>>

8

))

y_8

-

x_8

>>

8

));

z_9

<=

z_8

32

-

1

z_8

-

`arctan9

z_8

+

`arctan9

);

end

end

// 10次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_10

<=

32

‘d0

y_10

<=

32

’d0

z_10

<=

32

‘d0

end

else

if

cordic_en

10

])

begin

x_10

<=

z_9

32

-

1

x_9

-

y_9

>>

9

))

x_9

+

y_9

>>

9

));

y_10

<=

z_9

32

-

1

y_9

+

x_9

>>

9

))

y_9

-

x_9

>>

9

));

z_10

<=

z_9

32

-

1

z_9

-

`arctan10

z_9

+

`arctan10

);

end

end

// 11次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_11

<=

32

’d0

y_11

<=

32

‘d0

z_11

<=

32

’d0

end

else

if

cordic_en

11

])

begin

x_11

<=

z_10

32

-

1

x_10

-

y_10

>>

10

))

x_10

+

y_10

>>

10

));

y_11

<=

z_10

32

-

1

y_10

+

x_10

>>

10

))

y_10

-

x_10

>>

10

));

z_11

<=

z_10

32

-

1

z_10

-

`arctan11

z_10

+

`arctan11

);

end

end

// 12次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_12

<=

32

‘d0

y_12

<=

32

’d0

z_12

<=

32

‘d0

end

else

if

cordic_en

12

])

begin

x_12

<=

z_11

32

-

1

x_11

-

y_11

>>

11

))

x_11

+

y_11

>>

11

));

y_12

<=

z_11

32

-

1

y_11

+

x_11

>>

11

))

y_11

-

x_11

>>

11

));

z_12

<=

z_11

32

-

1

z_11

-

`arctan12

z_11

+

`arctan12

);

end

end

// 13次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_13

<=

32

’d0

y_13

<=

32

‘d0

z_13

<=

32

’d0

end

else

if

cordic_en

13

])

begin

x_13

<=

z_12

32

-

1

x_12

-

y_12

>>

12

))

x_12

+

y_12

>>

12

));

y_13

<=

z_12

32

-

1

y_12

+

x_12

>>

12

))

y_12

-

x_12

>>

12

));

z_13

<=

z_12

32

-

1

z_12

-

`arctan13

z_12

+

`arctan13

);

end

end

// 14次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_14

<=

32

‘d0

y_14

<=

32

’d0

z_14

<=

32

‘d0

end

else

if

cordic_en

14

])

begin

x_14

<=

z_13

32

-

1

x_13

-

y_13

>>

13

))

x_13

+

y_13

>>

13

));

y_14

<=

z_13

32

-

1

y_13

+

x_13

>>

13

))

y_13

-

x_13

>>

13

));

z_14

<=

z_13

32

-

1

z_13

-

`arctan14

z_13

+

`arctan14

);

end

end

// 15次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_15

<=

32

’d0

y_15

<=

32

‘d0

z_15

<=

32

’d0

end

else

if

cordic_en

15

])

begin

x_15

<=

z_14

32

-

1

x_14

-

y_14

>>

14

))

x_14

+

y_14

>>

14

));

y_15

<=

z_14

32

-

1

y_14

+

x_14

>>

14

))

y_14

-

x_14

>>

14

));

z_15

<=

z_14

32

-

1

z_14

-

`arctan15

z_14

+

`arctan15

);

end

end

// 16次迭代計算

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

x_16

<=

32

‘d0

y_16

<=

32

’d0

z_16

<=

32

‘d0

end

else

if

cordic_en

16

])

begin

x_16

<=

z_15

32

-

1

x_15

-

y_15

>>

15

))

x_15

+

y_15

>>

15

));

y_16

<=

z_15

32

-

1

y_15

+

x_15

>>

15

))

y_15

-

x_15

>>

15

));

z_16

<=

z_15

32

-

1

z_15

-

`arctan16

z_15

+

`arctan16

);

end

end

// 迭代結束

assign

cordic_cos_data

=

x_16

always

@(

posedge

clk

or

negedge

rst_n

begin

if

rst_n

begin

cordic_vaild

<=

0

end

else

begin

cordic_vaild

<=

cordic_en

16

])

1

0

end

end

endmodule

模擬結果如下所示:

BLE 學習筆記_1(CORDIC演算法)

當輸入相位’d51471和‘d34314的時候分別輸出的cos值為:’d46340和‘d56758。我們在到matlab中進行量化驗證一下:

%% FPGA test

Iterate

=

16

%迭代次數

x

=

zeros

Iterate

+

1

1

);

y

=

zeros

Iterate

+

1

1

);

z

=

zeros

Iterate

+

1

1

);

% x(1) = 0。607253;%初始設定

x

1

=

39796

%K=floor(0。607253*2^16)

% z(1) = 51471; % floor((pi/4)*2^16);%待求角度θ

z

1

=

34314

for

i

=

1

Iterate

if

z

i

>

=

0

d

=

1

else

d

=

-

1

end

x

i

+

1

=

x

i

-

d

*

y

i

*

2

^

-

i

-

1

)));

y

i

+

1

=

y

i

+

d

*

x

i

*

2

^

-

i

-

1

)));

z

i

+

1

=

z

i

-

d

*

Quat_atan

i

));

end

cosa

=

x

Iterate

+

1

real_cosa

=

cosa

/

2

^

16

結果如下:

BLE 學習筆記_1(CORDIC演算法)

和FPGA計算的數值基本一樣。

值得注意的是需要控制好輸入的象限,不然會出錯的。

參考文獻

[1]陳志強。 基於CORDIC演算法的BLE4。0發射機數字基帶的設計與實現[D]。東南大學,2016