FPGA学习笔记(6)逻辑设计小结与以太网发送前置

文章梳理了 FPGA 常见逻辑设计方法(计数器、移位寄存器、FSM)以及它们在波形对齐上的典型现象,并把序列检测与网络数据解析用状态机思路贯通起来。随后补充网络基础(分层与封装/解封装、以太网帧结构、MAC/PHY 分工),为后续用 FPGA 设计以太网/UDP 发送电路做铺垫。

一.FPGA逻辑设计方法 梳理

1.1 计数器

1.1.1 同步边沿检测电路

上升沿检测信号组合输出f'比时序输出fo晚1clk。

1.1.2 图样检测

不同写法 fhead和sr(shift reg)信号相位不同

  • 写法 A:fhead 用寄存器输出(时序版)

    复制代码
    always @(posedge clk) fhead <= (sr==8'hF6); 

    现象:fhead 拉高时,cnt 往往会显示成 1(或看起来从 1 开始) ,并且整体对齐会比你直觉晚一拍

  • 写法 B:fhead 用组合逻辑(组合逻辑版)

    复制代码
    assign fhead = (sr==8'hF6); 

同一个检测条件 (sr==8'hF6),如果 fhead 做成寄存器输出,cnt 的动作看起来会"晚一拍/从1开始";如果 fhead 用组合输出,fhead 会更早出现,但 cnt 仍按时钟更新,所以波形上会出现 fhead=1cnt 还是 0 的"错拍感"。(一般不影响最终功能,只是波形对齐不同)

复制代码
`timescale 1ns/1ns
module pattern_detc(
    input clk,            // 时钟信号
    input rstn,           // 复位信号
    input din,            // 输入数据信号
    output reg [7:0] dout // 输出数据信号
);
    reg [7:0] sr;         // 移位寄存器,用来存储数据
    reg [2:0] cnt;        // 计数器,用来追踪存储的数据位数
    reg fhead;            // 标志信号,表示检测到特定模式 (0xF6)
 
    // 数据存储 - 移位寄存器 (sr)
    always @(posedge clk or negedge rstn) begin
        if (~rstn)
            sr[7:0] <= 8'b0; // 复位时清空寄存器
        else
            sr[7:0] <= {sr[6:0], din}; // 每个时钟周期将新数据推入移位寄存器
    end
    // 特征识别 - 检测模式 0xF6
    always @(posedge clk or negedge rstn) begin
        if (~rstn)
            fhead <= 0;
        else  
            fhead <= (sr == 8'hF6); // 当移位寄存器中的数据为 0xF6 时置位
    end
    /*wire fhead;
    assign fhead = (sr == 8'hF6);   */
 
    // 计数器控制 - 在检测到模式后开始计数
    always @(posedge clk or negedge rstn) begin
        if (~rstn)
            cnt <= 0; // 复位时计数器清零
        else if (fhead) 
            //cnt<=0;
            cnt <= 1; // 当检测到模式 0xF6 时,从 1 开始计数
        else 
            cnt <= cnt + 1; // 否则继续计数
    end
 
    // 数据输出 - 当计数器达到 7 时,将数据存储到 dout
    always @(posedge clk or negedge rstn) begin
        if (~rstn)
            dout <= 8'b0; // 复位时输出清零
        else if (cnt==7)
            dout <= sr[7:0]; 
    end
endmodule

1.1.3 脉宽计数器 vs 脉冲数量计数器

cnt1 统计来了几次脉冲,cnt2 统计脉冲高了多久(以 clk 周期为单位)。

  • 输入pulse1 / pulse2(和 clk 同步),rst_n 复位

  • 数量计数 cnt1(pulse1)

    每出现一次上升沿,cnt1++ ------不管脉冲多宽,都只算一次

  • 脉宽计数 cnt2(pulse2) :把 enb2 = q1 当作"高电平窗口使能",pulse2 为高的每个时钟周期都 cnt2++,下降沿后停止计数

波形图

对比写法,时序逻辑写法注释掉了:

复制代码
module counterN (
    input  wire       clk,
    input  wire       rst_n,
    input  wire       pulse,
    output  reg       enb1,
    output reg  [7:0] cnt1
);
/*wire enb1_w = (~q0) & q1;

always @(posedge clk or negedge rst_n) begin
  if(!rst_n) cnt1 <= 8'd0;
  else if(enb1_w) cnt1 <= cnt1 + 8'd1;
end
*/
//enb1 用寄存器会导致"计数晚一拍"(通常不影响数量,但波形可能不完全一样)
   reg q0,q1;
   always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            q0 <= 1'b0;
            q1 <= 1'b0;
        end else begin
            q0 <= q1;
            q1 <= pulse;
        end
    end
 always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            enb1<=0;
        else 
            enb1<=~q0&q1;
    end
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            cnt1 <= 8'd0;
        else if(enb1)
            cnt1 <= cnt1 + 8'd1;
    end
endmodule

1.2 移位寄存器应用

1.2.1并串转换

(参考资料:老师图)

信号:

  • clk:系统时钟

  • reset:复位

  • pdata[15:0]:输入并行数据(每 16 个时钟更新一次)

  • dind(pin / 起始指示):告诉系统"新一帧数据来了,从这里开始"

  • latchd[15:0]:锁存后的稳定数据帧

  • cnt[3:0]:位序号计数器(0~15)

  • sind(sin):同步后的起始指示/串行输出有效窗口(常用于标记 sdata 正在输出一帧)

  • sdata:串行输出数据位,高位先出(按 cnt 依次输出 latchd 的各 bit)

后面把串并数据指示位都记英语缩写pin,sin了


pin→锁存 pdatalatchd,同时 counter16 从 0 开始计数;每个 clk 用 cnt 去 MUX 选择 latchd 的一位输出到 sdata,连续 16 拍完成一帧,并用 sind 标记这段输出有效区间。

对比data_sel(主要逻辑):

  1. assign sdata = latchd[15 - cnt] (之前博客记录的AI写的代码实现逻辑)

  2. 移位寄存器写法

pin起始位使能选择器,使pdata[0],pdata[1]....pdata[15]并行进入,否则移位寄存器功能串行输出,低位补0。

核心逻辑,{dout,sr15[14:0]}={sr[14:0],1'b0}

信号说明:pdata_latch={dout,sr15[14:0]},sr15为15个shift reg移位寄存器+reg dout=16个shift reg

对比:关于移位寄存器串并转换,1bit=>8bit:sr8<={sr8[6:0],din};低位补din,counter计数控制八位输出sr[7:0]/pdata[7:0]。

下面是老师给的10位转1位代码,

1.3 有限状态机

1.3.1 序列检测

一般为移位寄存器实现,这里简化版应用,可以对比网络数据包发送接收逻辑,用状态图表示:空闲状态,接收前导码,以太网帧头等,只是序列检测的数据逻辑比较简化。

  • 序列检测(0110 / 4'h6):

    也是 FSM,只不过"输入很简单(1bit)+ 规则很短(4位)",状态表示"匹配到前缀多少位"。

  • 网络数据解析:

    本质也是"序列检测",但序列变成了"多段长格式":前导码→以太网头→IP头→ICMP头→数据。

    状态不是"匹配到第几位",而是"正在解析哪一段协议字段"。

序列检测是"短模式匹配"的 FSM;网络解析是"长格式分段解析"的 FSM。两者思想一致:用状态表示进度,用转移表示条件。

下面又又又直接偷老师图:

1.3.2 网络数据解析

参考正点原子FPGA开发指南

(1)

图里的 ICMP 接收状态机就是在做这件事:一边按字节接收数据,一边按协议格式分段解析

状态机流程

  1. st_idle(空闲)

    等待网卡开始吐数据(接收开始)。

  2. st_preamble(前导码)

    先跳过以太网"同步用"的前导码 55...55 D5。这段不属于真正的数据内容,只是为了让收发双方"对齐节拍"。

  3. st_eth_head(以太网帧头)

    开始读帧头:目的 MAC、源 MAC、类型 EtherType。

    • 如果目的 MAC 不匹配(不是发给我的)或类型不是 IP → 直接丢掉(走 error_en / rx_end)。
  4. st_ip_head(IP 头)

    读 IP 首部:版本、长度、协议号、源/目的 IP 等。

    • 如果目的 IP 不对,或者 IP 协议号不是 ICMP(或者你图里写的 TCP 之类)→ 丢弃
  5. st_icmp_head(ICMP 头)

    读取 ICMP 类型/校验等。

    • 类型不对(比如只想要 echo request/reply)→ 丢弃
  6. st_rx_data(有效载荷数据)

    真正把 payload 当"有效数据"接收下来(缓存/上交)。

  7. st_rx_end(接收结束)

    一帧处理完,回到 idle,准备下一帧。

说明:

(1)

skip_en这一段我不需要存,只需要计数跳过(比如前导码、某些头部字段)。

error_en中途发现不符合条件,立刻放弃这帧(省资源、也避免把错数据当真)。

(2)为什么网络解析必须用状态机?

因为网络帧是"流式输入 + 分段结构 + 条件分支很多"。

  1. 数据是"边来边处理"的

    以太网数据是一拍一字节/一拍几bit连续进来的,不是一次性给你完整包。状态机能记住"我现在解析到哪一段了"。

  2. 协议是"分层封装"的

    以太网里嵌 IP,IP 里嵌 ICMP。每层的头部长度/字段不同。状态机就是"按层拆箱"的流程控制器。

  3. 有大量"早退/丢包"条件

    MAC 不对就丢、IP 不对就丢、协议号不对就丢、ICMP 类型不对就丢。

    状态机天然适合做"走一步判断一步,不符合就结束"。

  4. 不只要识别,还要控制资源与时序

    哪些字节需要保存,哪些只要跳过;什么时候开始写 FIFO,什么时候停止;这些都靠状态机在正确时刻开关控制信号。

二. 以太网UDP数据报文发送电路设计0-基础知识

2.1 OSI模型

通信系统的功能分层/分工图,通信过程里需要完成的功能(比如:比特怎么传、怎么成帧、怎么寻址路由、怎么建立连接、怎么表示/加密数据、怎么给应用用)分成 7 层。

ISO开发系统和TCP/IP协议栈分别对应OSI模型的七层和四层。

七层

详情查看维基百科,链接

https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B

四层-tcp/ip网络协议栈

2.2 数据

2.2.1 封装

(1)概念

通信过程每层协议都要加一个数据首部(header)称为封装。

同一份用户数据,在不同层会加上不同的控制信息(首部/尾部):

  • 传输层 :TCP 叫 段 segment ;UDP 常叫 数据报 datagram

    关键作用:端口号、可靠传输(TCP)、分段重传等。

  • 网络层(IP) :叫 IP 数据报 / 分组 packet

    关键作用:IP 地址、路由转发。

  • 链路层(以太网) :叫 帧 frame

    关键作用:MAC 地址、同一链路内传输、差错检测(FCS)。

每层都把数据"包装成自己这一层的格式"。

2.2.2 封装与解封装

发送端:从上到下逐层封装

应用数据往下走时,每层都加自己的首部(链路层还会加尾部 FCS),形成:

应用数据

TCP首部 + 应用数据 (传输层:段)

IP首部 + TCP段 (网络层:IP数据报)

以太网首部 + IP数据报 + 以太网尾部FCS (链路层:帧)

→ 变成比特流在网线/无线里发出去(物理层)

接收端:从下到上逐层解封装

收到的是以太网帧:

  • 链路层先检查帧格式/FCS,正确就把以太网首部/尾部去掉,把"有效载荷"交给上层。

  • 网络层再检查 IP 首部,决定交给 TCP/UDP,再把 IP 首部去掉。

  • 传输层根据端口号交给正确的应用进程,最后应用程序拿到原始数据。

2.2.3 典例

让我们专注于以太网/IP/UDP数据包。 这些数据包很容易生成;但功能强大,它们可以在互联网上传输(并发送到世界任何地方!)。

这是一个例子:

55 55 55 55 55 55 55 D5 00 10 A4 7B EA 80 00 12 34 56 78 90 08 00 45 00 00 2E B3 FE 00 00 80 11 05 40 C0 A8 00 2C C0 A8 00 04 04 00 04 00 00 1A 2D E8 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 B3 31 88 1B

下面是它的解码。

协议栈:

该数据包使用三种不同的协议:以太网、IP和UDP(从低到高层协议)。 每种协议都添加了自己的功能,并嵌入到较低级别的协议中。 UDP部分包含要发送的数据("有效负载")。 IP部分允许数据包通过互联网路由。 以太网部分允许数据包在以太网网络上本地发送。 UDP嵌入到IP中,IP本身又嵌入到以太网中。 这是一个关于如何一起使用网络协议的好例子。

上面显示的数据包在此处解码:

以太网前导码/SFD(同步器):55 55 55 55 55 55 55 D5

以太网目标地址:00 10 A4 7B EA 80

以太网源地址:00 12 34 56 78 90

以太网类型:08 00(=IP)

IP报头:45 00 00 2E B3 FE 00 00 80

IP协议:11(=UDP)

IP校验和:05 40

IP源地址(192.168.0.44):C0 A8 00 2C

IP目标地址(192.168.0.4):C0 A8 00 04

UDP源端口(1024):04 00

UDP目标端口(1024):04 00

UDP有效负载长度(18):00 1A

UDP校验和:2D E8

UDP有效负载(18 字节):00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11

以太网校验和:B3 31 88 1B

您可以注意到使用的IP:从"192.168.0.44"到"192.168.0.4"。 这些是本地IP,此数据包不会传播很远...

2.3 以太网(Ethernet)

1.当今现有局域网采用的最通用的通信协议标准。

分类,标准以太网(10M bit/s),快速以太网(100M),千兆以太网(1000M)

2.以太网接口

开发板/网卡上说的"以太网接口"一般包含两部分:

  • MAC 控制器(Media Access Control):链路层的 MAC 子层(偏数字逻辑)

  • PHY(Physical Layer Transceiver):物理层收发器(偏模拟/高速电路)

再加上两者之间的标准连接口:MII / RMII / GMII / RGMII / SGMII 等。

3.MAC 控制器

MAC 主要做的是 以太网帧(frame)这一层相关工作,负责构造/解析"以太网帧"的格式与校验(链路层)

例如:

  • 组帧/解帧:目的 MAC、源 MAC、EtherType、Payload

  • 添加/检查 FCS(CRC32)

  • 帧间隔、最小帧长、填充

  • 与上层(IP/ARP)或用户逻辑之间的数据搬运:常见是 FIFO / DMA 接口

说明:

(1)MAC 不负责 IP 路由、UDP/TCP 端口等网络层/传输层功能。

IP/UDP/TCP 的"包"通常在 CPU 的协议栈里生成,或者在 FPGA 里写"硬件协议栈/简化协议栈"生成。

(2)MAC 通常是一个硬件模块(外设/控制器),可能出现在:

  • MCU/SoC 内部(比如 STM32 的以太网 MAC 外设)

  • FPGA 里(自己写 Verilog/VHDL,或用厂商 IP Core)

  • 独立网卡芯片中

CPU/单片机做的更多是:

  • 管理 MAC/PHY 寄存器

  • 把要发送的数据交给 MAC(通过 DMA/FIFO),或者从 MAC 收到数据再处理

  • 跑协议栈(ARP/IP/UDP/TCP)

4.PHY

PHY 是 物理层收发器,负责把"数字比特流"变成"网线上能跑的电信号",以及反过来恢复回来。包括:

  • 自协商(Auto-Negotiation):10/100/1000 速率、双工模式协商

  • 向 MAC 提供一个标准数字接口(MII/RMII/RGMII/SGMII 等)

  • 线路编码/解码(不同速率/标准不同)

  • 模拟前端:驱动网线、接收放大、滤波、时钟恢复(CDR)

5.以太网接口典型发送路径:

  1. 上层(CPU/FPGA逻辑)准备好一帧以太网数据(至少包含 MAC 地址、类型、payload)

  2. MAC 加工:补齐、算 CRC、控制发送时序

  3. MAC 通过 MII/RMII/RGMII 等把数据喂给 PHY

  4. PHY 把它编码成电信号送到 RJ45(中间还要经过隔离变压器"磁性器件")

接收路径反过来:

PHY 从线缆恢复比特 → 交给 MAC → MAC 校验 CRC/拆帧 → 上交给协议栈或用户逻辑。

以太网接口电路设计可以FPGA实现MAC控制器,然后通过RGMII接口发给PHY,关于RGMII高速接口可以调用IP核简化。

相关推荐
燎原星火*2 小时前
FPGA 逻辑级数
fpga开发
锦瑟弦音2 小时前
跑酷游戏开发笔记3 && 游戏开始场景 cocos 3.8.7
javascript·笔记·游戏
受之以蒙2 小时前
智能目标检测:用 Rust + dora-rs + yolo 构建“机器之眼”
人工智能·笔记·rust
熬了夜的程序员3 小时前
【Rust学习之路】第 0 章:理解 Rust 的核心哲学
开发语言·学习·rust
EniacCheng3 小时前
【RUST】学习笔记-环境搭建
笔记·学习·rust
d111111111d3 小时前
STM32编码电机闭环PID调节教程。
笔记·stm32·单片机·嵌入式硬件·学习·面试
冬夜戏雪3 小时前
【学习日记】【12.18】【整理了下论文相关的计划】
学习
其美杰布-富贵-李3 小时前
TSTabFusionTransformer 深度学习学习笔记
笔记·深度学习·学习
speop3 小时前
【datawhale组队学习】|TASK02|结构化输入
网络·人工智能·学习