游乐游手机版
首页/AI教程/文章详情

嵌入式系统VHDL入门指南:从语法到状态机

时间:2026-06-30 16:15
前言 这份资料源于笔者个人学习 VHDL 过程中的笔记整理,内容覆盖了基本语法、组合逻辑电路、时序逻辑电路、计数器设计、分频器与 PWM 生成以及有限状态机。每个模块都配有可直接运行的代码示例与对应的电路说明,适合边阅读边动手实践,帮助快速上手 FPGA 开发。 一、VHDL 是什么?底层原理与语言

前言

这份资料源于笔者个人学习 VHDL 过程中的笔记整理,内容覆盖了基本语法、组合逻辑电路、时序逻辑电路、计数器设计、分频器与 PWM 生成以及有限状态机。每个模块都配有可直接运行的代码示例与对应的电路说明,适合边阅读边动手实践,帮助快速上手 FPGA 开发。

一、VHDL 是什么?底层原理与语言基础

1.1 VHDL 的定位

VHDL(全称为 VHSIC Hardware Description Language)是一种用于描述数字电路结构与行为的硬件描述语言。需要特别注意的是,它和普通的软件编程语言有本质区别——你撰写的每一行 VHDL 代码,最终都会被综合工具(Synthesis Tool)映射为 FPGA 或 CPLD 内部的逻辑门、触发器和互联线,或者在仿真器中按照事件驱动机制逐步执行。

我们可以将 VHDL 视为电路设计的三层抽象模型:

层次含义行为级IF/CASE/PROCESS 描述“做什么”RTL 级综合后形成寄存器 + 组合逻辑(最常用)门级/结构级实例化具体元件,如 AND2DFF","rows":4,"cols":2,"id":"YWY1W"}">

这三个层次并非互斥。在实际工程中,成熟的设计往往会混合使用——顶层采用行为级描述来把控逻辑,底层则通过结构级例化标准单元。不过,日常开发中最常用的是 RTL 级描述,因为它恰好对应了寄存器与组合逻辑的组合,综合工具也能最高效地处理这一层次。

1.2 与 Verilog 的简要对比

目前主流的硬件描述语言主要是 VHDL 和 Verilog 两大阵营,不少初学者会纠结该选择哪一门。实际上,两者都能描述完全相同的硬件电路,最终的抉择往往取决于团队的技术栈和工具链偏好。下面的对比表可以帮助你快速建立整体认知:

特性VHDLVerilog语法风格强类型、Ada 风格C 风格,较宽松类型系统严格(STD_LOGIC vs BIT)较灵活可读性大型工程结构清晰代码更紧凑工业应用欧洲航天/军工较多北美 ASIC/FPGA 较多","rows":5,"cols":3,"id":"canvR"}">

1.3 底层执行模型:事件驱动仿真

VHDL 仿真器的核心运行机制是事件驱动模型。一旦理解了这一概念,后面写代码时遇到的许多看似“奇怪”的现象就会豁然开朗。关键点可以归纳为以下三个:

  • 信号赋值<=):它采用调度机制。在当前 PROCESS 结束前并不会立即更新,而是要等到所有进程都执行完毕后,在所谓的 delta cycle(可以理解为时间轴上的一个极小步长)内统一生效。
  • 变量赋值:=):它是立即生效的,并且仅在当前进程内部可见。
  • 敏感列表决定了组合逻辑进程何时重新执行;而时序进程通常只对时钟边沿敏感,其他信号的变化一概不予理会。

笔记中 JK 触发器的那个修正案例(把 q <= q1 放在了 PROCESS 外面),本质上就是信号延迟更新特性的一个典型体现——后面会详细展开,这里先留个印象。

1.4 标准库与数据类型

几乎每个 .vhd 文件的开头都需要一段固定的库声明,直接复制粘贴即可,无需每次手动敲写:

-- 几乎所有设计都需要的库声明(复制即可)
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;   -- STD_LOGIC、逻辑运算
USE IEEE.STD_LOGIC_ARITH.ALL;  -- 算术运算(较老,部分工具仍支持)
USE IEEE.STD_LOGIC_UNSIGNED.ALL; -- 无符号向量加减

常用的数据类型并不多,记住下面几种就足够了:

类型含义示例STD_LOGIC单 bit,九态逻辑'0', '1', 'Z', 'U'STD_LOGIC_VECTOR(n DOWNTO 0)bit 向量\"1010\"INTEGER仿真用整数VARIABLE sum : INTEGER","rows":4,"cols":3,"id":"qaebr"}">

