Como dividir 50MHz para 2Hz em VHDL no Xilinx FPGA

13

Eu tenho uma placa FPGA Xilinx, com um cristal de 50 MHz. Eu preciso dividir isso em 2Hz em VHDL. Como eu faço isso?

ABAYOMI STEPHEN
fonte
13
Então, o que você realmente tentou?
Matt Young
Por que não usar o IP do gerenciador de clock Xilinx?
Arturs Vancans

Respostas:

19

Basicamente, existem duas maneiras de fazer isso. O primeiro é usar o núcleo do sintetizador de relógio nativo Xilinx. Uma das vantagens disso é que as ferramentas do Xlinx reconhecem o relógio como tal e o direcionam pelos caminhos necessários. As ferramentas também lidam com qualquer restrição de tempo (não é realmente aplicável neste caso, pois é um relógio de 2 Hz)

A segunda maneira é usar um contador para contar o número de pulsos de clock mais rápidos até que metade do seu período de relógio mais lento tenha passado. Por exemplo, no seu caso, o número de pulsos de clock rápidos que compõem um período de relógio de um ciclo lento é 50000000/2 = 25000000. Como queremos um período de meio relógio, isso é 25000000/2 = 12500000 para cada meio ciclo . (a duração de cada alta ou baixa).

Aqui está o que parece em VHDL:

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 scale_clock is
  port (
    clk_50Mhz : in  std_logic;
    rst       : in  std_logic;
    clk_2Hz   : out std_logic);
end scale_clock;

architecture Behavioral of scale_clock is

  signal prescaler : unsigned(23 downto 0);
  signal clk_2Hz_i : std_logic;
begin

  gen_clk : process (clk_50Mhz, rst)
  begin  -- process gen_clk
    if rst = '1' then
      clk_2Hz_i   <= '0';
      prescaler   <= (others => '0');
    elsif rising_edge(clk_50Mhz) then   -- rising clock edge
      if prescaler = X"BEBC20" then     -- 12 500 000 in hex
        prescaler   <= (others => '0');
        clk_2Hz_i   <= not clk_2Hz_i;
      else
        prescaler <= prescaler + "1";
      end if;
    end if;
  end process gen_clk;

clk_2Hz <= clk_2Hz_i;

end Behavioral;

Coisas a serem observadas:

  • O relógio gerado é zero durante a redefinição. Isso é bom para alguns aplicativos e não para outros, depende apenas do que você precisa para o relógio.
  • O relógio gerado será roteado como um sinal normal pelas ferramentas de síntese do Xilinx.
  • 2Hz é muito lento. Simular por um segundo vai demorar um pouco. É uma quantidade pequena de código, portanto, deve ser relativamente rápido simular até 1 segundo, mas se você começar a adicionar código, o tempo necessário para simular um ciclo de clock de 2 Hz pode ser significativamente longo.

EDIT: clk_2Hz_i é usado para armazenar em buffer o sinal de saída. O VHDL não gosta de usar um sinal à direita de uma atribuição quando também é uma saída.

Stanri
fonte
1
Nada mal, mas você pode adicionar / comparar sem assinatura com número inteiro, então: if prescaler = 50_000_000/4 then ...e prescaler <= prescaler + 1;seria um pouco mais simples.
Brian Drummond
@StaceyAnne Ao tentar isso, recebo "Não é possível ler do objeto 'out' clk_o; use 'buffer' ou 'inout'", perdi alguma coisa?
fugindo
@evading, é necessário um buffer na saída. O VHDL não gosta do fato de clk_2Hzser uma saída, mas seu valor está sendo lido nesta linha clk_2Hz <= not clk_2Hz;. Eu editei na correção.
stanri
+1 Ótimo exemplo. Mas aqui é onde minha ignorância mostra (novo no VHDL). Qual é a diferença entre prescaler <= (others => '0');e prescaler <= '0';?
Cbmeeks 28/01
NVM! Perdi totalmente o que othersera usado ao ler um livro de VHDL que tenho. É apenas um atalho para declarar todos os "outros" bits para um valor comum em vez de usar algo como "000000000000000000 ....", etc.
cbmeeks
9

Use um prescaler de relógio.

Seu valor de pré-calibrador será seu (velocidade do relógio / velocidade do relógio desejada) / 2, para (50Mhz (50.000.000) / 2hz (2)) / 2 = 12.500.000, que em binário seria 101111101011110000100000.

Mais simplesmente: (50.000.000) / 2) / 2 = 12.500.000 converter em binário -> 101111101011110000100000

Aqui está um código do que fazer: Use newClock para o que você precisar de 2 hz para ...

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity ClockPrescaler is
    port(
        clock   : in STD_LOGIC; -- 50 Mhz
        Led     : out STD_LOGIC
    );
