賽靈思官方提供了cordic(coordinate rotational digital computer) ip核實現直角坐標極坐標變化,三角函數的操作。我介紹下它進行反正切求解的使用:
新建個簡單工程:bd如下
進行ip設置,選擇運算位反正切后,ip端口回自動變為上圖,再引出2個總線和時鍾,xilinx的ip核不少是基於AXI4-Stream總線,這里使用並不復雜,默認只有2個信號,一個數據線tdata,一個握手信號tvalid,tvalid拉高時數據信號有效。
確定了輸入輸出位寬后,系統會自動求解出需要的延時latency。這里說一句,AXI-Stream總線收發基於byte的,無論輸入什么位寬最后都是8的整數倍。所以需要參照UG,放置好有效輸入位和分解輸出位。
如果輸入輸出位寬恰好不是8的倍數,那么tdata位寬是大於設定的值的。所以需要上圖提到進行填充(PAD),比如我輸入20位,x和y加起來40位,那么輸入S_AXIS的位寬是48,我要在0-19放x,20-23放填充數據(隨意,我用0),24-43放y,44-47放填充數據。必須保證0-19和24-25分別是我的x和y。
atan=y/x,要求輸入必須是[-1,1],所以如果數據不是這個區間還需要進行歸一化處理(可以用除法器div_gen ip核,這里不做介紹了)。上圖是一個簡單的示例,介紹輸入輸出的定點小數格式。整數部分的第一位是符號位,小數部分沒有符號位是正的。※Q格式:小數點位於第 n 位元之右側,稱為Qn 格式。
這里關於二進制小數表示,可以看下這個https://blog.csdn.net/AaricYang/article/details/87882868。整數的每一位權重是2^(n-1) 111就是2^2+2^1+2^0=7,小數部分的權重是2^(-n) 0.111就是2^(-1)+2^(-2)+2^(-3)=0.875
這里我覺得還有一個細節問題。如果輸入的數值x或y是負數小數,比如1100,常規默認符號位第一位,第二位開始就是小數部分了,所以1010就是1.010= -1+0.25= -0.75;這種符號位后一位就是小數部分的第一位的格式就是vivado 除法器ip輸出小數的格式。也可以說這種是0Qn format,即符號位后0位就是小數。但是cordic ip輸入要求 1Qn format,符號位結束后1位才是小數格式(為了兼容恰好等於±1的情況),那么剛剛1010就不可以直接作為cordic ip核的輸入,1010按照1Qn format,第3位才是小數部分,1010就是10.10= -1.5 因為整數部分是10,反碼01,補碼10為2,因為是負數所以-2,小數部分是0.5,最終結果是-2+0.5=1.5。這樣就不符合cordic ip核輸入要求了。從0Qn改為1Qn為了滿足,如果是絕對值小於1的數只要在符號位后重復符號位就可以了,1 010改為11 010,這樣就可以正常輸入到cordic中了。
`timescale 1 ns / 1 ps module cordic_tb_top; reg aclk; wire [31:0]M_AXIS_DOUT_0_tdata; wire M_AXIS_DOUT_0_tvalid; reg [47:0]S_AXIS_CARTESIAN_0_tdata; reg S_AXIS_CARTESIAN_0_tvalid; integer handle; initial begin aclk=0; S_AXIS_CARTESIAN_0_tvalid=1'b0; S_AXIS_CARTESIAN_0_tdata='b0; end initial handle = $fopen("D:/FPGAcode/_file/cordic.txt");//打開文件 always #10 aclk=~aclk; localparam PAD=(48-20*2)/2; always@(posedge aclk) begin S_AXIS_CARTESIAN_0_tvalid<=1'b1; S_AXIS_CARTESIAN_0_tdata<={{PAD{1'b0}},{3'b000,17'b0},{PAD{1'b0}},{3'b111,17'b0}}; end always@(posedge aclk) begin if(M_AXIS_DOUT_0_tvalid) $fdisplay(handle,"%b",M_AXIS_DOUT_0_tdata);//寫數據 end design_1_wrapper design_1_i (.M_AXIS_DOUT_0_tdata(M_AXIS_DOUT_0_tdata), .M_AXIS_DOUT_0_tvalid(M_AXIS_DOUT_0_tvalid), .S_AXIS_CARTESIAN_0_tdata(S_AXIS_CARTESIAN_0_tdata), .S_AXIS_CARTESIAN_0_tvalid(S_AXIS_CARTESIAN_0_tvalid), .aclk_0(aclk)); endmodule
給個簡單的testbench
在modelsim中可以設置小數位數,顯示出具體的數值。
如果自己參考文獻,用HDL實現一個CORDIC算法的模塊,應該是很不錯了。但是我偷懶直接用現成IP了