This design is a pure VHDL quadrature signal generator.
With variable frequency. The frequency can be controlled with a scroll wheel.
The purpose of this design is to build an alternative local oscilator for Shabaz' Software Defined Radio (SDR) Experiment Board.
I tried to make everything as standard as possible, so that it can be used on many FPGA makes and sizes.
I'm working on a Xilinx Spartan 6, with a 12 MHz clock (Xess XuLA2 kit).
The design has 4 inputs
- rotary encoder pins A and B
- clock input
- reset
and 5 outputs:
- quadrature signals 0 -> 3
- the variable clock based on the input clock and the encoder setting
The design is broken up in 3 components:
- decoder to translate the movements of a scroll wheel into a numeric value.
- variable clock that will stretch the input clock based on the value coming out of the decoder
- quadrature signal generator will create 4 90° phase-shifted signals from that variable clock.
A wrapper glues the components together.
All this has been used before for my Pynq blog series and this Spartan 6 article: XuLA2 FPGA - Rotary Encoder and VHDL.
Because all the code has been documented before, I'm just putting the source here.
Check the link earlier in this post for explanations.
quad_decode_quad_osc.vhd
This code should be portable.
The external pins are defined in wrapper entity quad_decode_quad_osc.
-- ==============================================
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity quadrature_decoder is
Port ( QuadA : in STD_LOGIC;
QuadB : in STD_LOGIC;
Clk : in STD_LOGIC;
nReset : in STD_LOGIC;
Position : out unsigned (7 downto 0));
end quadrature_decoder;
architecture Behavioral of quadrature_decoder is
signal QuadA_Delayed: std_logic_vector(2 downto 0) := (others=>'0');
signal QuadB_Delayed: std_logic_vector(2 downto 0) := (others=>'0');
signal Count_Enable: STD_LOGIC;
signal Count_Direction: STD_LOGIC;
signal Count: unsigned(Position'length-1 downto 0) := (others=>'0');
begin
process (Clk, nReset)
begin
if (nReset = '0') then
Count <= (others=>'0');
QuadA_Delayed <= (others=>'0');
QuadB_Delayed <= (others=>'0');
elsif rising_edge(Clk) then
QuadA_Delayed <= (QuadA_Delayed(1), QuadA_Delayed(0), QuadA);
QuadB_Delayed <= (QuadB_Delayed(1), QuadB_Delayed(0), QuadB);
if Count_Enable='1' then
if Count_Direction='1' then
Count <= Count + 1;
Position <= Count;
else
Count <= Count - 1;
Position <= Count;
end if;
end if;
end if;
end process;
Count_Enable <= QuadA_Delayed(1) xor QuadA_Delayed(2) xor QuadB_Delayed(1)
xor QuadB_Delayed(2);
Count_Direction <= QuadA_Delayed(1) xor QuadB_Delayed(2);
end Behavioral;
-- ==============================================
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity variable_clock is
Port (
clk_i : in STD_LOGIC;
resetn_i : in STD_LOGIC;
ticks_i : in unsigned (7 downto 0);
clk_o : out STD_LOGIC
);
end variable_clock;
architecture Behavioral of variable_clock is
signal counter_s: natural range 0 to 2**ticks_i'length-1 := 0;
signal clk_out_s: STD_LOGIC := '0';
begin
process (clk_i, resetn_i)
procedure perform_reset is -- this procedure contains the reset action
begin
counter_s <= 0;
clk_out_s <= '0';
end procedure;
procedure perform_action is -- this procedure contains the reset action
begin
if counter_s >= ticks_i then -- >= because the value can change
counter_s <= 0;
clk_out_s <= not clk_out_s;
else
counter_s <= counter_s + 1;
end if;
end procedure;
begin
if rising_edge(clk_i) then
if resetn_i = '0' then
perform_reset;
else
perform_action;
end if;
clk_o <= clk_out_s;
end if;
end process;
end Behavioral;
-- ==============================================
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity quadrature_oscillator is
Port (
clk_i : in std_logic;
resetn_i : in std_logic;
outputs_o : out std_logic_vector (3 downto 0)
);
end quadrature_oscillator;
architecture Behavioral of quadrature_oscillator is
signal pattern_s: std_logic_vector (6 downto 0) := "0011001";
signal counter_s: natural range 0 to 3 := 0;
begin
process (clk_i, resetn_i)
procedure perform_reset is -- this procedure contains the reset action
begin
counter_s <= 0;
end procedure;
procedure perform_action is -- this procedure contains the reset action
begin
if counter_s = 3 then
counter_s <= 0;
else
counter_s <= counter_s + 1;
end if;
end procedure;
begin
if rising_edge(clk_i) then
if resetn_i = '0' then
perform_reset;
else
perform_action;
end if;
outputs_o(0) <= pattern_s(counter_s + 3);
outputs_o(1) <= pattern_s(counter_s + 2);
outputs_o(2) <= pattern_s(counter_s + 1);
outputs_o(3) <= pattern_s(counter_s + 0);
end if;
end process;
end Behavioral;
-- ==============================================
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity quad_decode_quad_osc is
Port ( QuadA_in : in STD_LOGIC;
QuadB_in : in STD_LOGIC;
clk_in : in STD_LOGIC;
nReset_in : in STD_LOGIC;
oscillator_quad_out : out STD_LOGIC_VECTOR (3 downto 0);
clk_out : out STD_LOGIC);
end quad_decode_quad_osc;
architecture Behavioral of quad_decode_quad_osc is
signal position_s : unsigned (7 downto 0);
signal clk_s : STD_LOGIC;
component quadrature_decoder is
Port ( QuadA : in STD_LOGIC;
QuadB : in STD_LOGIC;
Clk : in STD_LOGIC;
nReset : in STD_LOGIC;
Position : out unsigned (7 downto 0));
end component quadrature_decoder;
component variable_clock is
Port (
clk_i : in STD_LOGIC;
resetn_i : in STD_LOGIC;
ticks_i : in unsigned (7 downto 0);
clk_o : out STD_LOGIC
);
end component variable_clock;
component quadrature_oscillator is
Port (
clk_i : in std_logic;
resetn_i : in std_logic;
outputs_o : out std_logic_vector (3 downto 0)
);
end component quadrature_oscillator;
begin
quadrature_decoder_i: component quadrature_decoder
port map (
QuadA => QuadA_in,
QuadB => QuadB_in,
Clk => clk_in,
nReset => nReset_in,
Position (7 downto 0) => position_s (7 downto 0)
);
variable_clock_i: component variable_clock
port map (
clk_i => clk_in,
resetn_i => nReset_in,
ticks_i (7 downto 0) => position_s (7 downto 0),
clk_o => clk_s
);
quadrature_oscillator_i: component quadrature_oscillator
port map (
clk_i => clk_s,
resetn_i => nReset_in,
outputs_o (3 downto 0) => oscillator_quad_out (3 downto 0)
);
clk_out <= clk_s;
end Behavioral;
constraints.ucf
The external pins defined in quad_decode_quad_osc are mapped here.
I've mapped them for my specific board. Adapt for your FPGA/kit.
net clk_in loc=a9; # 12 MHz clock to FPGA. net nReset_in loc=k15; # PM1 connections for rotary encoder module. net QuadA_in loc=r15; net QuadB_in loc=m15; # PM1 connections for the pwm outputs net oscillator_quad_out[0] loc=r7; net oscillator_quad_out[1] loc=r16; net oscillator_quad_out[2] loc=m16; net oscillator_quad_out[3] loc=k16; net clk_out loc=t7;
Untested. I'll update if there are errors.
If used for Shabaz' SDR, pins [0]and [2] should go to the JFETS for one opamp. Pins [1] and [3] to the JFETS for the other. Because they are the 2 differential pairs.





-
dougw
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
-
Jan Cumps
in reply to dougw
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
-
dougw
in reply to Jan Cumps
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
-
Jan Cumps
in reply to dougw
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
-
Jan Cumps
in reply to Jan Cumps
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
Comment-
Jan Cumps
in reply to Jan Cumps
-
Cancel
-
Vote Up
0
Vote Down
-
-
Sign in to reply
-
More
-
Cancel
Children