end ClockPrescaler;

architecture Behavioral of ClockPrescaler is
    -- prescaler should be (clock_speed/desired_clock_speed)/2 because you want a rising edge every period
    signal prescaler: STD_LOGIC_VECTOR(23 downto 0) := "101111101011110000100000"; -- 12,500,000 in binary
    signal prescaler_counter: STD_LOGIC_VECTOR(23 downto 0) := (others => '0');
    signal newClock : std_logic := '0';
begin

    Led <= newClock;

    countClock: process(clock, newClock)
    begin
        if rising_edge(clock) then
            prescaler_counter <= prescaler_counter + 1;
            if(prescaler_counter > prescaler) then
                -- Iterate
                newClock <= not newClock;

                prescaler_counter <= (others => '0');
            end if;
        end if;
    end process;


end Behavioral;
MLM
fonte
Parece que você está gerando dois relógios, um de 0,5 Hz e um de 1 Hz? (como o período do seu relógio é o seu pré-calibrador * 2?). Além disso, o "+" dará um erro, já que você está adicionando slvs, e não tenho tanta certeza sobre o uso da propriedade overflow do add dessa maneira em qualquer caso. por que não apenas ir newClock : std_logic := '0', contar até prescaler / 2 e atribuir newClk <= not newClk?
stanri
Obrigado, minha lógica estava um pouco errada. Eu atualizei meu post inicial com algum código testado agora e algumas de suas sugestões :)
MLM
Ugh - todos esses e zeros e um comentário para dizer o que realmente é! Por que não usar o compilador para fazer isso por você ??? E por que não usar números inteiros de qualquer maneira?
Martin Thompson
Posso estar errado, mas acho que o uso de valores padrão ao definir sinais na arquitetura como em ": = (others => '0')" não é sintetizado.
Arturs Vancans
É sintetizável, mas basicamente funciona apenas em FPGAs baseados em SRAM, como a maioria dos Xilinx, Altera ou Lattice.
Yann Vernier
8

Na verdade, você normalmente não quer marcar nada tão lento, basta criar uma habilitação na taxa correta e usá-la na lógica:

 if rising_edge(50MHz_clk) and enable = '1' then

você pode criar o enable assim:

process 
   variable count : natural;
begin
   if rising_edge(50MHz_clk) then
       enable <= '0';
       count := count + 1;
       if count = clock_freq/desired_freq then
          enable <= '1';
          count := 0;
       end if;
    end if;
end process;

crie algumas constantes com a sua frequência de clock e a freqüência de ativação desejada e pronto, com código de auto-documentação para inicializar.

Martin Thompson
fonte
3

Prefiro sugerir o uso do IP do gerenciador de relógio digital Xilinx primitice .

Possui interface de configurações gráficas onde você pode especificar qual frequência deseja. Ele irá gerar um componente com a saída desejada como frequência.

Pode ser encontrado no Assistente de IP;

insira a descrição da imagem aqui

E então você poderá especificar qual frequência deseja: insira a descrição da imagem aqui

Arturs Vancans
fonte
0

Fator = frequência de sinal de entrada / frequência de pré-calibrador de saída.

CE = Ativar relógio. Deve ser um pulso amplo de um relógio (clk) ou alto, se não for usado.

Q = Sinal de saída de um pulso amplo com a freqüência desejada.

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity prescaler is

  generic (
    FACTOR : integer);

  port (
    clk : in  std_logic;
    rst : in  std_logic;
    CE  : in  std_logic;
    Q   : out std_logic);

end prescaler;

architecture for_prescaler of prescaler is
  signal counter_reg, counter_next : integer range 0 to FACTOR-1;
  signal Q_next: std_logic;
begin  -- for_prescaler

  process (clk, rst)
  begin  -- process
    if rst = '1' then                   -- asynchronous reset (active low)
      counter_reg <= 0;
    elsif clk'event and clk = '1' then  -- rising clock edge
      counter_reg <= counter_next;
    end if;
  end process;

  process (counter_reg, CE)
  begin  -- process
    Q_next <= '0';
     counter_next <= counter_reg;
     if CE = '1' then
        if counter_reg = FACTOR-1  then
          counter_next <= 0;
           Q_next <= '1';
         else
           counter_next <= counter_reg + 1;
        end if;
      end if;
  end process;

  process (clk, rst)
  begin  -- process
    if rst = '1' then                   -- asynchronous reset (active low)
      Q <= '0';
    elsif clk'event and clk = '1' then  -- rising clock edge
      Q <= Q_next;
    end if;
  end process; 

end for_prescaler;
Roger Garzon Nieto
fonte