1.系统设计要求
1)设计一个应用CORDIC算法求解正弦函数sinθ和余弦函数cosθ的VHDL程序。
2)CORDIC算法简介
在现代信号处理中,经常会遇到三角函数、超越函数和坐标转化等问题。传统的实现方法有查找表、多项式展开等方法。这些方法在精度、速度、简单性和效率方面往往不能兼顾,而CORDIC算法则可以很好地兼顾这几方面的要求。
CORDIC是坐标旋转数字计算机(Coordinate Rotations Digital Computer)的英文字头缩写,它于1959年由J.Volder提出,首先应用于导航系统,使得矢量的旋转和定向运算不需要做复杂运算。
CORDIC算法的基本思想是通过一系列固定的、与运算基数相关的角度偏摆来逼近所需的旋转角度。可通过该算法的圆周模式、双曲线模式、线性模式等不同的实现模式,来计算乘除、平方根、正弦、余弦、反正切向量旋转以及指数运算等。由于基本运算单元只有移位与加减法,这就为CORDIC算法的FPGA实现打下了良好的基础。
2.CORDIC算法实现结构
CORDIC算法的实现有两种结构方案:迭代结构和流水线结构。
基于迭代结构的CORDIC算法实现方案只需要一组移位及加减运算的单元,硬件开销很小,但控制比较复杂,而且完成一次CORDIC运算需要多个时钟周期,当对速度要求较高时很难满足要求。
基于流水线结构的实现方案,每级CORDIC迭代运算都使用单独的一套运算单元,与基于迭代结构的实现方案相比,流水线结构的处理速度非常快,当流水线填满之后每个时钟周期就会计算出一组结果。
本设计采用流水线结构进行设计:
外部输入XI、YI、ZI作为第一级流水线单元的输入X(0)、Y(0)、Z(0);
中间各个单元首尾相接,也就是第N个单元的输入与N-1个单元的输出X(N-1), Y(N-1), Z(N-1)相连,第N个单元的输出X(N), Y(N), Z(N)又与N+1个单元的输入相连;
最后一级处理单元的输出X(N)、Y(N)、Z(N)就是整个系统的输出XO、YO、ZO。
先设计一个根据已知的( Xn-1,Yn-1,Zn-)求解( Xn,Yn ,Zn )的通用流水单元电路程序CORDICPIPE.VHD,再利用N个单元电路构成流水线,实现由(X0,Y0 ,Z0 )求解( Xn,Yn ,Zn )的CORDIC算法实现电路程序CORDIC.VHD,最后给CORDIC算法实现电路赋予确定的初值(0.60725,0,角度 ),构建一个求解正弦和余弦函数的顶层电路程序
3 . 主要模块的设计思想与源程序设计
--CORDIC.VHDL
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
ENTITY CORDIC IS
GENERIC(PIPELINE:INTEGER:=15;
WIDTH:INTEGER:=16);
PORT(CLK:IN STD_LOGIC;
ENA:IN STD_LOGIC;
XI:IN SIGNED(WIDTH-1 DOWNTO 0);
YI:IN SIGNED(WIDTH-1 DOWNTO 0):=(OTHERS=>'0');
ZI:IN SIGNED(WIDTH-1 DOWNTO 0);
XO:OUT SIGNED(WIDTH-1 DOWNTO 0);
YO: OUT SIGNED(WIDTH-1 DOWNTO 0));
END ENTITY CORDIC;
ARCHITECTURE ART OF CORDIC IS
TYPE XYVECTOR IS ARRAY(PIPELINE DOWNTO 0)OF
SIGNED(WIDTH-1 DOWNTO 0);
TYPE ZVECTOR IS ARRAY(PIPELINE DOWNTO 0)OF
SIGNED(19 DOWNTO 0);
COMPONENT CORDICPIPE
GENERIC(WIDTH:NATURAL:=16;
PIPEID:NATURAL:=1);
PORT(CLK:IN STD_LOGIC;
ENA:IN STD_LOGIC;
XI:IN SIGNED(WIDTH-1 DOWNTO 0);
YI:IN SIGNED(WIDTH-1 DOWNTO 0);
ZI:IN SIGNED(19 DOWNTO 0);
XO: OUT SIGNED(WIDTH-1 DOWNTO 0);
YO:OUT SIGNED(WIDTH-1 DOWNTO 0);
ZO:OUT SIGNED(19 DOWNTO 0));
END COMPONENT CORDICPIPE;
SIGNAL X,Y:XYVECTOR;--定义模块间连接信号,用于16个流水线单元电路的级联
SIGNAL Z:ZVECTOR;
BEGIN
--FILL FIRST NODES
--FILLX
X(0)<=XI; --将外部输入xi赋值给第一级流水线单元的输入X(0)
--FILL Y
Y(0)<=YI;--将外部输入Yi赋值给第一级流水线单元的输入Y(0)
--FILLZ
Z(0)(19 DOWNTO 4)<=ZI;
--把外部输入赋值给第一级流水线单元的输入
Z(0)(3 DOWNTO 0)<=(OTHERS=>'0');
--GENERATE PIPELINE
GEN_PIPE: FOR N IN 1 TO PIPELINE GENERATE
PIPE:CORDICPIPE
GENERIC MAP(WIDTH=>WIDTH,PIPEID=>N-1)
PORT MAP(CLK,ENA,X(N-1),Y(N-1),Z(N-1),X(N),Y(N),Z(N));
END GENERATE GEN_PIPE;
--ASSIGN OUTPUTS
XO<=X(PIPELINE);
--复制了16个相同的单元电路,并且各个单元首尾相接,构成流水线结构
YO<=Y(PIPELINE);
END ARCHITECTURE ART;
--CORDICPIPE.VHD 流水线模块
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
ENTITY CORDICPIPE IS
GENERIC(WIDTH:NATURAL:= 16; --类属表
PIPEID:NATURAL:=1);
PORT(CLK:IN STD_LOGIC;
ENA:IN STD_LOGIC;
XI:IN SIGNED(WIDTH-1 DOWNTO 0);
YI:IN SIGNED(WIDTH-1 DOWNTO 0);
ZI:IN SIGNED(19 DOWNTO 0);
XO:OUT SIGNED(WIDTH-1 DOWNTO 0);
YO:OUT SIGNED(WIDTH-1 DOWNTO 0);
ZO:OUT SIGNED(19 DOWNTO 0));
END ENTITY CORDICPIPE;
ARCHITECTURE ART OF CORDICPIPE IS
--FUNCTION CATAN (CONSTANTE ARC-TANGENT).
--THIS IS A LOOKUP TABLE CONTAINING PRE-CALCULATED ARC-TANGENTS.
--N IS THE NUMBER OF THE PIPE,RETURNED IS A20BIT ARC-TANGENT VALUE.
--THE NUMBERS ARE CALCULATED AS FOLLOWS:Z(N)=ATAN(I/2^N)
--EXAMPLES:
--20BIT VALUES=>2~20=2PI(RAD)
--1(RAD)=2~20/2PI=166886.053...
--N:O,ATAN(I/1)=0.7853..(RAD)
--0.7853..*166886.053..=131072(DEC)=20000(HEX)
--N:1,ATAN(I/2)=0.4636.(RAD)
--0.4636..*166886.053..=77376.32(DEC)= 12E40(HEX)
--N:2,ATAN(1/4)=0.2449.(RAD)
--0.2449..*166886.053.=40883.52(DEC)= 9FB3(HEX)
--N:3,ATAN(1/8)=0.1243..(RAD)
--0.1243..*166886.053..=20753.1I(DEC)=5111(HEX)
FUNCTION CATAN(N:NATURAL) RETURN INTEGER IS
VARIABLE RESULT:INTEGER; --根据迭代步骤N的值求解该步需要旋转的角度,提高一个预计算的CASE查表
BEGIN
CASE N IS
WHEN 0=> RESULT:=16#020000#;
WHEN 1=> RESULT:=16#012E40#;
WHEN 2=> RESULT:=16#09FB4#;
WHEN 3=> RESULT:=16#05111#;
WHEN 4=> RESULT:=16#028B1#;
WHEN 5=> RESULT:=16#0145D#;
WHEN 6=> RESULT:=16#0A2F#;
WHEN 7=> RESULT:=16#0518#;
WHEN 8=> RESULT:=16#028C#;
WHEN 9=> RESULT:=16#0146#;
WHEN 10=> RESULT:=16#0A3#;
WHEN 11=> RESULT:=16#051#;
WHEN 12=> RESULT:=16#029#;
WHEN 13=> RESULT:=16#014#;
WHEN 14=> RESULT:=16#0A#;
WHEN 15=> RESULT:=16#05#;
WHEN 16=> RESULT:=16#03#;
WHEN 17=> RESULT:=16#01#;
WHEN OTHERS=>RESULT:=16#0#;
END CASE;
RETURN RESULT;
END CATAN;
--FUNCTION DELTA IS ACTUALLY AN ARITHMATIC SHIFT RIGHT
FUNCTION DELTA(ARG:SIGNED;CNT:NATURAL)RETURN SIGNED IS --根据输入ARG和CNT求解ARG/2^CNT
VARIABLE TMP:SIGNED(ARG'RANGE);
CONSTANT LO:INTEGER:=ARG'HIGH-CNT+1;
BEGIN
FOR N IN ARG'HIGH DOWNTO LO LOOP
TMP(N):=ARG(ARG'HIGH); --将右移后空余各位补符号位
END LOOP;
FOR N IN ARG'HIGH-CNT DOWNTO 0 LOOP
TMP(N):=ARG(N+CNT); --TMP的数据右移的CNT位
END LOOP;
RETURN TMP;
END FUNCTION DELTA;
FUNCTION ADDSUB(DATAA,DATAB:IN SIGNED;ADD_SUB:IN STD_LOGIC) --根据信号的不同将DATAA和DATAB做加法和减法
RETURN SIGNED IS
BEGIN
IF(ADD_SUB='1')THEN
RETURN DATAA+DATAB;
ELSE
RETURN DATAA-DATAB;
END IF;
END FUNCTION ADDSUB;
SIGNAL DX,XRESULT:SIGNED(WIDTH-1 DOWNTO 0);
SIGNAL DY,YRESULT:SIGNED(WIDTH-1 DOWNTO 0);
SIGNAL ATAN,ZRESULT:SIGNED(19 DOWNTO 0);
SIGNAL ZNEG,ZPOS:STD_LOGIC;
--开始真正的计算所需要的
BEGIN
DX<=DELTA(XI,PIPEID); --调用函数求出2^-nXN并将函数的返回值赋值给DX
DY<=DELTA(YI,PIPEID); --调用函数求出2^-nYN并将函数的返回值赋值给DY
ATAN<=CONV_SIGNED(CATAN(PIPEID),20); --实际就是调用函数求出弧度数并转换成20位有符号数
--GENERATE ADDER STRUCTURES
ZNEG<=ZI(19);
ZPOS<=NOT ZI(19); --将符号位给他,即是旋转的方向
--XADD
XRESULT<=ADDSUB(XI,DY,ZNEG); --求出Xn+1
--YADD
YRESULT<=ADDSUB(YI,DX,ZPOS);--求出Yn+1
--ZADD
ZRESULT<=ADDSUB(ZI,ATAN,ZNEG); --求出Zn+1
GEN_REGS:PROCESS(CLK)IS
BEGIN
IF(CLK'EVENT AND CLK='1')THEN
IF(ENA='1')THEN
XO<=XRESULT;
YO<=YRESULT;
ZO<=ZRESULT;
END IF;
END IF;
END PROCESS GEN_REGS;
END ARCHITECTURE ART;
--SINANDCOS.VHD
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
ENTITY SINANDCOS_CORDIC IS
PORT(CLK:IN STD_LOGIC;
ENA:IN STD_LOGIC;
AIN:IN SIGNED(15 DOWNTO 0);
SIN:OUT SIGNED(15 DOWNTO 0);
COS:OUT SIGNED(15 DOWNTO 0));
END ENTITY SINANDCOS_CORDIC;
ARCHITECTURE ART OF SINANDCOS_CORDIC IS
CONSTANT PIPELENGTH:NATURAL:=15;
CONSTANT P:SIGNED(15 DOWNTO 0):=X"4DBA";
COMPONENT CORDIC IS
GENERIC(PIPELINE:INTEGER:=15;
WIDTH:INTEGER:=16);
PORT(CLK:IN STD_LOGIC;
ENA:IN STD_LOGIC;
XI:IN SIGNED(WIDTH-1 DOWNTO 0);
YI:IN SIGNED(WIDTH-1 DOWNTO 0):=(OTHERS=>'0');
XO:OUT SIGNED(WIDTH-1 DOWNTO 0); ZI:IN SIGNED(WIDTH-1 DOWNTO 0);
YO:OUT SIGNED(WIDTH-1 DOWNTO 0));
END COMPONENT CORDIC;
BEGIN
U1:CORDIC
GENERIC MAP(PIPELINE=>PIPELENGTH,WIDTH=>16)
PORT MAP(CLK=>CLK,ENA=>ENA,XI=>P,
ZI=>AIN,XO=>COS,YO=>SIN);
END ARCHITECTURE ART;
--TOP.VHD
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY TOP IS
PORT(K1,K2,K3,K4,K5,K6,ENA,CLK,CLK1:IN STD_LOGIC;
COM:OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
SEG:OUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END ENTITY TOP;
ARCHITECTURE ART OF TOP IS
COMPONENT SINANDCOS_CORDIC IS
PORT(CLK:IN STD_LOGIC;
ENA:IN STD_LOGIC;
AIN:IN STD_LOGIC_VECTOR(15 DOWNTO 0);
SIN:OUT STD_LOGIC_VECTOR(15 DOWNTO 0);
COS:OUT STD_LOGIC_VECTOR(15 DOWNTO 0));
END COMPONENT SINANDCOS_CORDIC;
COMPONENT DISPLAY IS
PORT(SEL:IN STD_LOGIC_VECTOR(2 DOWNTO 0);
DATAIN:IN STD_LOGIC_VECTOR(15 DOWNTO 0);
COM: OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
SEG: OUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END COMPONENT DISPLAY;
COMPONENT CTRLS IS
PORT(CLK: IN STD_LOGIC;
SEL: OUT STD_LOGIC_VECTOR(2 DOWNTO 0));
END COMPONENT CTRLS;
COMPONENT XUAN IS
PORT(K1,K2:IN STD_LOGIC;
X0,Y0:IN STD_LOGIC_VECTOR(15 DOWNTO 0);
DOUT:OUT STD_LOGIC_VECTOR(15 DOWNTO 0));
END COMPONENT XUAN;
COMPONENT XUANIN IS
PORT(K1,K2,K3:IN STD_LOGIC;
DOUT:OUT STD_LOGIC_VECTOR(15 DOWNTO 0));
END COMPONENT XUANIN;
COMPONENT KG IS
PORT(A,B:IN STD_LOGIC_VECTOR(15 DOWNTO 0);
K:IN STD_LOGIC;
C:OUT STD_LOGIC_VECTOR(15 DOWNTO 0));
END COMPONENT KG;
SIGNAL S1,S3,S4,S5,S7:STD_LOGIC_VECTOR(15 DOWNTO 0);
SIGNAL S2:STD_LOGIC:='1';
SIGNAL S6:STD_LOGIC_VECTOR(2 DOWNTO 0);
BEGIN
U2:XUANIN PORT MAP(K1,K2,K3,S1);
U3:SINANDCOS_CORDIC PORT MAP(CLK,S2,AIN=>S1,SIN=>S3,COS=>S4);
U4:XUAN PORT MAP(K4,K5,S3,S4,S5);
U5:CTRLS PORT MAP(CLK1,S6);
U6:DISPLAY PORT MAP(S6,S7,COM,SEG);
U7:KG PORT MAP(S5,S1,K6,S7);
END ARCHITECTURE ART;
--XUANIN.VHD
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
ENTITY XUANIN IS
PORT(K1,K2,K3:IN STD_LOGIC;
DOUT:OUT STD_LOGIC_VECTOR(15 DOWNTO 0));
END ENTITY XUANIN;
ARCHITECTURE ART OF XUANIN IS
SIGNAL K:STD_LOGIC_VECTOR(2 DOWNTO 0);
BEGIN
PROCESS (K1,K2,K3) IS
BEGIN
K<=K1&K2&K3;
CASE K IS
WHEN "000"=>DOUT<="0000111000111000"; --0E38 20
WHEN "001"=>DOUT<="0000000000000000"; --0000 0
WHEN "010"=>DOUT<="0001010101010101"; --1555 30
WHEN "011"=>DOUT<="0010000000000000"; --2000 45
WHEN "100"=>DOUT<="0010101010101010"; --2AAA 60
WHEN "101"=>DOUT<="0011111111111111"; --3FFF 90
WHEN "110"=>DOUT<="0001110001110001"; --1C71 40
WHEN "111"=>DOUT<="0000011100011100"; ---071C 10
WHEN OTHERS=>DOUT<="0000000000000000";
END CASE;
END PROCESS;
END ARCHITECTURE ART;
--XUAN.VHD
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
ENTITY XUAN IS
PORT(K1,K2:IN STD_LOGIC;
X0,Y0:IN STD_LOGIC_VECTOR(15 DOWNTO 0);
DOUT:OUT STD_LOGIC_VECTOR(15 DOWNTO 0));
END ENTITY XUAN;
ARCHITECTURE ART OF XUAN IS
SIGNAL K:STD_LOGIC_VECTOR(1 DOWNTO 0);
BEGIN
PROCESS (K1,K2,X0,Y0) IS
BEGIN
K<=K1&K2;
CASE K IS
WHEN "01"=>DOUT<=X0;--cos的值
WHEN "10"=>DOUT<=Y0;
WHEN OTHERS=>DOUT<="0000000000000000";
END CASE;
END PROCESS;
END ARCHITECTURE ART;
--KG.VHD
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY KG IS
PORT(A,B:IN STD_LOGIC_VECTOR(15 DOWNTO 0);
K:IN STD_LOGIC;
C:OUT STD_LOGIC_VECTOR(15 DOWNTO 0));
END ENTITY KG;
ARCHITECTURE ART OF KG IS
BEGIN
PROCESS(A,B,K)IS
BEGIN
IF K='1' THEN
C<=A;
ELSE
C<=B;
END IF;
END PROCESS;
END ARCHITECTURE ART;
--DISPLAY.VHD
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY DISPLAY IS
PORT(SEL: IN STD_LOGIC_VECTOR(2 DOWNTO 0);
DATAIN: IN STD_LOGIC_VECTOR(15 DOWNTO 0);
COM: OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
SEG: OUT STD_LOGIC_VECTOR(7 DOWNTO 0));
END ENTITY DISPLAY;
ARCHITECTURE ART OF DISPLAY IS
SIGNAL DATA:STD_LOGIC_VECTOR(3 DOWNTO 0);
BEGIN
PROCESS(SEL) IS
BEGIN
CASE SEL IS
WHEN "000" =>COM<="11111110";
WHEN "001" =>COM<="11111101";
WHEN "010" =>COM<="11111011";
WHEN "011" =>COM<="11110111";
WHEN "100" =>COM<="11101111";
WHEN "101" =>COM<="11011111";
WHEN "110" =>COM<="10111111";
WHEN "111" =>COM<="01111111";
WHEN OTHERS =>COM<="11111111";
END CASE ;
END PROCESS;
PROCESS(SEL) IS
BEGIN
CASE SEL IS
WHEN "000" =>DATA<=DATAIN(3 DOWNTO 0);
WHEN "001" =>DATA<=DATAIN(7 DOWNTO 4);
WHEN "010" =>DATA<=DATAIN(11 DOWNTO 8);
WHEN "011" =>DATA<=DATAIN(15 DOWNTO 12);
WHEN OTHERS=>DATA<="0000";
END CASE;
CASE DATA IS
WHEN"0000"=>SEG<="00111111";
WHEN"0001"=>SEG<="00000110";
WHEN"0010"=>SEG<="01011011";
WHEN"0011"=>SEG<="01001111";
WHEN"0100"=>SEG<="01100110";
WHEN"0101"=>SEG<="01101101";
WHEN"0110"=>SEG<="01111101";
WHEN"0111"=>SEG<="00000111";
WHEN"1000"=>SEG<="01111111";
WHEN"1001"=>SEG<="01101111";
WHEN"1010"=>SEG<="01110111";
WHEN"1011"=>SEG<="01111100";
WHEN"1100"=>SEG<="00111001";
WHEN"1101"=>SEG<="01011110";
WHEN"1110"=>SEG<="01111001";
WHEN"1111"=>SEG<="01110001";
WHEN OTHERS=>SEG<="00000000";
END CASE ;
END PROCESS;
END ARCHITECTURE ART;
--CTRLS.VHD 动态控制信号产生模块
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY CTRLS IS
PORT(CLK: IN STD_LOGIC;
SEL: OUT STD_LOGIC_VECTOR(2 DOWNTO 0));
END ENTITY CTRLS;
ARCHITECTURE ART OF CTRLS IS
SIGNAL CNT: STD_LOGIC_VECTOR(2 DOWNTO 0);
BEGIN
PROCESS(CLK) IS
BEGIN
IF CLK'EVENT AND CLK='1' THEN
IF CNT="111" THEN
CNT<="000";
ELSE
CNT<=CNT+'1';
END IF ;
END IF;
END PROCESS;
SEL<=CNT;
END ARCHITECTURE ART;
4. 软硬件实验系统构建
整体RTL视图
SINANDCOS模块仿真
实物 COS45°量化值
正弦和余弦函数求解过程中有关参数及量化值对照表