IP Hash 硬件防火墙 IP 设计规格
1. 概述
1.1 功能定位
ip_hash 是一个基于 CRC32 Hash 算法的硬件查表防火墙 IP,用于千兆以太网报文的快速过滤。核心思路:
• 对每个进入的 IPv4 报文提取 {DstIP, DstPort} 作为 Hash Key
• 通过 CRC32 计算 Hash 值,索引到 4 路组相联 Hash 表中并行比较
• 命中表项后执行 Pass/Drop 决策
• 非 IPv4 报文全部透传
• 表项管理(增/删/改)由外部 CPU 通过 LCPU 总线发起,Hash 计算和桶位分配由 FPGA 硬件完成
1.2 关键指标
| 项目 | 规格 |
|------|------|
| 接口 | 1 × GMII RX (8b @125MHz) + 1 × GMII TX (8b @125MHz) |
| 时钟 | 单时钟域 125MHz |
| 表容量 | 128 条表项,4-way × 32 rows |
| Hash 算法 | CRC32(Ethernet 多项式 0x04C11DB7) |
| Hash Key | {DstIP[31:0], DstPort[15:0]} = 48b |
| 转发模式 | Store-and-Forward |
| 转发延迟 | 1~2 个包周期 |
| 最大包长 | 1522 Bytes |
| 管理总线 | LCPU (REQ/ACK, 32b ADDR/DATA, 125MHz) |
| 分片支持 | 首片查表,后续分片跟随;4 条并发流,1ms 超时 |
---
2. 系统架构
2.1 顶层框图
+------------------------------------------+
| ip_hash_top |
| |
gmii_rx_clk --->|----+ |
gmii_rx_d[7:0] ->|GMII|-->[PktBuf]-->[Parser]-->[Hash] |
gmii_rx_dv --->| RX | (W) (L3/L4) (CRC32) |
|----+ | | |
| | [Lookup] |
| [FragCache] (4-way) |
| | | |
gmii_tx_clk <---|----+ +---[Decision] |
gmii_tx_d[7:0] <-|GMII|<--[PktBuf]<---------+ | |
gmii_tx_en <---| TX | (R) | |
|----+ | |
| v |
lcpu_clk ------>| |
lcpu_req ------>| [LCPU I/F] --> [CmdDec] --> [TableMgr] |
lcpu_addr[31:0]>| |
lcpu_rdata[31:0]| |
lcpu_wdata[31:0]| |
lcpu_ack <------| |
+------------------------------------------+
2.2 子模块划分
| 模块 | 功能 |
|------|------|
| gmii_rx_if | GMII 接收接口,提取 RX_DV/RX_ER/RXD |
| gmii_tx_if | GMII 发送接口,产生 TX_EN/TXD |
| pkt_buffer | 例化 package_fifo.v,Store-and-Forward 整包缓存 |
| pkt_parser | 解析 EtherType→IP Header→L4 Port,提取查表 Key |
| crc32_hash | 对 48b Key 做 CRC32,输出 32b Hash 值,取低 5b 作索引 |
| hash_table | 4-way × 32 rows 表体,含并行比较器和命中逻辑 |
| frag_cache | 分片状态缓存:4 条并发流,1ms 超时管理 |
| lcpu_if | LCPU 总线接口,REQ/ACK 握手,地址译码,寄存器读写 |
| cmd_decoder | 解析 CPU 命令(Insert/Delete/Modify),驱动 TableMgr |
| table_mgr | Hash 表项管理状态机:扫描桶位、写入/删除/修改表项 |
| stat_counter | 统计计数器(总包数/命中/未命中/FCS 错误) |
---
3. 接口信号
3.1 GMII 接收(GMII RX)
| 信号 | 方向 | 位宽 | 说明 |
|------|------|------|------|
| gmii_rx_clk | I | 1 | 125MHz 接收时钟 |
| gmii_rx_dv | I | 1 | 接收数据有效 |
| gmii_rx_er | I | 1 | 接收错误指示 |
| gmii_rx_d | I | 8 | 接收数据总线 |
3.2 GMII 发送(GMII TX)
| 信号 | 方向 | 位宽 | 说明 |
|------|------|------|------|
| gmii_tx_clk | I | 1 | 125MHz 发送时钟(与 RX 同源) |
| gmii_tx_en | O | 1 | 发送数据有效 |
| gmii_tx_er | O | 1 | 发送错误(固定为 0) |
| gmii_tx_d | O | 8 | 发送数据总线 |
3.3 LCPU 管理总线
| 信号 | 方向 | 位宽 | 说明 |
|------|------|------|------|
| lcpu_clk | I | 1 | LCPU 操作时钟(125MHz) |
| lcpu_addr | I | 32 | 地址总线 |
| lcpu_wdata | I | 32 | 写数据总线 |
| lcpu_rdata | O | 32 | 读数据总线 |
| lcpu_req | I | 1 | 读写请求脉冲 |
| lcpu_rh_wl | I | 1 | 1=读 / 0=写 |
| lcpu_ack | O | 1 | 操作完成应答 |
> 时序参见《常用LRIP接口时序.md》LCPU 读写寄存器时序。所有信号同步于 lcpu_clk(125MHz)。
---
4. Hash 表设计
4.1 Hash 算法
• 多项式:Ethernet CRC32 0x04C11DB7(reflected: 0xEDB88320),与 GMII FCS 同款
• 输入:Key = {DstIP[31:0], DstPort[15:0]}, 48b 有效位
• Mask 控制:全局 Mask 寄存器对 Key 做屏蔽后再送入 CRC32
• mask_ip = 0 → DstIP 段清零(仅用 Port 匹配)
• mask_port = 0 → DstPort 段清零(仅用 IP 匹配)
• 默认 mask_ip=1, mask_port=1(完整 Key)
• 输出:32b CRC 结果
• 索引:hash_index = crc_result[4:0],5b → 32 rows
• Tag:hash_tag = crc_result[31:5](高 27b),用于桶内比较,降低误命中
4.2 表组织结构
Row 0 +--------+--------+--------+--------+
| Way 0 | Way 1 | Way 2 | Way 3 |
Row 1 | Entry | Entry | Entry | Entry |
... | ... | ... | ... | ... |
Row 31 | Entry | Entry | Entry | Entry |
+--------+--------+--------+--------+
↓ 并行比较 ↓
hash_tag[27:0] vs entry_tag[27:0]
• 32 rows × 4 ways = 128 entries
• 每条表项包含:valid + tag + key + action
• 查表时并行读取同一行的 4 路 entry,同时比较 tag 和 key,选出命中的那一路
4.3 表项格式
┌────────┬──────────────────────────┬────────────────────────────┬────────┐
│ valid │ hash_tag │ key │ action │
│ [1b] │ [27:0] │ {DstIP[31:0], DstPort[15:0]} │ [1b] │
└────────┴──────────────────────────┴────────────────────────────┴────────┘
| 字段 | 位宽 | 说明 |
|------|------|------|
| valid | 1 | 表项有效标志 |
| hash_tag | 28 | CRC32[31:5] 高 28b,桶内 Tag 比较 |
| key_ip | 32 | 目的 IP 地址匹配值 |
| key_port | 16 | 目的端口匹配值 |
| action | 1 | 0 = Pass, 1 = Drop |
| 合计 | 78b | —— |
> 存储映射:每条 entry 占 3 × 32b(96b),分配 3 个连续寄存器地址。
> 其中 78b 有效,18b reserved。
4.4 查表流程(硬件自动完成)
报文到达 → 解析 DstIP, DstPort
↓
key = {Masked(DstIP), Masked(DstPort)}
↓
crc_out = CRC32(key) // 组合逻辑 + 1 拍寄存
↓
index = crc_out[4:0] // 读 Row[index] 的 4 路
tag = crc_out[31:5]
↓
并行比较 4 路:
hit[i] = entry[i].valid && (entry[i].tag == tag) && (entry[i].key == masked_key)
↓
if any hit → action = hit_entry.action
else → action = default_policy // Pass or Drop
时序:CRC32 1 拍 + 读表 1 拍 + 并行比较 1 拍 = 3 拍查表延迟(@125MHz = 24ns)。
---
5. 表项管理(CPU 命令)
5.1 命令概述
CPU 不参与 Hash 计算,仅通过 LCPU 总线向 FPGA 发送管理命令。FPGA 内部 table_mgr 状态机完成 Hash 计算、桶位扫描、表项读写。
| 命令 | 编码 | 说明 |
|------|------|------|
| NOP | 2'b00 | 无操作 |
| INSERT | 2'b01 | 插入表项:CPU 提供 {DstIP, DstPort, Action},FPGA 计算 Hash、找空桶、写入 |
| DELETE | 2'b10 | 删除表项:CPU 提供 {DstIP, DstPort},FPGA 计算 Hash、找匹配桶、清除 valid |
| MODIFY | 2'b11 | 修改表项:CPU 提供 {DstIP, DstPort, Action},FPGA 计算 Hash、找匹配桶、更新 Action |
5.2 命令执行流程
INSERT:
CPU写 CMD_KEY_LO (DstIP)
CPU写 CMD_KEY_HI ({DstPort, Action})
CPU写 CMD (INSERT)
↓ FPGA
1. CRC32(Masked(Key)) → index, tag
2. 读 Row[index] 4 路
3. 扫描 valid=0 的空桶
4. 找到空桶 → 写入 {valid=1, tag, key, action} → status = OK
5. 4 路全满 → status = TABLE_FULL(stat: overflow_cnt++)
DELETE:
CPU写 CMD_KEY_LO (DstIP)
CPU写 CMD_KEY_HI ({DstPort, ...})
CPU写 CMD (DELETE)
↓ FPGA
1. CRC32(Masked(Key)) → index, tag
2. 读 Row[index] 4 路
3. 查找 tag+key 均匹配的 entry
4. 找到 → valid=0 → status = OK
5. 未找到 → status = NOT_FOUND
MODIFY:
CPU写 CMD_KEY_LO (DstIP)
CPU写 CMD_KEY_HI ({DstPort, Action})
CPU写 CMD (MODIFY)
↓ FPGA
1. CRC32(Masked(Key)) → index, tag
2. 读 Row[index] 4 路
3. 查找 tag+key 均匹配的 entry
4. 找到 → 更新 action → status = OK
5. 未找到 → status = NOT_FOUND
5.3 命令时序约束
• 表项管理操作与查表操作共享同一个 Hash Table 存储器
• 若 table_mgr 占用存储器时(写/删/改进行中),数据包查表被阻塞
• 管理操作通常不超过 10 个时钟周期(读表+比较+写回)
• CPU 应避免在高流量期间频繁执行管理命令
• CPU 查询 STATUS 寄存器确认操作完成后,方可发起下一条命令
---
6. 报文处理管线
6.1 整体数据流
GMII RX → [FCS Check] ──(pass)──→ [PktBuf Write] ─→ [Parser] → [Hash Lookup] → [Decision]
↓ ↓
(FCS fail) Pass / Drop
↓ ↓
丢弃 ┌───────┴───────┐
(不写PktBuf) ↓ ↓
Pass Drop
↓ ↓
wpkt_push=1 wpkt_push=0
↓ ↓
GMII TX ← [Preamble] ← [PktBuf Read] ←── rpkt_pop ←────────── [Forward] (数据块不提交)
↑
└── (非IPv4: 直接透传, 也走 wpkt_push=1)
关键机制:报文边接收边写入 PktBuf,此时尚未完成查表、不知道最终决策。查表完成获得 Pass/Drop 结果后,通过控制 wpkt_push 信号决定该包是否提交到读侧队列:
• Pass:wpkt_push = 1,数据块提交,读侧可 rpkt_pop 读出转发
• Drop:wpkt_push = 0,数据块不提交,已被写入的 RAM 空间将在下一个报文到来时被覆盖(相当于静默丢弃)
6.2 非 IPv4 报文
EtherType != 0x0800 → 不查表 → 直接透传(Default Pass)
• ARP(0x0806)、VLAN(0x8100)、IPv6(0x86DD)等全部放行
• 不计入 Hash 命中/未命中统计
6.3 FCS 校验
• 输入 GMII → 接收时逐字节送入 CRC32 校验器
• 若 crc_result != 0xC704DD7B(Magic Number)→ 丢弃报文,fcs_err_cnt++
• 丢弃的报文不写入 PktBuf
• 输出 GMII → TX 侧不重新计算 FCS(透传原始 FCS 或由下游处理)
> 说明:store-and-forward 模式下转发的是包含原始 FCS 的完整报文。若需要 FCS 刷新,下游 MAC IP 自行处理。
6.4 转发时序
RX: | Preamble | SFD | MAC Header | IP Header | Payload | FCS |
←────────── 存入 PktBuf ──────────→
↓ wpkt_push
PktBuf 短暂同步延迟
↓ rpkt_pop
TX: | Preamble | SFD | MAC Header | IP Header | Payload | FCS |
• 延迟范围:1~2 个最大包周期(约 12~24μs @ 1.5KB / 125MHz)
• 存入整包 → wpkt_push → rpkt_pop → 读出整包转发
• Preamble / SFD 不存入缓存,由出向 GMII TX 模块重新生成
6.5 丢弃机制
核心原理:利用 package_fifo.v 的 block_mode 特性——wpkt_push 是数据块提交到读侧队列的唯一入口,不 push 则数据不可见。
时序流程:
GMII RX: | DstMAC | SrcMAC | EtherType | IP Header | Payload | FCS |
↓ ↓ ↓ ↓ ↓ ↓
PktBuf: 写addr=0 写addr=1 写addr=2 ... ... 写addr=N-1
↓
Parser: 开始解析 EtherType → IP Header → DstIP, DstPort
↓
Hash: CRC32(HashKey)
↓
Lookup: 并行比较4路
↓
Decision: → Pass/Drop
↓ ↓
wpkt_push: (Pass) 1 (Drop) 0
关键时序说明:
1. 边收边写:GMII RX 字节流到达即写入 PktBuf,wen=1 持续有效,waddr 递增。此时 FCS 校验和 Hash 查表均在并行进行中,尚不知道处理结果。
2. 先写后判:报文接收完毕(gmii_rx_dv 下降沿)后,Hash 查表结果已经稳定(查表仅 3 拍延迟,远小于最小报文 64B 的传输时间 512ns),可以获得 Pass/Drop 决策。
3. push 即放行:若 DstIP/DstPort 匹配白名单规则 → wpkt_push=1(需同时满足 FCS 校验通过),PktBuf 将当前数据块提交到读侧参数 FIFO,后续 rpkt_pop 读出转发。
4. 不 push 即丢弃:若匹配黑名单规则(action=Drop)或 FCS 校验失败 → wpkt_push=0。当前 block_waddr_pt 不递增,下一个报文将从同一数据块的起始地址覆盖写入,已被写入的废弃数据自然消失。
非 IPv4 报文:跳过 Hash 查表,直接 wpkt_push=1 透传。
极端情况:最小报文 64B → 64 × 8ns = 512ns = 64 个时钟周期,远远大于 Hash 查表的 3 个周期延迟。因此查表决策必然在报文接收完成之前就已得出,不需要等待。wpkt_push 的时机完全由报文边界决定,不存在查表延迟导致 push 延迟的问题。
---
7. 分片处理
7.1 分片识别
IPv4 Header 中的分片相关字段:
| 字段 | 位宽 | 位置 | 说明 |
|------|------|------|------|
| Identification | 16b | IP Header[18:19] | 分片组 ID |
| Flags[MF] | 1b | IP Header[20].bit5 | 0=最后一片 |
| Fragment Offset | 13b | IP Header[20:21] | 8-byte 为单位 |
分片判断逻辑:
• Offset=0 && MF=0:非分片报文,正常查表
• Offset=0 && MF=1:首片,查表并记录决策
• Offset>0:后续分片,查 FragCache 获取决策
7.2 分片状态缓存(FragCache)
| 项目 | 规格 |
|------|------|
| 容量 | 4 条并发分片流 |
| 索引 Key | {SrcIP[31:0], DstIP[31:0], Identification[15:0]} = 80b |
| 存储内容 | {Key, Action, Timestamp} |
| 超时 | 1ms(125,000 cycles @125MHz) |
| 替换策略 | 满时淘汰最旧 entry |
| 超时默认 | Drop |
7.3 分片处理流程
报文到达 → 解析 IP 头分片字段
↓
┌── Offset=0 && MF=0 ──→ 正常查表流程
│
├── Offset=0 && MF=1 ──→ Hash Lookup → 记录 {SrcIP, DstIP, ID, Action} 到 FragCache
│ ↓
│ 按查表结果 Pass/Drop
│
└── Offset>0 ──→ 查 FragCache {SrcIP, DstIP, ID}
↓
┌── Hit ──→ 按记录 Action 处理,刷新 Timestamp
└── Miss ─→ Drop(首片已超时或从未到达)
---
8. 寄存器映射
8.1 LCPU 地址空间
| 地址偏移 | 名称 | 类型 | 位宽 | 说明 |
|----------|------|------|------|------|
| 0x00 | HASH_CMD | WO | 32 | [1:0] cmd_type,写即触发执行 |
| 0x04 | HASH_CMD_KEY_LO | WO | 32 | DstIP[31:0] |
| 0x08 | HASH_CMD_KEY_HI | WO | 32 | [16] action, [15:0] DstPort |
| 0x0C | HASH_MASK | RW | 32 | [1] mask_port, [0] mask_ip |
| 0x10 | HASH_DEFAULT_POLICY | RW | 32 | [0] default_action(0=Pass, 1=Drop) |
| 0x14 | HASH_STATUS | RO | 32 | [2] not_found, [1] table_full, [0] busy |
| 0x18 | PKT_CNT_TOTAL | RO | 32 | 接收报文总数(不含 FCS 错误包) |
| 0x1C | PKT_CNT_HIT | RO | 32 | Hash 表命中计数 |
| 0x20 | PKT_CNT_MISS | RO | 32 | Hash 表未命中计数 |
| 0x24 | PKT_CNT_FCS_ERR | RO | 32 | FCS 校验错误计数 |
| 0x28 | OVERFLOW_CNT | RO | 32 | 表项 insert 失败(4 路全满)次数 |
8.2 寄存器详细描述
HASH_CMD (0x00, WO)
┌──────────┬─────┬─────┐ │ reserved │ cmd │ │ │ [31:2] │[1:0]│ │ └──────────┴─────┴─────┘ cmd: 00=NOP, 01=INSERT, 10=DELETE, 11=MODIFY
写入任何命令字触发状态机执行。执行期间 STATUS.busy = 1。
HASH_CMD_KEY_LO (0x04, WO)
┌────────────────────┐ │ DstIP │ │ [31:0] │ └────────────────────┘
HASH_CMD_KEY_HI (0x08, WO)
┌──────────┬────────┬────────────────────┐ │ reserved │ action │ DstPort │ │ [31:17] │ [16] │ [15:0] │ └──────────┴────────┴────────────────────┘
• DELETE 命令无需 action 字段(忽略)
• action 仅 INSERT 和 MODIFY 有效
HASH_MASK (0x0C, RW)
┌──────────┬───────────┬──────────┐ │ reserved │ mask_port │ mask_ip │ │ [31:2] │ [1] │ [0] │ └──────────┴───────────┴──────────┘ 1 = 参与 Hash, 0 = 屏蔽 默认: 2'b11(完整 Key 匹配)
> 注意:修改 HASH_MASK 将改变所有 Hash 计算的索引和 Tag。运行中修改 Mask 后,之前插入的所有表项将无法被命中(Hash 索引已变)。应在修改 Mask 前将所有表项 valid 清零,修改后重新下发。建议在系统初始化时配置 Mask 后不再变更。
HASH_DEFAULT_POLICY (0x10, RW)
┌──────────┬─────────┐ │ reserved │ default │ │ [31:1] │ [0] │ └──────────┴─────────┘ default: 0=Pass, 1=Drop 用于 Hash 未命中时的默认动作
HASH_STATUS (0x14, RO)
┌──────────┬───────────┬────────────┬──────┐ │ reserved │ not_found │ table_full │ busy │ │ [31:3] │ [2] │ [1] │ [0] │ └──────────┴───────────┴────────────┴──────┘ busy: 命令执行中 table_full: INSERT 时 4 路全满 not_found: DELETE/MODIFY 时目标表项不存在
读后自动清零(clear-on-read),busy 除外。
---
9. 时钟与复位
9.1 时钟方案
| 时钟 | 频率 | 驱动模块 |
|------|------|----------|
| clk_125m | 125MHz | 全局统一时钟:GMII、数据通路、Hash 表、LCPU 总线 |
• 单时钟域设计,GMII RX/TX 和 LCPU 共享同一 125MHz 时钟
• 无跨时钟域 CDC 逻辑需求
• SDC 约束:create_clock -name clk_125m -period 8.000 [get_ports clk_125m]
9.2 复位
• reset_l 异步复位、同步释放
• 复位后:
• 所有表项 valid = 0(空表)
• 统计计数器归零
• FragCache 清空
• 寄存器恢复默认值
---
10. PktBuf 配置
例化 linkreal_common/ip/package_fifo.v,参数如下:
| 参数 | 取值 | 说明 |
|------|------|------|
| dual_clock | 0 | 单时钟域 |
| block_mode | "true" | 块模式,整包写入/整包读出 |
| addr_width | 11 | 每块 2048 × 8b(可容纳 1522B) |
| block_addr_width | 2 | 4 个数据块 |
| data_width | 8 | GMII 字节流接口 |
| para_width | 16 | 附参位宽 |
| max_pkt_length | 1522 | 最大报文长度 |
| data_ram_type | "M9K" | Cyclone IV M9K Block RAM |
| para_ram_type | "registers" | 参数 FIFO 用寄存器实现 |
---
11. 统计计数器
| 计数器 | 位宽 | 说明 | 饱和处理 |
|--------|------|------|----------|
| pkt_cnt_total | 32 | 接收的合法报文总数(FCS 通过,含 IPv4 和非 IPv4) | 饱和不翻转 |
| pkt_cnt_hit | 32 | Hash 查表命中次数 | 饱和不翻转 |
| pkt_cnt_miss | 32 | Hash 查表未命中次数 | 饱和不翻转 |
| pkt_cnt_fcs_err | 32 | FCS 校验错误包数 | 饱和不翻转 |
| overflow_cnt | 32 | 表项插入失败次数 | 饱和不翻转 |
所有计数器 readable,CPU 读后不自动清除(如需清除需增加 CLEAR 命令或在后续版本中扩展)。
---
12. 资源预估
以下为 Cyclone IV E(EP4CE10)平台参考估算:
| 资源项 | 参考用量 | 说明 |
|--------|----------|------|
| Logic Elements | ~2500 LEs | CRC32 + 比较器 + 控制状态机 |
| Block RAM (M9K) | ~12 个 | PktBuf: 8K×8b=64Kb ≈ 8 M9K;Hash Table: 128×78b ≈ 2 M9K(双口);FragCache: 4×80b ≈ 1 M9K |
| DSP Blocks | 0 | 无可乘累加 |
| fMAX | >150MHz | 关键路径:4-way 并行比较器 + MUX |
> 注:以上为粗略估算,以实际综合结果为准。
---
13. 参数汇总
| 参数 | 默认值 | 说明 |
|------|--------|------|
| TABLE_SIZE | 128 | 表项总数 |
| TABLE_WAYS | 4 | 组相联路数 |
| TABLE_ROWS | 32 | Hash 索引行数 |
| HASH_KEY_WIDTH | 48 | Hash key 位宽 |
| HASH_TAG_WIDTH | 28 | Hash tag 位宽(CRC32[31:5]) |
| HASH_IDX_WIDTH | 5 | Hash 索引位宽(CRC32[4:0]) |
| GMII_DATA_WIDTH | 8 | GMII 数据位宽 |
| LCPU_DATA_WIDTH | 32 | LCPU 数据位宽 |
| LCPU_ADDR_WIDTH | 32 | LCPU 地址位宽 |
| MAX_PKT_LENGTH | 1522 | 最大报文长度 (Bytes) |
| FRAG_CACHE_ENTRIES | 4 | 分片缓存条数 |
| FRAG_TIMEOUT_CYCLES | 125000 | 分片超时 (cycles @125MHz = 1ms) |
| DEFAULT_POLICY | 0 (Pass) | 默认策略 |
---
14. 附录
14.1 目录结构(参考 WebServer 项目)
HashSearch/
├── doc/
│ └── ip_hash.md ← 本设计规格文档
├── rtl/
│ ├── ip_hash_top.v # 顶层集成
│ ├── ip_hash_gmii_rx.v # GMII RX 接口
│ ├── ip_hash_gmii_tx.v # GMII TX 接口
│ ├── ip_hash_parser.v # 报文解析 + Key 提取
│ ├── ip_hash_crc32.v # CRC32 Hash 计算
│ ├── ip_hash_table.v # 4-way Hash 表 + 并行比较
│ ├── ip_hash_table_mgr.v # 表项管理状态机
│ ├── ip_hash_frag_cache.v # 分片状态缓存
│ ├── ip_hash_lcpu_if.v # LCPU 总线接口 + 寄存器
│ ├── ip_hash_decision.v # Pass/Drop 决策 + 统计
│ └── ip_hash_pkg.sv # 参数 / typedef 定义
├── sim/
│ ├── tb_ip_hash.sv # 顶层 Testbench
│ ├── define.sv # 仿真定义
│ ├── filelist.f # 文件列表
│ ├── run_questa.do # Questa 仿真脚本
│ └── pkt_gen/ # 测试激励(报文模板)
├── par_intel/
│ ├── ip_hash.qpf # Quartus 工程
│ ├── ip_hash.qsf # Quartus 约束
│ └── ip_hash.sdc # 时序约束
└── par_xilinx/
├── (预留)
└── ...
14.2 待定/未来扩展
• [ ] 每表项独立命中统计(按需后续添加)
• [ ] 支持背靠背报文的 Ping-Pong Buffer
• [ ] 支持多端口队列(多个 GMII 出向)
• [ ] 支持 IPv6 报文的五元组过滤
• [ ] 表项读回功能(Debug 用)
• [ ] 统计计数器清零命令