亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> VHDL

教程:您的第一个 FPGA 程序:LED 闪光灯

第 1 部分:VHDL 或 Verilog 的设计

本教程展示了以指定频率闪烁 LED 的 VHDL 和 Verilog 代码的构造。 VHDL 和 Verilog 都有显示,你可以选择你想先学习的。每当编写设计代码时,FPGA 设计人员都需要确保它按照预期的方式工作。尽管您尽了最大的努力,但您的初始设计中总会出现错误。找出这些错误的最好方法是在模拟环境中。本教程分为两个阶段:

  1. HDL的设计
  2. HDL模拟

这两个步骤对于成功的 FPGA 开发都至关重要。有时时间紧迫的 FPGA 设计人员会尝试跳过第二步,即代码仿真。然而,这是非常重要的一步!如果没有适当的模拟,您将被迫在硬件上调试代码,这可能是一项非常困难且耗时的工作。

项目要求:

设计以 100 Hz、50 Hz、10 Hz 或 1 Hz 的指定频率闪烁 LED 的 HDL 代码。对于每个闪烁频率,LED 将设置为 50% 的占空比(它将在一半时间亮起)。 LED 频率将通过作为 FPGA 输入的两个开关来选择。还有一个名为 LED_EN 的附加开关需要为“1”才能打开 LED。 FPGA 将由 25 MHz 振荡器驱动。

我们先画出频率选择器的真值表:

启用 开关 1 开关 2 LED 驱动频率 0 - - (禁用) 1 0 0 100 Hz 1 0 1 50 Hz 1 1 0 10 Hz 1 1 1 1 Hz

为了使其正常工作,将有 4 个输入和 1 个输出。信号将是:

信号名称 方向 说明 i_clock 输入 25 MHz 时钟 i_enable 输入 使能开关(逻辑 0 =无 LED 驱动) i_switch_1 输入真值表中的开关 1 i_switch_2 输入真值表中的开关 2 o_led_drive 输出 驱动 LED 的信号

对于该设计,有四个同时运行的计数器进程。这意味着它们都在完全相同的时间运行。他们的工作是跟踪每个不同频率看到的时钟脉冲数。即使开关没有选择该特定频率,计数器仍在运行!这就是硬件设计和并发的美妙之处。一切都在运行!最初理解这一点可能具有挑战性,但它是您需要掌握的核心概念。

开关仅用于选择要使用的输出。他们创建了所谓的多路复用器。多路复用器或简称多路复用器是一个选择器,它将选择多个输入中的一个来传播或传递到输出。它是一个组合逻辑,这意味着它不需要时钟来操作。下面是该设计的框图。花一些时间思考如何实现这个设计。尝试自己编写代码。我选择的方式可以在下面找到。

框图-LED闪烁程序

设计的VHDL代码,tutorial_led_blink.vhd:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity tutorial_led_blink is
  port (
    i_clock      : in  std_logic;
    i_enable     : in  std_logic;
    i_switch_1   : in  std_logic;
    i_switch_2   : in  std_logic;
    o_led_drive  : out std_logic
    );
end tutorial_led_blink;

architecture rtl of tutorial_led_blink is

  -- Constants to create the frequencies needed:
  -- Formula is: (25 MHz / 100 Hz * 50% duty cycle)
  -- So for 100 Hz: 25,000,000 / 100 * 0.5 = 125,000
  constant c_CNT_100HZ : natural := 125000;
  constant c_CNT_50HZ  : natural := 250000;
  constant c_CNT_10HZ  : natural := 1250000;
  constant c_CNT_1HZ   : natural := 12500000;


  -- These signals will be the counters:
  signal r_CNT_100HZ : natural range 0 to c_CNT_100HZ;
  signal r_CNT_50HZ  : natural range 0 to c_CNT_50HZ;
  signal r_CNT_10HZ  : natural range 0 to c_CNT_10HZ;
  signal r_CNT_1HZ   : natural range 0 to c_CNT_1HZ;
  
  -- These signals will toggle at the frequencies needed:
  signal r_TOGGLE_100HZ : std_logic := '0';
  signal r_TOGGLE_50HZ  : std_logic := '0';
  signal r_TOGGLE_10HZ  : std_logic := '0';
  signal r_TOGGLE_1HZ   : std_logic := '0';

  -- One bit select wire.
  signal w_LED_SELECT : std_logic;
  