关于引号的使用,有一条简单的规则:单 bit 用单引号,多位向量用双引号。多写几次就能形成条件反射了。

1.5 VHDL 文件三段式结构

每个 .vhd 文件的结构都可以视为一个固定的三层模板,从外到内依次为:

┌─────────────────────────┐
│  Library & Package      │  库与包引用
├─────────────────────────┤
│  Entity(实体)          │  对外接口:端口名、方向、类型
├─────────────────────────┤
│  Architecture(结构体)  │  内部实现:行为/结构/数据流
└─────────────────────────┘

有一个约定俗成的习惯:ENTITY 的名称要与文件名保持一致,并且 ENTITY ... ISEND ENTITY ... 要首尾呼应。这样做的好处在于,当项目中的文件数量增多时,只看文件名就能大致了解该模块的功能。

二、基本结构示例:逻辑门

2.1 实体模板

我们从最基础的逻辑门开始。下面这个模板是一个标准的 VHDL 模块骨架,你可以把它保存下来,每次新建文件时直接复制粘贴使用:

-- Library and package
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- entity:定义模块对外接口
ENTITY e_name IS
    PORT(
        in1, in2, in3 : IN STD_LOGIC;  -- 输入端口
        out1, out2    : OUT STD_LOGIC  -- 输出端口
    );
END ENTITY e_name;

-- architecture:定义模块内部行为
ARCHITECTURE arch_name OF e_name IS
BEGIN
    -- 具体逻辑写在这里
END ARCHITECTURE arch_name;

2.2 逻辑门测试模块 logic_test

接下来让这个模板真正跑起来。下面的模块演示了最基本的与门和或门:

-- entity
ENTITY logic_test IS
    PORT(
        a, b, c, d       : IN STD_LOGIC;
        and_OUT, or_OUT  : OUT STD_LOGIC
    );
END ENTITY logic_test;

-- architecture
ARCHITECTURE bhv OF logic_test IS
BEGIN
    and_OUT <= a AND b;              -- 2 输入与门
    or_OUT  <= a OR b OR c OR d;     -- 4 输入或门
END ARCHITECTURE bhv;

这里有两个关键点需要留意:<= 是信号赋值操作符;ANDORNOT 等逻辑运算符都是 STD_LOGIC_1164 包中预先定义好的,直接拿来用即可,完全无需担心底层是如何实现的。

三、组合逻辑

3.1 二选一多路选择器(2-to-1 MUX)

电路原理

MUX 的核心思想是“根据选择信号,从多个输入中挑选一个输出”。对于 2-to-1 MUX,其逻辑表达式为:

Output = (A · S̄) + (B · S)

当选择端 S(有时也记作 ch)为 0 时选通 A,为 1 时选通 B。下面是它的门级电路实现:

VHDL 实现 mux2

下面是用行为级描述编写的 VHDL 版本,重点在于 PROCESS 和敏感列表的用法:

-- Library and package
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- entity
ENTITY mux2 IS
    PORT(
        a, b, ch : IN STD_LOGIC;  -- a/b 为数据输入,ch 为选择端
        data     : OUT STD_LOGIC  -- 输出
    );
END ENTITY mux2;

-- architecture:行为级描述
ARCHITECTURE bhv OF mux2 IS
BEGIN
    PROCESS(a, b, ch)  -- 敏感列表:任一输入变化即重新计算
    BEGIN
        IF ch = '0' THEN
            data <= a;  -- 选择 A
        ELSE
            data <= b;  -- 选择 B
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

3.2 四选一多路选择器(4-to-1 MUX)

写法一:嵌套 IF(独立选择位 ch0、ch1)

4-to-1 MUX 需要两位选择信号。如果你觉得使用独立信号更加直观,可以采用嵌套 IF 的方式来实现:

-- Library and package
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- entity
ENTITY mux4 IS
    PORT(
        a, b, c, d       : IN STD_LOGIC;
        ch0, ch1         : IN STD_LOGIC;  -- 两位选择信号
        data             : OUT STD_LOGIC
    );
END ENTITY mux4;

-- architecture
ARCHITECTURE bhv OF mux4 IS
BEGIN
    PROCESS(a, b, c, d, ch0, ch1)
    BEGIN
        IF ch1 = '0' THEN
            IF ch0 = '0' THEN
                data <= a;  -- ch1ch0 = 00
            ELSE
                data <= b;  -- ch1ch0 = 01
            END IF;
        ELSE
            IF ch0 = '0' THEN
                data <= c;  -- ch1ch0 = 10
            ELSE
                data <= d;  -- ch1ch0 = 11
            END IF;
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

