วิธีแบ่ง 50MHz ลงเป็น 2Hz ใน VHDL บน Xilinx FPGA


13

ฉันมีบอร์ด Xilinx FPGA พร้อมคริสตัล 50MHz ฉันต้องหารมันลงไปที่ 2Hz ใน VHDL ฉันจะทำสิ่งนี้ได้อย่างไร


13
คุณลองทำอะไรจริง ๆ
Matt Young

ทำไมไม่ใช้ IP ตัวจัดการนาฬิกา Xilinx
Arturs Vancans

คำตอบ:


19

โดยทั่วไปมีสองวิธีในการทำเช่นนี้ สิ่งแรกคือการใช้แกนซินธิไซเซอร์นาฬิกาพื้นเมือง Xilinx ข้อดีอย่างหนึ่งของสิ่งนี้คือเครื่องมือ Xlinx จะจดจำนาฬิกาดังกล่าวและกำหนดเส้นทางผ่านเส้นทางที่ต้องการ เครื่องมือจะจัดการกับข้อ จำกัด เวลาใด ๆ (ไม่สามารถใช้ได้จริงในกรณีนี้เนื่องจากเป็นนาฬิกา 2Hz)

วิธีที่สองคือการใช้ตัวนับเพื่อนับจำนวนสัญญาณนาฬิกาที่เร็วขึ้นจนกว่าจะถึงครึ่งหนึ่งของช่วงเวลาที่ช้าลงของนาฬิกา ตัวอย่างเช่นสำหรับกรณีของคุณจำนวนพัลส์นาฬิกาเร็วที่ทำขึ้นหนึ่งรอบนาฬิกาของรอบนาฬิกาช้าคือ 50000000/2 = 25000000 เนื่องจากเราต้องการครึ่งนาฬิกาเป็น 25000000/2 = 12500000 สำหรับแต่ละครึ่งรอบ . (ระยะเวลาของแต่ละสูงหรือต่ำ)

นี่คือสิ่งที่ดูเหมือนใน 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;

สิ่งที่ควรทราบ:

  • นาฬิกาที่สร้างขึ้นเป็นศูนย์ระหว่างการรีเซ็ต สิ่งนี้ใช้ได้สำหรับแอปพลิเคชั่นบางตัวและไม่ใช่สำหรับแอปพลิเคชันอื่น ๆ ขึ้นอยู่กับสิ่งที่คุณต้องการสำหรับนาฬิกา
  • นาฬิกาที่สร้างขึ้นจะถูกกำหนดเส้นทางเป็นสัญญาณปกติโดยเครื่องมือการสังเคราะห์ Xilinx
  • 2Hz ช้ามาก การจำลองสำหรับวินาทีกำลังจะใช้เวลาสักครู่ มันเป็นรหัสจำนวนเล็กน้อยดังนั้นจึงควรจะค่อนข้างรวดเร็วในการจำลองแม้เป็นเวลา 1 วินาที แต่ถ้าคุณเริ่มเพิ่มรหัสเวลาที่ใช้ในการจำลองรอบสัญญาณนาฬิกาที่ 2 Hz อาจมีความยาวอย่างมีนัยสำคัญ

แก้ไข: clk_2Hz_i ใช้เพื่อบัฟเฟอร์สัญญาณเอาต์พุต VHDL ไม่ต้องการใช้สัญญาณทางด้านขวาของการมอบหมายเมื่อมันเป็นเอาต์พุต


1
ไม่เลว แต่คุณสามารถเพิ่ม / เปรียบเทียบแบบไม่มีเครื่องหมายด้วยจำนวนเต็มดังนั้น: if prescaler = 50_000_000/4 then ...และprescaler <= prescaler + 1;จะง่ายกว่าเล็กน้อย
Brian Drummond

@StaceyAnne ลองนี้ฉันจะได้รับ "ไม่สามารถอ่านจาก 'out' object clk_o; ใช้ 'buffer' หรือ 'inout'" ฉันพลาดอะไรไปหรือเปล่า?
เลี่ยง

@evading ต้องการบัฟเฟอร์ในเอาต์พุต VHDL ไม่ชอบความจริงที่ว่าclk_2Hzเป็นเอาท์พุท แต่ยังมีการอ่านค่าในบรรทัดclk_2Hz <= not clk_2Hz;นี้ ฉันแก้ไขในการแก้ไข
stanri

+1 ตัวอย่างที่ดี แต่ที่นี่เป็นที่ที่ความไม่รู้ของฉันแสดง (ใหม่ถึง VHDL) ความแตกต่างระหว่างprescaler <= (others => '0');และprescaler <= '0';คืออะไร?
cbmeeks