begin

  -- All processes toggle a specific signal at a different frequency.
  -- They all run continuously even if the switches are
  -- not selecting their particular output.
  
  p_100_HZ : process (i_clock) is
  begin
    if rising_edge(i_clock) then
      if r_CNT_100HZ = c_CNT_100HZ-1 then  -- -1, since counter starts at 0
        r_TOGGLE_100HZ <= not r_TOGGLE_100HZ;
        r_CNT_100HZ    <= 0;
      else
        r_CNT_100HZ <= r_CNT_100HZ + 1;
      end if;
    end if;
  end process p_100_HZ;


  p_50_HZ : process (i_clock) is
  begin
    if rising_edge(i_clock) then
      if r_CNT_50HZ = c_CNT_50HZ-1 then  -- -1, since counter starts at 0
        r_TOGGLE_50HZ <= not r_TOGGLE_50HZ;
        r_CNT_50HZ    <= 0;
      else
        r_CNT_50HZ <= r_CNT_50HZ + 1;
      end if;
    end if;
  end process p_50_HZ;

  
  p_10_HZ : process (i_clock) is
  begin
    if rising_edge(i_clock) then
      if r_CNT_10HZ = c_CNT_10HZ-1 then  -- -1, since counter starts at 0
        r_TOGGLE_10HZ <= not r_TOGGLE_10HZ;
        r_CNT_10HZ    <= 0;
      else
        r_CNT_10HZ <= r_CNT_10HZ + 1;
      end if;
    end if;
  end process p_10_HZ;

  
  p_1_HZ : process (i_clock) is
  begin
    if rising_edge(i_clock) then
      if r_CNT_1HZ = c_CNT_1HZ-1 then  -- -1, since counter starts at 0
        r_TOGGLE_1HZ <= not r_TOGGLE_1HZ;
        r_CNT_1HZ    <= 0;
      else
        r_CNT_1HZ <= r_CNT_1HZ + 1;
      end if;
    end if;
  end process p_1_HZ;

  
  -- Create a multiplexor based on switch inputs
  w_LED_SELECT <= r_TOGGLE_100HZ when (i_switch_1 = '0' and i_switch_2 = '0') else
                  r_TOGGLE_50HZ  when (i_switch_1 = '0' and i_switch_2 = '1') else
                  r_TOGGLE_10HZ  when (i_switch_1 = '1' and i_switch_2 = '0') else
                  r_TOGGLE_1HZ;

  
  -- Only allow o_led_drive to drive when i_enable is high (and gate).
  o_led_drive <= w_LED_SELECT and i_enable;

end rtl;

设计的 Verilog 代码,tutorial_led_blink.v:

module tutorial_led_blink 
  (
   i_clock,
   i_enable,
   i_switch_1,
   i_switch_2,
   o_led_drive
   );

  input i_clock;
  input i_enable;
  input i_switch_1;
  input i_switch_2;
  output o_led_drive;
   
  // Constants (parameters) to create the frequencies needed:
  // Input clock is 25 kHz, chosen arbitrarily.
  // Formula is: (25 kHz / 100 Hz * 50% duty cycle)
  // So for 100 Hz: 25,000 / 100 * 0.5 = 125
  parameter c_CNT_100HZ = 125;
  parameter c_CNT_50HZ  = 250;
  parameter c_CNT_10HZ  = 1250;
  parameter c_CNT_1HZ   = 12500;

  // These signals will be the counters:
  reg [31:0] r_CNT_100HZ = 0;
  reg [31:0] r_CNT_50HZ = 0;
  reg [31:0] r_CNT_10HZ = 0;
  reg [31:0] r_CNT_1HZ = 0;
  
  // These signals will toggle at the frequencies needed:
  reg 	     r_TOGGLE_100HZ = 1'b0;
  reg 	     r_TOGGLE_50HZ  = 1'b0;
  reg 	     r_TOGGLE_10HZ  = 1'b0;
  reg 	     r_TOGGLE_1HZ   = 1'b0;
  
  // One bit select
  reg 	     r_LED_SELECT;
  wire 	     w_LED_SELECT;
  
    
