Данная статья продолжение серии топиков Элемент задержки на VHDL, Элемент задержки на VHDL. Другой взгляд о элементах задержки на VHDL реализованных в ПЛИС.

Акцент будет сделан на конкретный прикладной пример, который любой желающий может запустить в симуляторе или реальном железе. Пример создан для удобной симуляции в среде Xilinx ISE с использованием Modelsim SE и с минимальными изменениями реализован в полноценное IP Core.

Постановка задачи


Осуществить сдвиг фазы импульсного сигнала на заданную величину (длительность импульса произвольна), возможно не синхронного с частотой работы логики ядра. Сделать это без перезагрузки или выключения модуля/устройства.

Инструменты


ДИП свич 8 позиций, на котором выставляется код задержки в двоичном коде (величина сдвига). Hard или Soft Reset — начальных сброс, установка параметров по умолчанию. Опорная частота 100 MHz, т.е 10 ns минимальное время смещения.

Реализация


Импульсом буду называть логическую единицу — 1.
Паузой, логический ноль — 0.

Код реализован в виде машины состояний, которая на мой взгляд благодаря пошаговой структуре и возможности дать внятное имя каждому этапу, весьма проста и понятна.

Помимо комментариев к коду, прилагается файл симуляции testbench.

Диаграмма конечного автомата:

image

Основная логика. Код отслеживает изменение уровня сигнала, далее запускается счетчик, когда его значение становится равным выставленному сдвигу, на выход подается тот же уровень, что и отслеживается и так по кругу.

freq_shift_half_cycle.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_arith.all;

use ieee.std_logic_unsigned.all;

entity freq_shift_half_cycle is
    Port ( 
			  Bus2IP_Clk          : in  STD_LOGIC;                     -- частота работы логики
           Bus2IP_Reset        : in  STD_LOGIC;                     -- сброс
           Clk_in              : in  STD_LOGIC;                     -- входной сигнал
           Shift_reg           : in  STD_LOGIC_VECTOR (7 downto 0); -- знчение задержки в тактах Bus2IP_Clk
           counter_reg_test    : out STD_LOGIC_VECTOR (7 downto 0); -- тестовый счетчик
           Clk_out             : out STD_LOGIC                      -- выходной сигнал
			  );
end freq_shift_half_cycle;

architecture Behavioral of freq_shift_half_cycle is

type state_type is (set_level, wait_high_low, wait_low_high); -- описание машины состояний
signal current_stage  : state_type;                            
signal counter_shift  : STD_LOGIC_VECTOR (7 downto 0); -- внутренний счетчик

begin

