The following logic performs an inverse park transformation and converts dq-components into alpha-beta-components. It uses the cordic implementation of Mitu Raj that supports angles between -360° and 360°. The implementation can be found in another snippet on this website.
VHDL
-- Inverse Park Transformation (dq -> alpha/beta)
-- Christian Noeding, christian@noeding-online.de
-- https://chrisdevblog.com | https://github.com/xn--nding-jua
--
-- Released under GNU General Public License v3
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity dq_to_alpha_beta is
generic (
Vdc : natural := 12
);
port(
clk : in std_logic;
d : in signed(31 downto 0); -- Q15.16
q : in signed(31 downto 0); -- Q15.16
theta : in signed(31 downto 0); -- Q15.16 | values between 0 and 2*pi
sync_in : in std_logic;
alpha : out signed(31 downto 0); -- Q15.16
beta : out signed(31 downto 0); -- Q15.16
sync_out : out std_logic
);
end dq_to_alpha_beta;
architecture behavioural of dq_to_alpha_beta is
signal state : natural range 0 to 11 := 0;
signal reset_cordic : std_logic := '0';
signal start_cordic : std_logic := '0';
signal cordic_mini_done : std_logic;
signal sine, cosine : signed(15 downto 0) := (others => '0'); -- type: Q0.15
signal alpha_int, beta_int : signed(31 downto 0) := (others => '0'); -- type: Q15.16
signal theta_int : signed(15 downto 0) := (others => '0'); -- type: Q0.15
--signals for multiplier
signal mult_in_a : signed(31 downto 0) := (others=>'0');
signal mult_in_b : signed(31 downto 0) := (others=>'0');
signal mult_out : signed(63 downto 0) := (others=>'0');
component cordic_mini is
generic(
XY_WIDTH : integer := 16; -- OUTPUT WIDTH
ANGLE_WIDTH : integer := 16; -- ANGLE WIDTH
STAGE : integer := 14 -- NUMBER OF ITERATIONS
);
port(
clock : in std_logic; -- CLOCK INPUT
angle : in signed (ANGLE_WIDTH-1 downto 0); -- ANGLE INPUT from -360 to 360
load : in std_logic; -- LOAD SIGNAL TO ENABLE THE CORE
reset : in std_logic; -- ASYNC ACTIVE-HIGH RESET
done : out std_logic; -- STATUS SIGNAL TO SHOW WHETHER COMPUTATION IS FINISHED
Xout : out signed (XY_WIDTH-1 downto 0); -- COSINE OUTPUT
Yout : out signed (XY_WIDTH-1 downto 0) -- SINE OUTPUT
);
end component;
begin
-- multiplier
process(mult_in_a, mult_in_b)
begin
mult_out <= mult_in_a * mult_in_b;
end process;
process(clk)
begin
if rising_edge(clk) then
if (sync_in = '1' and state = 0) then
-- rescale theta from 0..2*pi to 0...+1 as cordic implementation accepts values between -1 to +1 and interpretes as -360 to +360
-- convert Q15.16 to Qx.15 and calculate (theta / (2 * pi))
mult_in_a <= shift_right(theta, 1); -- convert to Qx.15
mult_in_b <= to_signed(5215, 32); -- 1/(2*pi) as Qx.15
state <= 1; -- start of state-machine
elsif (state = 1) then
theta_int <= resize(shift_right(mult_out, 15), 16);
reset_cordic <= '1';
start_cordic <= '0';
state <= state + 1;
elsif (state = 2) then
-- stop resetting
reset_cordic <= '0';
state <= state + 1;
elsif (state = 3) then
-- start mini-cordic. As it takes 16 clocks to calculate we have to wait until it is ready
start_cordic <= '1';
state <= state + 1;
elsif (state = 4 and cordic_mini_done = '1') then
-- alpha = (d * cossin(theta)) - (q * sin(theta))
-- beta = (d * sin(theta)) + (q * cossin(theta))
mult_in_a <= d;
mult_in_b <= shift_left(resize(cosine, 32), 1); -- convert Q0.15 to Q15.16
state <= state + 1;
elsif (state = 5) then
alpha_int <= resize(shift_right(mult_out, 16), 32); -- Q15.16 * Q15.16 -> Qx.32 -> convert back to Q15.16
mult_in_a <= d;
mult_in_b <= shift_left(resize(sine, 32), 1); -- convert Q0.15 to Q15.16
state <= state + 1;
elsif (state = 6) then
beta_int <= resize(shift_right(mult_out, 16), 32); -- Q15.16 * Q15.16 -> Qx.32 -> convert back to Q15.16
mult_in_a <= q;
mult_in_b <= shift_left(resize(sine, 32), 1); -- convert Q0.15 to Q15.16
state <= state + 1;
elsif (state = 7) then
alpha_int <= alpha_int - resize(shift_right(mult_out, 16), 32); -- Q15.16 * Q15.16 -> Qx.32 -> convert back to Q15.16
mult_in_a <= q;
mult_in_b <= shift_left(resize(cosine, 32), 1); -- convert Q0.15 to Q15.16
state <= state + 1;
elsif (state = 8) then
beta_int <= beta_int + resize(shift_right(mult_out, 16), 32); -- Q15.16 * Q15.16 -> Qx.32 -> convert back to Q15.16
-- now rescale to PWM-values
mult_in_a <= alpha_int;
mult_in_b <= to_signed((2**16)/Vdc, 32); -- divide by Vdc
state <= state + 1;
elsif (state = 9) then
alpha_int <= resize(shift_right(mult_out, 16), 32);
mult_in_a <= beta_int;
mult_in_b <= to_signed((2**16)/Vdc, 32); -- divide by Vdc
state <= state + 1;
elsif (state = 10) then
-- set outputs
alpha <= alpha_int;
beta <= resize(shift_right(mult_out, 16), 32);
sync_out <= '1';
state <= state + 1;
elsif (state = 11) then
sync_out <= '0';
state <= 0;
end if;
end if;
end process;
cordic_sine_cos2 : cordic_mini
port map (
clock => clk, -- CLOCK INPUT
angle => theta_int, -- ANGLE INPUT from -360 to 360 as Q0.15
load => start_cordic, -- LOAD SIGNAL TO ENABLE THE CORE
reset => reset_cordic, -- ASYNC ACTIVE-HIGH RESET
done => cordic_mini_done, -- STATUS SIGNAL TO SHOW WHETHER COMPUTATION IS FINISHED
Xout => cosine, -- COSINE OUTPUT as Q0.15
Yout => sine -- SINE OUTPUT as Q0.15
);
end behavioural;