begin

  // All always blocks toggle a specific signal at a different frequency.
  // They all run continuously even if the switches are
  // not selecting their particular output.

  always @ (posedge i_clock)
    begin
      if (r_CNT_100HZ == c_CNT_100HZ-1) // -1, since counter starts at 0
        begin	      
          r_TOGGLE_100HZ <= !r_TOGGLE_100HZ;
          r_CNT_100HZ    <= 0;
        end
      else
        r_CNT_100HZ <= r_CNT_100HZ + 1;
    end

  
  always @ (posedge i_clock)
    begin
      if (r_CNT_50HZ == c_CNT_50HZ-1) // -1, since counter starts at 0
        begin	      
          r_TOGGLE_50HZ <= !r_TOGGLE_50HZ;
          r_CNT_50HZ    <= 0;
        end
      else
        r_CNT_50HZ <= r_CNT_50HZ + 1;
    end


  always @ (posedge i_clock)
    begin
      if (r_CNT_10HZ == c_CNT_10HZ-1) // -1, since counter starts at 0
        begin	      
          r_TOGGLE_10HZ <= !r_TOGGLE_10HZ;
          r_CNT_10HZ    <= 0;
        end
      else
        r_CNT_10HZ <= r_CNT_10HZ + 1;
    end

  
  always @ (posedge i_clock)
    begin
      if (r_CNT_1HZ == c_CNT_1HZ-1) // -1, since counter starts at 0
        begin	      
          r_TOGGLE_1HZ <= !r_TOGGLE_1HZ;
          r_CNT_1HZ    <= 0;
        end
      else
        r_CNT_1HZ <= r_CNT_1HZ + 1;
    end

  // Create a multiplexer based on switch inputs
  always @ (*)
  begin
    case ({i_switch_1, i_switch_2}) // Concatenation Operator { }
      2'b11 : r_LED_SELECT <= r_TOGGLE_1HZ;
      2'b10 : r_LED_SELECT <= r_TOGGLE_10HZ;
      2'b01 : r_LED_SELECT <= r_TOGGLE_50HZ;
      2'b00 : r_LED_SELECT <= r_TOGGLE_100HZ;
    endcase      
  end

  assign o_led_drive = r_LED_SELECT & i_enable;

  // Alternative way to design multiplexer (same as above):
  // More compact, but harder to read, especially to those new to Verilog
  // assign w_LED_SELECT = i_switch_1 ? (i_switch_2 ? r_TOGGLE_1HZ : r_TOGGLE_10HZ) : 
                                        (i_switch_2 ? r_TOGGLE_50HZ : r_TOGGLE_100HZ);
  // assign o_led_drive = w_LED_SELECT & i_enable;
    
  
end 
  
endmodule


VHDL

  1. C# Hello World - 你的第一个 C# 程序
  2. 使用 FPGA 进行嵌入式设计:构建项目
  3. 使用嵌入式 FPGA 技术简化设计
  4. 如何创建您的第一个 VHDL 程序:Hello World!
  5. 如何优化您的 PM 程序
  6. 最大化您的润滑油分析程序
  7. C# Hello World:第一个控制台应用程序
  8. Verilog 教程
  9. 如何为您的设备设计预防性维护计划
  10. 让你的 PM 程序更好的 10 个技巧
  11. Ultiboard PCB 设计教程
  12. KiCAD PCB 设计教程