shift_fsm : process (Bus2IP_Reset, Bus2IP_Clk, Clk_in, Shift_reg)
begin
	if Shift_reg = x"00" or Bus2IP_Reset = '1' then    -- если задержка нулевая или подан reset
		Clk_out       <= Clk_in;
		counter_shift <= x"01";
		counter_reg_test <= x"01";                      -- тестовый счетчик
		current_stage <= set_level;  
	elsif (Bus2IP_Clk'event and Bus2IP_Clk = '1') then
		case current_stage is
			when set_level =>  
				if counter_shift = Shift_reg   then        -- после выставленной задержки, подаём на выход 0 или 1
					if Clk_in = '1' then
						Clk_out       <= '1';
						current_stage <= wait_high_low; 
					else
						Clk_out       <= '0';
						current_stage <= wait_low_high; 
					end if;
					counter_shift    <= x"01";
					counter_reg_test <= x"01";              -- тестовый счетчик
				elsif counter_shift < Shift_reg   then
					counter_shift    <= counter_shift + 1;
					counter_reg_test <= counter_shift + 1;  -- тестовый счетчик
					current_stage    <= set_level;
				end if;
			when wait_high_low =>                   -- ждем переключения 1 на 0 и возвращаемся в set_level
				if Clk_in = '1' then
					current_stage <= wait_high_low;
				else	
					current_stage <= set_level; 
				end if;
			when wait_low_high =>                   -- ждем переключения 0 на 1 и возвращаемся в set_level
				if Clk_in = '0' then
					current_stage <= wait_low_high;
				else	
					current_stage <= set_level; 
				end if;
			when others => 
				current_stage    <= set_level;
		end case;
	end if;
end process shift_fsm;


end Behavioral;


Код симуляции для Modelsim:

testbench_half_cycle.vhd
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
 
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--USE ieee.numeric_std.ALL;
 
ENTITY testbench_half_cycle IS
END testbench_half_cycle;
 
ARCHITECTURE behavior OF testbench_half_cycle IS 
 
    -- Component Declaration for the Unit Under Test (UUT)
 
    COMPONENT freq_shift_half_cycle
    PORT(
         Bus2IP_Clk : IN  std_logic;
         Bus2IP_Reset : IN  std_logic;
         Clk_in : IN  std_logic;
         Shift_reg : IN  std_logic_vector(7 downto 0);
         counter_reg_test : OUT  std_logic_vector(7 downto 0);
         Clk_out : OUT  std_logic
        );
    END COMPONENT;
    

   --Inputs
   signal Bus2IP_Clk : std_logic := '0';
   signal Bus2IP_Reset : std_logic := '0';
   signal Clk_in : std_logic := '0';
   signal Shift_reg : std_logic_vector(7 downto 0) := (others => '0');

 	--Outputs
   signal counter_reg_test : std_logic_vector(7 downto 0);
   signal Clk_out : std_logic;

   -- Clock period definitions
   constant Bus2IP_Clk_period : time := 10 ns;
   constant Clk_in_period : time := 100 ns;

 
BEGIN
 
	-- Instantiate the Unit Under Test (UUT)
   uut: freq_shift_half_cycle PORT MAP (
          Bus2IP_Clk => Bus2IP_Clk,
          Bus2IP_Reset => Bus2IP_Reset,
          Clk_in => Clk_in,
          Shift_reg => Shift_reg,
          counter_reg_test => counter_reg_test,
          Clk_out => Clk_out
        );

   -- Clock process definitions
   Bus2IP_Clk_process :process
   begin
	Bus2IP_Clk <= '1';
	wait for Bus2IP_Clk_period/2;
	Bus2IP_Clk <= '0';
	wait for Bus2IP_Clk_period/2;
   end process;
 
   Clk_in_process :process
   begin
	Clk_in <= '1';
	 wait for Clk_in_period/2;
	 Clk_in <= '0';
	 wait for Clk_in_period/2;
        --  wait for 1000 ns;	
  end process;

   -- Stimulus process
   stim_proc: process
   begin		
      -- hold reset state for 100 ns.
		Bus2IP_Reset <= '1';
                wait for 500 ns;	
		Bus2IP_Reset <= '0';
		wait for 5000 ns;	
		Shift_reg <= x"01";   -- выставляется задержка
		wait for 5000 ns;	
		Shift_reg <= x"00"; 
		wait for 5000 ns;	
		Shift_reg <= x"04"; 
      wait for Bus2IP_Clk_period*10;

      -- insert stimulus here 

      wait;
   end process;

END;


Опытный электронщик мог заметить недостатки данного кода, а именно. Выставленная задержка не должна превышать:

— длительности импульса, если длительность импульса меньше длительности паузы;
— длительности паузы, если длительность паузы меньше длительности импульса.

Т.е. величина фазового сдвига не должна превышать 180° как для 0 так и для 1 в случае импульсного сигнала.

На схеме ниже вы можете видеть, как осуществляется сдвиг фазы входного сигнала на 40 ns в реальном так сказать времени, с задержкой в работе логики:



Далее идет демонстрация ситуации если подстраиваемый сигнал и опорная частота асинхронны:



Предлагаю вам, проанализировать данную ситуацию и сделать собственные выводы.

Буду рад вашим комментариям и замечаниям, с помощью которых в следующей статье, этот код будет дополнен новыми функциональными возможностями.

Спасибо за внимание.

Комментарии (5)


  1. nerudo
    23.11.2015 18:20
    +1

    Простая двухпортовая память, счетчик на шину адреса записи, сумматор — входы подключаем на счетчик и дип-свичи, выход — к шине адреса чтения.
    А в вашем описании я бы рекомендовал добавить минимальную задержку в 1 такт, убрав назначение на clk_out под асинхронными условиями сброса. Во избежании разных незапланированных эффектов.


    1. UA3MQJ
      23.11.2015 22:54

      Суть ясна из одного предложения. Отличная идея.


    1. lazifo
      24.11.2015 07:55

      Попробую.


  1. NikitosZs
    23.11.2015 21:00
    +1

    Тут нельзя разве обойтись D-триггерами последовательно включёнными?


    1. lazifo
      24.11.2015 07:57

      Не понял вопроса, этот код компилятор синтезирует… как раз таки в «железное соединение» логических элементов. (картинка выбрана случайно)
      image