NVM! ฉันคิดถึงสิ่งที่othersใช้สำหรับการอ่านหนังสือ VHDL ที่ฉันมี มันเป็นแค่ทางลัดสำหรับการประกาศบิต "อื่น ๆ " ทั้งหมดให้เป็นค่าทั่วไปแทนที่จะใช้บางอย่างเช่น "0000000000000000000000 .... " ฯลฯ
cbmeeks

9

ใช้พรีสเกลนาฬิกา

ค่าพรีเซลเลอร์ของคุณจะเป็นของคุณ (clock_speed / ที่ต้องการ_clock_speed) / 2 ดังนั้น (50Mhz (50,000,000) / 2hz (2)) / 2 = 12,500,000 ซึ่งในรูปแบบเลขฐานสองจะเป็น 1011111010111100000000000

ง่ายขึ้น: (50,000,000) / 2) / 2 = 12,500,000 แปลงเป็นไบนารี่ -> 1011111010111100000000000

นี่คือรหัสของสิ่งที่ต้องทำ: ใช้ newClock สำหรับทุกสิ่งที่คุณต้องการ 2hz สำหรับ ...

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;

ดูเหมือนว่าคุณกำลังสร้างสองนาฬิกาหนึ่งใน 0.5 Hz และหนึ่งใน 1 Hz? (เนื่องจากนาฬิกาเป็นพรีสเกลเลอร์ของคุณ * 2) นอกจากนี้ "+" จะให้ข้อผิดพลาดเนื่องจากคุณกำลังเพิ่ม slvs และฉันไม่แน่ใจเกี่ยวกับการใช้คุณสมบัติโอเวอร์โฟลของการเพิ่มด้วยวิธีนี้ในทุกกรณี ทำไมไม่ไปเพียงไปnewClock : std_logic := '0'ถึง prescaler / 2 และมอบหมายnewClk <= not newClk?
stanri

ขอบคุณตรรกะของฉันถูกปิดเล็กน้อย ฉันปรับปรุงการโพสต์ครั้งแรกของฉันที่มีรหัสการทดสอบบางอย่างในขณะนี้และบางส่วนของข้อเสนอแนะของคุณ :)
MLM

ฮึ - คนพวกนั้นและเลขศูนย์ทั้งหมดและคอมเม้นท์บอกว่ามันคืออะไร! ทำไมไม่ใช้คอมไพเลอร์เพื่อทำสิ่งนั้นให้คุณ ??? แล้วทำไมไม่ใช้เลขจำนวนเต็มล่ะ?
Martin Thompson

ฉันอาจจะผิด แต่ฉันคิดว่าการใช้ค่าเริ่มต้นเมื่อกำหนดสัญญาณใน arhitecture เช่นเดียวกับใน ": = (others => '0')" ไม่สามารถสังเคราะห์ได้
Arturs Vancans

มันสามารถสังเคราะห์ได้ แต่โดยทั่วไปแล้วจะใช้งานได้กับ FPGA ที่ใช้ SRAM เท่านั้นเช่น Xilinx, Altera หรือ Lattice
Yann Vernier

8

โดยปกติแล้วคุณไม่ต้องการที่จะบันทึกสิ่งใด ๆ ที่ช้าเพียงแค่สร้างการเปิดใช้งานในอัตราที่ถูกต้องและใช้มันในตรรกะ:

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

คุณสามารถสร้างการเปิดใช้งานดังนี้:

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;

สร้างค่าคงที่สองสามตัวด้วยความถี่สัญญาณนาฬิกาของคุณและเปิดใช้งานความถี่ที่ต้องการและออกไปพร้อมรหัสการบู๊ตด้วยตนเองเพื่อบูต


3

ผมค่อนข้างจะแนะนำให้ใช้Xilinx primitice นาฬิกาดิจิตอล IP

มันมีอินเตอร์เฟซการตั้งค่ากราฟิกที่คุณสามารถระบุความถี่ที่คุณต้องการ มันจะสร้างส่วนประกอบที่มีเอาต์พุตที่คุณต้องการเป็นความถี่

สามารถพบได้ในตัวช่วยสร้าง IP

ป้อนคำอธิบายรูปภาพที่นี่

จากนั้นคุณจะสามารถระบุความถี่ที่คุณต้องการ: ป้อนคำอธิบายรูปภาพที่นี่


0

ตัวคูณ = อินพุต - สัญญาณ - frecuency / output-prescaler-frecuency

CE = เปิดใช้งานนาฬิกา ควรเป็นพัลส์แบบกว้างหนึ่งนาฬิกา (clk) หรือสูงหากไม่ได้ใช้

Q = สัญญาณเอาท์พุตของพัลส์กว้างหนึ่งนาฬิกาพร้อมค่าความอิสระที่ต้องการ

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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.