Cách chia 50 MHz xuống còn 2Hz trong VHDL trên Xilinx FPGA


13

Tôi có một bo mạch Xilinx, với tinh thể 50 MHz. Tôi cần chia nó xuống 2Hz trong VHDL. Làm thế nào để tôi làm điều này?


13
Vì vậy, những gì bạn đã thực sự cố gắng?
Matt Young

Tại sao không sử dụng IP quản lý đồng hồ Xilinx?
Arturs Wrapsans 20/03/13

Câu trả lời:


19

Về cơ bản, có hai cách để làm điều này. Đầu tiên là sử dụng lõi tổng hợp đồng hồ gốc Xilinx. Một trong những lợi thế của điều này là các công cụ Xlinx sẽ nhận ra đồng hồ như vậy và định tuyến nó thông qua các con đường cần thiết. Các công cụ cũng sẽ xử lý mọi ràng buộc về thời gian (không thực sự áp dụng trong trường hợp này, vì đó là đồng hồ 2Hz)

Cách thứ hai là sử dụng bộ đếm để đếm số xung đồng hồ nhanh hơn cho đến khi một nửa thời gian đồng hồ chậm hơn của bạn trôi qua. Ví dụ, đối với trường hợp của bạn, số xung đồng hồ nhanh tạo nên một chu kỳ đồng hồ của chu kỳ đồng hồ chậm là 50000000/2 = 25000000. Vì chúng tôi muốn nửa chu kỳ đồng hồ, đó là 25000000/2 = 12500000 cho mỗi nửa chu kỳ . (thời lượng của mỗi mức cao hay thấp).

Đây là những gì nó trông giống như trong 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;

Những điều cần lưu ý:

  • Đồng hồ được tạo bằng 0 trong khi đặt lại. Điều này là ổn đối với một số ứng dụng, và không phải cho các ứng dụng khác, nó chỉ phụ thuộc vào những gì bạn cần đồng hồ cho.
  • Đồng hồ được tạo sẽ được định tuyến như một tín hiệu bình thường bởi các công cụ tổng hợp Xilinx.
  • 2Hz rất chậm. Mô phỏng trong một giây sẽ mất một lúc. Đó là một lượng mã nhỏ, do đó, nó sẽ tương đối nhanh để mô phỏng ngay cả trong 1 giây, nhưng nếu bạn bắt đầu thêm mã, thời gian để mô phỏng chu kỳ xung nhịp 2 Hz có thể dài hơn đáng kể.

EDIT: clk_2Hz_i được sử dụng để đệm tín hiệu đầu ra. VHDL không muốn sử dụng tín hiệu ở bên phải của bài tập khi nó cũng là đầu ra.


1
Không tệ, nhưng bạn có thể thêm / so sánh không dấu với số nguyên, vì vậy: if prescaler = 50_000_000/4 then ...prescaler <= prescaler + 1;sẽ đơn giản hơn một chút.
Brian Drumond

@StaceyAnne Thử điều này, tôi nhận được "Không thể đọc từ đối tượng 'out' clk_o; sử dụng 'bộ đệm' hoặc 'inout'" tôi có bỏ lỡ điều gì không?
trốn

@evading, một bộ đệm trên đầu ra là cần thiết. VHDL không thích thực tế clk_2Hzlà một đầu ra, nhưng giá trị của nó đang được đọc trong dòng này clk_2Hz <= not clk_2Hz;. Tôi đã chỉnh sửa trong bản sửa lỗi.
stanri

+1 Ví dụ tuyệt vời. Nhưng đây là nơi mà sự thiếu hiểu biết của tôi thể hiện (mới đối với VHDL). Sự khác biệt giữa prescaler <= (others => '0');và là prescaler <= '0';gì?
cbmeek

NVM! Tôi hoàn toàn bỏ lỡ những gì othersđã được sử dụng khi đọc một cuốn sách VHDL mà tôi có. Đây chỉ là một phím tắt để khai báo tất cả các bit "khác" thành một giá trị chung thay vì sử dụng một cái gì đó như "000000000000000000 ....", v.v.
cbmeek

9

Sử dụng một bộ đếm trước đồng hồ.

Giá trị bộ đếm trước của bạn sẽ là (clock_speed / wish_clock_speed) / 2 vì vậy (50Mhz (50.000.000) / 2hz (2)) / 2 = 12.500.000 mà trong nhị phân sẽ là 101111101011110000100000.

Đơn giản hơn: (50.000.000) / 2) / 2 = 12.500.000 chuyển đổi thành nhị phân -> 101111101011110000100000

Đây là một số mã về những việc cần làm: Sử dụng newClock cho bất cứ điều gì bạn cần 2hz cho ...

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;

Có vẻ như bạn đang tạo hai đồng hồ, một trong 0,5 Hz và một của 1 Hz? (vì thời gian đồng hồ của bạn là bộ đếm gộp * 2?). Ngoài ra, "+" sẽ báo lỗi, vì bạn đang thêm slv và tôi không chắc chắn về việc sử dụng thuộc tính tràn của add theo cách này trong mọi trường hợp. Tại sao không đi newClock : std_logic := '0', đếm đến prescaler / 2 và gán newClk <= not newClk?
stanri

Cảm ơn, logic của tôi đã được một chút. Tôi đã cập nhật bài viết ban đầu của mình với một số mã được thử nghiệm ngay bây giờ và một số đề xuất của bạn :)
MLM

Ugh - tất cả những người và số không và một nhận xét để nói nó thực sự là gì! Tại sao không sử dụng trình biên dịch để làm điều đó cho bạn ??? Và tại sao không chỉ sử dụng số nguyên?
Martin Thompson

Tôi có thể sai, nhưng tôi nghĩ rằng việc sử dụng các giá trị mặc định khi xác định tín hiệu trong kiến ​​trúc như trong ": = (other => '0')" là không thể tổng hợp được.
Arturs Wrapsans

Nó có thể tổng hợp được, nhưng về cơ bản chỉ hoạt động trên các GPU dựa trên SRAM, giống như hầu hết từ Xilinx, Altera hoặc Lattice.
Yann Vernier

8

Bạn thường không thực sự muốn xem bất cứ thứ gì chậm, chỉ cần tạo một kích hoạt ở tốc độ chính xác và sử dụng nó trong logic:

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

do đó bạn có thể tạo kích hoạt:

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;

tạo một vài hằng số với tần số đồng hồ của bạn và tần số cho phép bạn muốn và đi xa, với mã tự ghi để khởi động.


3

Tôi muốn đề nghị sử dụng IP quản lý đồng hồ kỹ thuật số Xilinx primitice .

Nó có giao diện cài đặt đồ họa nơi bạn có thể chỉ định tần số bạn muốn. Nó sẽ tạo ra một thành phần với đầu ra mong muốn của bạn dưới dạng tần số.

Nó có thể được tìm thấy trong IP Wizard;

nhập mô tả hình ảnh ở đây

Và sau đó bạn sẽ có thể chỉ định tần suất nào bạn muốn: nhập mô tả hình ảnh ở đây


0

Yếu tố = đầu vào-tín hiệu-frecuency / đầu ra-prescaler-frecuency.

CE = Đồng hồ kích hoạt. Nó nên là một xung rộng một xung nhịp (clk) hoặc cao nếu không được sử dụng.

Q = Tín hiệu đầu ra của xung rộng một xung nhịp với độ chính xác mong muốn.

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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.