写法二:CASE 语句(推荐,更简洁)

如果选择信号本身就是一个 2 位向量,使用 CASE 语句会让代码显得更加清晰。这也是实际工程中推荐的做法:

-- entity(使用 2 位向量作为选择端)
ENTITY mux4 IS
    PORT(
        a, b, c, d : IN STD_LOGIC;
        ch         : IN STD_LOGIC_VECTOR(1 DOWNTO 0);
        data       : OUT STD_LOGIC
    );
END ENTITY mux4;

-- architecture
ARCHITECTURE bhv OF mux4 IS
BEGIN
    PROCESS(a, b, c, d, ch)
    BEGIN
        CASE ch IS
            WHEN "00" => data <= a;
            WHEN "01" => data <= b;
            WHEN "10" => data <= c;
            WHEN "11" => data <= d;
            WHEN OTHERS => data <= '0';  -- 兜底,防止 latch
        END CASE;
    END PROCESS;
END ARCHITECTURE bhv;

语法提醒:多位二进制串用双引号 "00",单位 bit 用单引号 '0'。这是新手最容易犯的错误,务必留意。

3.3 数码管编码器 seg7

将 4 bit 的 BCD 码(0~9)转换成 8 bit 段码,用于驱动七段数码管。下面的代码段带有使能控制,当使能无效时所有段全部熄灭:

-- Library and package
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- entity
ENTITY seg7 IS
    PORT(
        en      : IN STD_LOGIC;                    -- 使能:'1' 时关闭显示
        dat     : IN STD_LOGIC_VECTOR(3 DOWNTO 0); -- 4 bit 输入数据
        display : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)  -- 8 bit 段码输出 (a~g + dp)
    );
END ENTITY seg7;

-- architecture
ARCHITECTURE bhv OF seg7 IS
BEGIN
    PROCESS(en, dat)
    BEGIN
        IF en = '1' THEN
            display <= "11111111";  -- 使能无效,全部熄灭
        ELSE
            CASE dat IS
                WHEN "0000" => display <= "11111100";  -- 数字 0
                WHEN "0001" => display <= "01100000";  -- 数字 1
                WHEN "0010" => display <= "11011010";  -- 数字 2
                WHEN "0011" => display <= "11110010";  -- 数字 3
                WHEN "0100" => display <= "01100110";  -- 数字 4
                WHEN "0101" => display <= "10110110";  -- 数字 5
                WHEN "0110" => display <= "10111110";  -- 数字 6
                WHEN "0111" => display <= "11100100";  -- 数字 7
                WHEN "1000" => display <= "11111110";  -- 数字 8
                WHEN "1001" => display <= "11110110";  -- 数字 9
                WHEN OTHERS  => display <= "00000000";  -- 非法输入
            END CASE;
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

上图中右侧的 10 个七段管,分别标注了 a~g 各段与 dp 的对应关系,绿色高亮的部分表示当前数字需要点亮的段,这与 CASE 语句中的 8 bit 编码一一对应。

3.4 多人表决器 vote13

13 个人参与投票,当赞成票超过半数(≥7 票)时输出 '1'。这个例子中用到了变量——因为在循环中需要做累加操作,变量 := 的立即生效特性正好派上用场:

-- entity
ENTITY vote13 IS
    PORT(
        vt     : IN STD_LOGIC_VECTOR(13 DOWNTO 1);  -- 13 路投票输入
        result : OUT STD_LOGIC                      -- 表决结果
    );
END ENTITY vote13;

-- architecture
ARCHITECTURE bhv OF vote13 IS
BEGIN
    PROCESS(vt)
        VARIABLE sum : INTEGER RANGE 0 TO 13;  -- 变量:进程内立即更新
    BEGIN
        sum := 0;
        FOR i IN 1 TO 13 LOOP
            IF vt(i) = '1' THEN
                sum := sum + 1;  -- 统计赞成票
            END IF;
        END LOOP;
        IF sum >= 7 THEN
            result <= '1';  -- 超过半数
        ELSE
            result <= '0';
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

四、时序逻辑:触发器

4.1 D 触发器

基础版(无复位)

D 触发器是时序逻辑中最基本的单元,这里采用下降沿触发方式:

-- Library and package
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- entity
ENTITY dff IS
    PORT(
        clk, d : IN STD_LOGIC;
        q      : OUT STD_LOGIC
    );
END ENTITY dff;

-- architecture
ARCHITECTURE bhv OF dff IS
BEGIN
    PROCESS(clk)
    BEGIN
        -- 下降沿触发:检测 clk 事件且当前值为 '0'
        IF (clk'EVENT AND clk = '0') THEN
            q <= d;
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

注意:工业设计中更常见的是上升沿触发(clk='1'),原理完全相同,只需要改一处判断条件即可。

同步清零 / 置位版

清零和置位操作都在时钟边沿内进行判断,因此属于同步控制——时钟的优先级最高:

-- entity
ENTITY dff IS
    PORT(
        clk, d, clr, set : IN STD_LOGIC;
        q                : OUT STD_LOGIC
    );
END ENTITY dff;

-- architecture
ARCHITECTURE bhv OF dff IS
BEGIN
    PROCESS(clk)
    BEGIN
        IF (clk'EVENT AND clk = '0') THEN
            IF clr = '1' THEN
                q <= '0';       -- 同步清零,优先级高于 set
            ELSIF set = '1' THEN
                q <= '1';       -- 同步置位
            ELSE
                q <= d;         -- 正常采样
            END IF;
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

异步清零 / 置位版

异步控制的关键在于:将 clrset 加入敏感列表,并且在判断时钟之前优先判断这两个信号——它们不等待时钟,一旦有效立即生效:

-- architecture
ARCHITECTURE bhv OF dff IS
BEGIN
    PROCESS(clk, clr, set)  -- 异步控制必须加入敏感列表
    BEGIN
        IF clr = '1' THEN
            q <= '0';  -- 异步清零
        ELSIF set = '1' THEN
            q <= '1';  -- 异步置位
        ELSIF (clk'EVENT AND clk = '0') THEN
            q <= d;    -- 下降沿采样
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;
类型敏感列表生效时机同步复位仅 clk时钟边沿异步复位clk, rst, ...立即,优先级高于时钟","rows":3,"cols":3,"id":"JjXQp"}">

4.2 JK 触发器

功能说明

JK 触发器的特性表如下:

  • clrset 为低电平有效的异步控制端(符号上的小圆圈代表取反)
  • 时钟上升沿时:
    • J=0, K=1 → Q 清零
    • J=1, K=0 → Q 置 1
    • J=0, K=0 → Q 保持
    • J=1, K=1 → Q 翻转

初版代码(存在 OUT 端口读回问题)

-- entity
ENTITY jkff IS
    PORT(
        clk, j, k, clr, set : IN STD_LOGIC;
        q                   : OUT STD_LOGIC
    );
END ENTITY jkff;

-- architecture(有问题:OUT 端口不能在进程内读取)
ARCHITECTURE bhv OF jkff IS
BEGIN
    PROCESS(clk, clr, set)
    BEGIN
        IF clr = '0' THEN           -- 低有效异步清零
            q <= '0';
        ELSIF set = '0' THEN        -- 低有效异步置位
            q <= '1';
        ELSIF (clk'EVENT AND clk = '1') THEN -- 上升沿
            IF (j = '1' AND k = '0') THEN
                q <= '1';
            ELSIF (j = '0' AND k = '1') THEN
                q <= '0';
            ELSIF (j = '1' AND k = '1') THEN
                q <= NOT q;  -- ❌ 错误:不能读取 OUT 端口 q
            ELSE
                q <= q;      -- ❌ 错误:同上
            END IF;
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

问题出在哪里?在 VHDL 中,OUT 端口在架构内部是只写的,不能读取其值。因此 q <= NOT qq <= q 都会导致编译报错。

有两种修复方案可供选择:

  • OUT 改为 BUFFER(可读可写,但级联时需要注意)
  • 推荐做法:定义一个内部信号 q1,在进程中操作 q1,然后在进程外部将 q1 赋值给 q

修正版(内部信号 + 进程外赋值)

-- Library and package
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- entity
ENTITY jkff IS
    PORT(
        clk, j, k, clr, set : IN STD_LOGIC;
        q                   : OUT STD_LOGIC
    );
END ENTITY jkff;

-- architecture
ARCHITECTURE bhv OF jkff IS
    SIGNAL q1 : STD_LOGIC;  -- 内部信号,可在进程内读写
BEGIN
    PROCESS(clk, clr, set)
    BEGIN
        IF clr = '0' THEN
            q1 <= '0';
        ELSIF set = '0' THEN
            q1 <= '1';
        ELSIF (clk'EVENT AND clk = '1') THEN
            IF (j = '1' AND k = '0') THEN
                q1 <= '1';
            ELSIF (j = '0' AND k = '1') THEN
                q1 <= '0';
            ELSIF (j = '1' AND k = '1') THEN
                q1 <= NOT q1;  -- 翻转:读取的是 q1,合法
            ELSE
                q1 <= q1;      -- 保持
            END IF;
        END IF;
    END PROCESS;
    q <= q1;  -- ✅ 关键:进程外赋值,信号在此刻更新后驱动输出
END ARCHITECTURE bhv;

信号 vs 变量:变量使用 := 赋值,立即生效;信号使用 <= 赋值,在进程挂起后才更新。正是由于信号具有这种“延迟”特性,输出映射必须放在 PROCESS 外部,或者改用 BUFFER 端口类型。

五、计数器

5.1 八位加计数器

-- Library and package
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- entity
ENTITY counter IS
    PORT(
        clk  : IN STD_LOGIC;
        data : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
    );
END ENTITY counter;

-- architecture
ARCHITECTURE bhv OF counter IS
BEGIN
    PROCESS(clk)
        VARIABLE temp : STD_LOGIC_VECTOR(7 DOWNTO 0) := "00000000";
    BEGIN
        IF (clk'EVENT AND clk = '0') THEN -- 下降沿计数
            temp := temp + 1;               -- 变量立即 +1
            data <= temp;                   -- 输出当前值
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

5.2 加减可控计数器

增加一个 sub 控制端,sub='0' 时加计数,sub='1' 时减计数,并带有溢出回绕功能:

-- entity
ENTITY counter IS
    PORT(
        clk, sub : IN STD_LOGIC;
        data     : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
    );
END ENTITY counter;

-- architecture
ARCHITECTURE bhv OF counter IS
BEGIN
    PROCESS(clk)
        VARIABLE temp : STD_LOGIC_VECTOR(7 DOWNTO 0) := "00000000";
    BEGIN
        IF (clk'EVENT AND clk = '0') THEN
            IF sub = '0' THEN
                -- 加计数,255 后回绕到 0
                IF temp = "11111111" THEN
                    temp := "00000000";
                ELSE
                    temp := temp + 1;
                END IF;
            ELSIF sub = '1' THEN
                -- 减计数,0 后回绕到 255
                IF temp = "00000000" THEN
                    temp := "11111111";
                ELSE
                    temp := temp - 1;
                END IF;
            END IF;
            data <= temp;
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

5.3 带溢出标志、复位、重装载的完整计数器

这一版本集成了多项功能:复位、预置数以及溢出标志。注意观察复位和重装载是如何嵌入到整个流程中的:

-- entity
ENTITY counter IS
    PORT(
        clk, sub, rst, load, clr_f : IN STD_LOGIC;
        number                     : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
        flag                       : OUT STD_LOGIC;  -- 溢出/下溢标志
        data                       : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
    );
END ENTITY counter;

-- architecture
ARCHITECTURE bhv OF counter IS
BEGIN
    PROCESS(clk, rst, load, clr_f)
        VARIABLE temp : STD_LOGIC_VECTOR(7 DOWNTO 0) := "00000000";
    BEGIN
        IF clr_f = '1' THEN
            flag <= '0';              -- 清除溢出标志
        ELSIF rst = '1' THEN
            temp := "00000000";       -- 同步复位
            flag <= '0';
        ELSIF load = '1' THEN
            temp := number;           -- 加载预设值
        ELSIF (clk'EVENT AND clk = '0') THEN
            IF sub = '0' THEN
                IF temp = "11111111" THEN
                    temp := "00000000";
                    flag <= '1';      -- 向上溢出
                ELSE
                    temp := temp + 1;
                END IF;
            ELSE
                IF temp = "00000000" THEN
                    temp := "11111111";
                    flag <= '1';      -- 向下溢出
                ELSE
                    temp := temp - 1;
                END IF;
            END IF;
        END IF;
        data <= temp;
    END PROCESS;
END ARCHITECTURE bhv;

六、分频器与 PWM

6.1 2/4/8/16 分频

利用计数器各个 bit 的自然分频特性,是最巧妙也最简单的方法:bit0 对应二分频,bit1 对应四分频,依此类推,各输出的占空比均为 50%:

-- entity
ENTITY freq IS
    PORT(
        clk, en               : IN STD_LOGIC;
        f2, f4, f8, f16       : OUT STD_LOGIC
    );
END ENTITY freq;

-- architecture
ARCHITECTURE bhv OF freq IS
BEGIN
    PROCESS(clk, en)
        VARIABLE count : STD_LOGIC_VECTOR(3 DOWNTO 0) := "0000";
    BEGIN
        IF en = '1' THEN
            IF (clk'EVENT AND clk = '0') THEN
                count := count + 1;
                f2  <= count(0);  -- ÷2
                f4  <= count(1);  -- ÷4
                f8  <= count(2);  -- ÷8
                f16 <= count(3);  -- ÷16
            END IF;
        ELSE
            f2  <= '1';
            f4  <= '1';
            f8  <= '1';
            f16 <= '1';
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

6.2 十二分频 freq12

对于非 2 的幂次分频,需要手动设置计数上限和翻转点:

-- entity
ENTITY freq12 IS
    PORT(
        clk, en : IN STD_LOGIC;
        wa ve    : OUT STD_LOGIC
    );
END ENTITY freq12;

-- architecture
ARCHITECTURE bhv OF freq12 IS
BEGIN
    PROCESS(clk, en)
        VARIABLE count : STD_LOGIC_VECTOR(3 DOWNTO 0) := "0000";
    BEGIN
        IF en = '0' THEN -- 低有效使能
            IF (clk'EVENT AND clk = '1') THEN -- 上升沿计数
                IF count = "0101" THEN          -- 计数到 5
                    count := count + 1;
                    wa ve <= '0';               -- 拉低输出
                ELSIF count >= "1011" THEN     -- 计数到 11
                    wa ve <= '1';               -- 拉高输出
                    count := "0000";           -- 复位计数器,完成 12 分频周期
                ELSE
                    count := count + 1;
                END IF;
            END IF;
        ELSE
            wa ve <= '1';  -- 禁用时输出高
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

6.3 PWM 脉宽调制

第一种:可调占空比

通过比较计数值与 data 的大小,来控制 PWM 波形中高电平的宽度:

-- entity
ENTITY PWM IS
    PORT(
        clk, en : IN STD_LOGIC;
        data    : IN STD_LOGIC_VECTOR(3 DOWNTO 0);  -- 占空比控制(0~15)
        wa ve    : OUT STD_LOGIC
    );
END ENTITY PWM;

-- architecture
ARCHITECTURE bhv OF PWM IS
BEGIN
    PROCESS(clk, en, data)
        VARIABLE count : STD_LOGIC_VECTOR(3 DOWNTO 0) := "0000";
    BEGIN
        IF en = '1' THEN
            IF data = "1111" THEN
                wa ve <= '1';  -- 100% 占空比
            ELSIF (clk'EVENT AND clk = '0') THEN
                count := count + 1;
                IF count < data THEN
                    wa ve <= '1';  -- 计数小于 data:高电平
                ELSE
                    wa ve <= '0';  -- 否则低电平
                END IF;
            END IF;
        ELSE
            wa ve <= '1';
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

第二种:可调占空比与周期

更灵活的版本,使用 nh(高电平持续时间)和 nl(低电平持续时间)分别独立控制:

-- entity
ENTITY pwm IS
    PORT(
        clk           : IN STD_LOGIC;
        nh, nl        : IN STD_LOGIC_VECTOR(3 DOWNTO 0);  -- 高/低持续时间
        wa ve          : OUT STD_LOGIC
    );
END ENTITY pwm;

-- architecture
ARCHITECTURE bhv OF pwm IS
BEGIN
    PROCESS(clk, nh, nl)
        VARIABLE count : STD_LOGIC_VECTOR(4 DOWNTO 0) := "00000";
    BEGIN
        -- 边界条件处理
        IF (nh = "0000" AND nl = "0000") THEN
            wa ve <= 'Z';          -- 高阻态
        ELSIF nh = "0000" THEN
            wa ve <= '0';          -- 恒低
        ELSIF nl = "0000" THEN
            wa ve <= '1';          -- 恒高
        ELSIF (clk'EVENT AND clk = '0') THEN
            IF count >= ('0' & nh + nl - 1) THEN
                count := "00000"; -- 一个完整周期结束
                wa ve  <= '1';
            ELSE
                IF count = (nh - 1) THEN
                    wa ve <= '0';  -- 高电平阶段结束,转低
                END IF;
                count := count + 1;
            END IF;
        END IF;
    END PROCESS;
END ARCHITECTURE bhv;

单个脉冲的周期长度 = nh + nl 个时钟周期,其中高电平持续 nh 个周期,低电平持续 nl 个周期。调整这两个参数可以同时改变占空比和输出频率。

七、有限状态机(FSM)

7.1 基本四状态 RGB 控制器

这是一个最简单的 Moore 型状态机:状态在时钟上升沿切换,输出仅取决于当前状态。这种模式非常经典,值得熟练掌握:

-- Library and package
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

-- entity
ENTITY rgb IS
    PORT(
        clk : IN STD_LOGIC;
        led : OUT STD_LOGIC_VECTOR(2 DOWNTO 0)  -- RGB 三色,每 bit 驱动一色
    );
END ENTITY rgb;

-- architecture
ARCHITECTURE bhv OF rgb IS
    TYPE state_type IS (s0, s1, s2, s3);
    SIGNAL state : state_type;
BEGIN
    -- 状态转移进程(时序逻辑)
    PROCESS(clk)
    BEGIN
        IF (clk'EVENT AND clk = '1') THEN
            CASE state IS
                WHEN s0 => state <= s1;
                WHEN s1 => state <= s2;
                WHEN s2 => state <= s3;
                WHEN s3 => state <= s0;
                WHEN OTHERS => state <= s0;
            END CASE;
        END IF;
    END PROCESS;
    -- 输出逻辑进程(组合逻辑)
    PROCESS(state)
    BEGIN
        CASE state IS
            WHEN s0 => led <= "010";  -- 绿
            WHEN s1 => led <= "011";  -- 黄
            WHEN s2 => led <= "100";  -- 红
            WHEN s3 => led <= "001";  -- 蓝
            WHEN OTHERS => led <= "000";
        END CASE;
    END PROCESS;
END ARCHITECTURE bhv;

7.2 完善版:八色可逆状态机

这个版本展示了更完整的三段式 FSM 写法:现态寄存器 → 次态逻辑 → 输出逻辑,同时支持方向控制和异步复位:

-- entity
ENTITY rgb IS
    PORT(
        clk, ddr, clr : IN STD_LOGIC;  -- ddr:方向控制(0 正向,1 反向)
        led           : OUT STD_LOGIC_VECTOR(2 DOWNTO 0)
    );
END ENTITY rgb;

-- architecture
ARCHITECTURE bhv OF rgb IS
    TYPE state_type IS (
        red, blue, green, yellow,
        purple, cyan, white, black
    );
    SIGNAL current_state, next_state : state_type;
BEGIN
    ----------------------------------------------------------------
    -- 现态进程:状态寄存器
    ----------------------------------------------------------------
    cs : PROCESS(clk, clr)
    BEGIN
        IF clr = '1' THEN
            current_state <= black;  -- 异步清零到 black
        ELSIF (clk'EVENT AND clk = '1') THEN
            current_state <= next_state;
        END IF;
    END PROCESS cs;

    ----------------------------------------------------------------
    -- 次态进程:组合逻辑,决定下一状态
    ----------------------------------------------------------------
    ns : PROCESS(current_state, ddr)
    BEGIN
        CASE current_state IS
            WHEN black  =>
                IF ddr = '0' THEN next_state <= yellow;
                ELSE              next_state <= blue;   END IF;
            WHEN yellow =>
                IF ddr = '0' THEN next_state <= purple;
                ELSE              next_state <= black;  END IF;
            WHEN purple =>
                IF ddr = '0' THEN next_state <= cyan;
                ELSE              next_state <= yellow; END IF;
            WHEN cyan   =>
                IF ddr = '0' THEN next_state <= white;
                ELSE              next_state <= purple; END IF;
            WHEN white  =>
                IF ddr = '0' THEN next_state <= green;
                ELSE              next_state <= cyan;   END IF;
            WHEN green  =>
                IF ddr = '0' THEN next_state <= red;
                ELSE              next_state <= white;  END IF;
            WHEN red    =>
                IF ddr = '0' THEN next_state <= blue;
                ELSE              next_state <= green;  END IF;
            WHEN blue   =>
                IF ddr = '0' THEN next_state <= black;
                ELSE              next_state <= red;    END IF;
            WHEN OTHERS =>
                next_state <= black;
        END CASE;
    END PROCESS ns;

    ----------------------------------------------------------------
    -- 输出逻辑进程:Moore 型,输出只依赖现态
    ----------------------------------------------------------------
    ol : PROCESS(current_state)
    BEGIN
        CASE current_state IS
            WHEN red    => led <= "100";
            WHEN green  => led <= "010";
            WHEN blue   => led <= "001";
            WHEN white  => led <= "111";
            WHEN yellow => led <= "110";
            WHEN cyan   => led <= "011";
            WHEN purple => led <= "101";
            WHEN OTHERS => led <= "000";  -- black 及默认:全灭
        END CASE;
    END PROCESS ol;
END ARCHITECTURE bhv;

八、知识点速查

主题关键语法 / 概念赋值信号 <=(延迟更新);变量 :=(立即更新)进程PROCESS(敏感列表);时序逻辑放时钟边沿内触发clk'EVENT AND clk='1' 上升沿;'0' 下降沿复位同步:仅 clk 敏感;异步:控制信号加入敏感列表端口OUT 不可读;用内部 SIGNAL 或改 BUFFER多路选择嵌套 IFCASE;向量用双引号计数器VARIABLE 做中间累加;注意溢出回绕分频计数器 bit 输出 = 2^n 分频PWM计数值与阈值比较;nh/nl 分别控制高/低时间状态机现态寄存器 + 次态逻辑 + 输出逻辑(三段式)","rows":11,"cols":2,"id":"tnzF5"}">
来源:https://developer.aliyun.com/article/1743849
上一篇全球独角兽榜单正式揭晓 下一篇ZStack源码级AI诊断套件让故障排查秒出答案
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
企业组织级AI赋能具体实施方法
AI教程 · 2026-06-30

企业组织级AI赋能具体实施方法

前段时间收到一位读者的留言,希望聊聊企业级、组织级的AI赋能究竟该怎么落地。巧的是,前几天刚看到一份咨询调研机构的数据:对近一两年所有企业级AI赋能项目的统计显示,超过90%的甲方企业认为,AI赋能在核心业务价值链上没有发挥任何实质性作用。除了AI辅助办公、企业智能知识库这类边缘应用起到了一些辅助效

Scrapy与Redis分布式架构的日本电商多平台数据聚合系统
AI教程 · 2026-06-30

Scrapy与Redis分布式架构的日本电商多平台数据聚合系统

从事日本电商数据聚合工作时,最大的难点在于要同时应对雅虎拍卖、煤炉(Mercari)、乐天和亚马逊日本站等截然不同的平台。以往使用单机爬虫,经常出现运行中崩溃的情况——单点故障、带宽利用率不足、数据存储混乱,这三大痛点令人困扰。 本文分享一套基于Scrapy + Redis的分布式爬虫方案,专门解决

详细PuTTY 0.81安装教程 SSH远程连接与自定义路径设置
AI教程 · 2026-06-30

详细PuTTY 0.81安装教程 SSH远程连接与自定义路径设置

​ PuTTY(简称PT)是一款轻量级开源SSH Telnet客户端,凭借简洁高效的特性,多年来始终是系统管理员与开发者进行远程连接的首选利器。本教程将详细介绍PuTTY 0 81版本的完整安装过程,并指导您自定义安装路径,以便更灵活地管理SSH远程连接工具。 安装准备 首先需要说明的是,整个安装流

在线教育系统必备功能:直播课堂与题库考试架构
AI教程 · 2026-06-30

在线教育系统必备功能:直播课堂与题库考试架构

很多人一想到做在线教育系统,第一反应往往是先把直播间和课程播放器搭起来,觉得“能看课”就万事大吉了。真到落地那天才发现,系统能不能顺滑跑起来,关键全藏在那些细节里——课程怎么组织、学习进度怎么记、考试怎么处理、后台怎么管得住。前端看起来就几个页面,后端其实是一整条业务链路。不管你是要做在线教育APP

ZStack源码级AI诊断套件让故障排查秒出答案
AI教程 · 2026-06-30

ZStack源码级AI诊断套件让故障排查秒出答案

一次故障排查,到底要花多少时间? 运维人员处理私有云、虚拟化平台的问题,流程大致都是这样:先翻日志看现象,再去文档里找对应机制,然后搜社区有没有类似案例,最后综合判断给出答复。简单问题半小时,复杂问题可能要跨天——而这些时间里,大部分精力耗在了“找信息”而不是“做决策”上。 类似的问题,也许每天都在