本文基于Synopsys DesignWare Cores Ethernet Quality-of-Service Databook第3章和第6章,深入讲解VLAN功能的硬件实现、寄存器配置、以及报文处理流程。
一、DWC Ethernet QoS VLAN功能概述
1.1 支持的VLAN功能
DWC_ether_qos MAC支持全面的VLAN处理功能:
发送路径(Tx)功能:
- VLAN标签插入(Insertion)
- VLAN标签替换(Replacement)
- VLAN标签删除(Deletion)
- 双层VLAN(QinQ)处理
- 基于队列/通道的VLAN标签插入
接收路径(Rx)功能:
- VLAN标签剥离(Stripping)
- VLAN过滤(Filtering)
- 双层VLAN标签解析
- VLAN优先级到队列映射
- VLAN过滤失败队列
1.2 VLAN处理的核心寄存器
| 寄存器名称 | 功能描述 |
|---|---|
| MAC_VLAN_Tag | VLAN标签控制和状态 |
| MAC_VLAN_Incl | 外层VLAN标签插入/替换/删除控制 |
| MAC_Inner_VLAN_Incl | 内层VLAN标签插入/替换/删除控制 |
| MAC_VLAN_Hash_Table | VLAN哈希过滤表 |
| MAC_VLAN_Data | VLAN过滤数据 |
二、双层VLAN处理(Double VLAN Processing)
2.1 功能使能
要启用双层VLAN处理功能,需要在coreConsultant配置时选择:
Enable Double VLAN Processing
配置位置:General Features → Enable Double VLAN Processing
2.2 发送路径功能详解
2.2.1 功能列表
| 功能 | 描述 | 控制位 |
|---|---|---|
| Outer/Inner VLAN Tag Insertion | 插入外层或内层VLAN标签 | VLC字段 |
| C-VLAN/S-VLAN Tag Insertion | 插入C-VLAN或S-VLAN类型标签 | VLT字段 |
| Outer/Inner VLAN Tag Replacement | 替换外层或内层VLAN标签 | VLC字段 |
| Outer/Inner VLAN Tag Deletion | 删除外层或内层VLAN标签 | VLC字段 |
| VLAN Tag from Register or Control Word | VLAN标签来源选择 | VLTI位 |
2.2.2 VLAN标签操作模式
MAC_VLAN_Incl寄存器的VLC字段:
| VLC值 | 操作模式 | 说明 |
|---|---|---|
| 00 | 无操作 | 不处理VLAN标签 |
| 01 | 删除标签 | 删除指定位置的VLAN标签 |
| 10 | 插入标签 | 在指定位置插入VLAN标签 |
| 11 | 替换标签 | 替换指定位置的VLAN标签 |
2.2.3 VLAN标签来源
VLTI位(VLAN Tag Type Insert):
- VLTI = 0:VLAN标签从寄存器获取(MAC_VLAN_Tag)
- VLTI = 1:VLAN标签从控制字(Control Word)获取
控制字方式的优势:
- 每个包可以使用不同的VLAN标签
- 更灵活的动态配置
- 适合多VLAN场景
2.3 接收路径功能详解
2.3.1 功能列表
| 功能 | 描述 | 控制位 |
|---|---|---|
| Outer/Inner VLAN Tag-based Filtering | 基于外层或内层VLAN标签过滤 | ERIVLT位 |
| C-VLAN/S-VLAN Tag-based Filtering | 基于C-VLAN或S-VLAN类型过滤 | ERSVLM位 |
| Outer/Inner VLAN Tag Stripping | 剥离外层或内层VLAN标签 | EVLS/EIVLS位 |
| 16-bit VLAN Tag in Rx Status | 在接收状态中提供VLAN标签 | EVLRXS/EIVLRXS位 |
| Disable Outer VLAN Tag Type Check | 禁用外层VLAN类型检查 | DOVLTC位 |
2.3.2 VLAN标签剥离模式
EVLS(Outer VLAN Tag Stripping)字段:
| EVLS值 | 剥离模式 | 说明 |
|---|---|---|
| 00 | 不剥离 | 保留VLAN标签 |
| 01 | 剥离 | 剥离外层VLAN标签 |
| 1x | 保留 | 保留VLAN标签 |
EIVLS(Inner VLAN Tag Stripping)字段:
- 配置方式与EVLS相同
- 用于内层VLAN标签剥离
三、VLAN标签插入、替换、删除的实现
3.1 寄存器配置详解
3.1.1 MAC_VLAN_Incl寄存器结构
Bit 31-16: VLT (VLAN Tag Type) - VLAN标签类型
0x8100: C-VLAN (Customer VLAN)
0x88A8: S-VLAN (Service VLAN)
Bit 15: VLTI (VLAN Tag Type Insert) - VLAN标签来源
0: 从寄存器获取
1: 从控制字获取
Bit 14-13: VLC (VLAN Tag Control) - VLAN标签操作
00: 无操作
01: 删除
10: 插入
11: 替换
Bit 12-0: VLT (VLAN Tag) - 13位VLAN标签值
3.1.2 MAC_Inner_VLAN_Incl寄存器
结构与MAC_VLAN_Incl相同,用于内层VLAN标签处理。
3.2 实现示例
3.2.1 插入外层VLAN标签
场景:在发送帧中插入VLAN ID=100,优先级=5
c
// 方法1:使用寄存器配置
void insert_outer_vlan_from_register() {
// 配置VLAN标签值
uint32_t vlan_tag = (5 << 13) | 100; // Priority=5, VID=100
// 配置MAC_VLAN_Tag寄存器
MAC_VLAN_Tag = vlan_tag;
// 配置MAC_VLAN_Incl寄存器
MAC_VLAN_Incl = (0x8100 << 16) | // TPID = 0x8100 (C-VLAN)
(0 << 15) | // VLTI = 0 (从寄存器获取)
(2 << 13) | // VLC = 10 (插入)
vlan_tag; // VLAN标签
}
// 方法2:使用控制字
void insert_outer_vlan_from_control_word() {
// 配置MAC_VLAN_Incl寄存器
MAC_VLAN_Incl = (0x8100 << 16) | // TPID = 0x8100
(1 << 15) | // VLTI = 1 (从控制字获取)
(2 << 13); // VLC = 10 (插入)
// 在发送描述符的控制字中设置VLAN标签
Tx_Descriptor.Control_Word = (5 << 13) | 100;
}
3.2.2 替换VLAN标签
场景:将现有VLAN标签替换为新的VLAN ID=200
c
void replace_outer_vlan() {
uint32_t new_vlan_tag = (3 << 13) | 200; // Priority=3, VID=200
// 配置MAC_VLAN_Tag寄存器
MAC_VLAN_Tag = new_vlan_tag;
// 配置MAC_VLAN_Incl寄存器
MAC_VLAN_Incl = (0x8100 << 16) | // TPID = 0x8100
(0 << 15) | // VLTI = 0 (从寄存器获取)
(3 << 13) | // VLC = 11 (替换)
new_vlan_tag;
}
3.2.3 删除VLAN标签
场景:删除帧中的VLAN标签
c
void delete_outer_vlan() {
// 配置MAC_VLAN_Incl寄存器
MAC_VLAN_Incl = (0 << 15) | // VLTI = 0
(1 << 13); // VLC = 01 (删除)
}
3.2.4 双层VLAN(QinQ)处理
场景:插入双层VLAN标签
c
void insert_qinq_tags() {
// 外层VLAN (S-VLAN): VID=100, Priority=7
uint32_t outer_vlan = (7 << 13) | 100;
// 内层VLAN (C-VLAN): VID=10, Priority=3
uint32_t inner_vlan = (3 << 13) | 10;
// 配置外层VLAN
MAC_VLAN_Tag = outer_vlan;
MAC_VLAN_Incl = (0x88A8 << 16) | // S-VLAN TPID
(0 << 15) | // 从寄存器获取
(2 << 13) | // 插入
outer_vlan;
// 配置内层VLAN
MAC_Inner_VLAN_Tag = inner_vlan;
MAC_Inner_VLAN_Incl = (0x8100 << 16) | // C-VLAN TPID
(0 << 15) | // 从寄存器获取
(2 << 13) | // 插入
inner_vlan;
}
3.3 SystemVerilog VIP实现
3.3.1 构造VLAN帧并配置插入
systemverilog
class vlan_insert_sequence extends svt_ethernet_sequence;
`uvm_object_utils(vlan_insert_sequence)
task body();
svt_ethernet_transaction trans;
trans = svt_ethernet_transaction::type_id::create("trans");
// 设置为普通数据帧(VIP会根据配置插入VLAN)
trans.command = ETH_MAC_DATA_FRAME;
// 设置MAC地址
trans.address = 48'h00_11_22_33_44_55;
trans.source = 48'h66_77_88_99_AA_BB;
// 设置载荷
trans.payload = new[100];
foreach(trans.payload[i])
trans.payload[i] = $urandom;
// 发送帧
start_item(trans);
trans.randomize();
finish_item(trans);
endtask
endclass
// 配置VIP插入VLAN标签
class test_env extends uvm_env;
function void build_phase(uvm_phase phase);
svt_ethernet_agent_configuration cfg;
cfg = svt_ethernet_agent_configuration::type_id::create("cfg");
// 配置VLAN插入
cfg.tx_vlan_insert_enable = 1'b1;
cfg.tx_vlan_tpid = 16'h8100;
cfg.tx_vlan_id = 12'd100;
cfg.tx_vlan_priority = 3'h5;
uvm_config_db#(svt_ethernet_agent_configuration)::set(
this, "agent", "cfg", cfg);
endfunction
endclass
3.3.2 构造带VLAN的帧并配置替换
systemverilog
class vlan_replace_sequence extends svt_ethernet_sequence;
`uvm_object_utils(vlan_replace_sequence)
task body();
svt_ethernet_transaction trans;
trans = svt_ethernet_transaction::type_id::create("trans");
// 构造已有VLAN标签的帧
trans.command = ETH_MAC_VLAN_FRAME;
trans.address = 48'h00_11_22_33_44_55;
trans.source = 48'h66_77_88_99_AA_BB;
// 原始VLAN标签
trans.vlan_id = 12'd10;
trans.vlan_priority = 3'h3;
trans.payload = new[100];
foreach(trans.payload[i])
trans.payload[i] = $urandom;
start_item(trans);
trans.randomize();
finish_item(trans);
endtask
endclass
// 配置VIP替换VLAN标签
class test_env extends uvm_env;
function void build_phase(uvm_phase phase);
svt_ethernet_agent_configuration cfg;
cfg = svt_ethernet_agent_configuration::type_id::create("cfg");
// 配置VLAN替换
cfg.tx_vlan_replace_enable = 1'b1;
cfg.tx_vlan_tpid = 16'h8100;
cfg.tx_vlan_id = 12'd200; // 新的VLAN ID
cfg.tx_vlan_priority = 3'h7; // 新的优先级
uvm_config_db#(svt_ethernet_agent_configuration)::set(
this, "agent", "cfg", cfg);
endfunction
endclass
四、VLAN过滤功能详解
4.1 VLAN过滤模式
DWC_ether_qos支持两种VLAN过滤模式:
4.1.1 哈希过滤模式
特点:
- 使用哈希表进行VLAN ID过滤
- 支持最多64个VLAN条目
- 适合VLAN数量较多的场景
配置方法:
c
// 启用VLAN过滤
MAC_Packet_Filter.VTFE = 1; // VLAN Tag Filter Enable
// 配置VLAN哈希表
void config_vlan_hash_table() {
// 允许VLAN 10, 20, 30, 40
uint16_t allowed_vlans[] = {10, 20, 30, 40};
for(int i = 0; i < 4; i++) {
// 计算哈希值
uint32_t hash_index = crc32(allowed_vlans[i]) % 64;
// 设置哈希表位
MAC_VLAN_Hash_Table |= (1 << hash_index);
// 存储VLAN数据
MAC_VLAN_Data[hash_index] = allowed_vlans[i];
}
}
4.1.2 完全过滤模式
特点:
- 检查所有VLAN字段(VID、PCP、DEI)
- 更精确的过滤控制
- 适合需要严格VLAN匹配的场景
4.2 VLAN过滤失败处理
4.2.1 VLAN Filter Fail Packets Queue
功能:VLAN过滤失败的帧可以路由到特定队列,而不是直接丢弃。
配置寄存器:
- MAC_RxQ_Ctrl4.VFFQE:VLAN Filter Fail Queue Enable
- MAC_RxQ_Ctrl4.VFFQ:VLAN Filter Fail Queue编号
配置示例:
c
void config_vlan_filter_fail_queue() {
// 启用VLAN过滤失败队列
MAC_RxQ_Ctrl4.VFFQE = 1;
// 设置失败队列编号为7
MAC_RxQ_Ctrl4.VFFQ = 7;
// 配置RA位(Receive All)
MAC_Packet_Filter.RA = 1; // 接收所有帧,包括过滤失败的帧
}
应用场景:
- 监控异常流量:捕获不符合VLAN规则的帧
- 调试和诊断:分析VLAN配置问题
- 安全审计:记录潜在的VLAN攻击
4.3 扩展VLAN过滤和路由
4.3.1 基于VLAN优先级的队列映射
功能:根据VLAN标签的PCP字段将帧路由到不同队列。
配置方法:
c
void config_vlan_priority_routing() {
// PCP 0-1 → Queue 0 (Best Effort)
MAC_RxQ_Ctrl2.PSRQ[0] = 0x0003; // Bit 0-1
// PCP 2-3 → Queue 1 (Excellent Effort)
MAC_RxQ_Ctrl2.PSRQ[1] = 0x000C; // Bit 2-3
// PCP 4-5 → Queue 2 (Video)
MAC_RxQ_Ctrl3.PSRQ[2] = 0x0030; // Bit 4-5
// PCP 6-7 → Queue 3 (Voice)
MAC_RxQ_Ctrl3.PSRQ[3] = 0x00C0; // Bit 6-7
}
4.3.2 双层VLAN过滤
功能:基于外层或内层VLAN标签进行过滤。
配置方法:
c
void config_double_vlan_filter() {
// 启用双层VLAN处理
MAC_VLAN_Tag.EDVLP = 1; // Enable Double VLAN Processing
// 启用内层VLAN过滤
MAC_VLAN_Tag.ERIVLT = 1; // Enable Receive Inner VLAN Tag
// 启用S-VLAN/C-VLAN过滤
MAC_VLAN_Tag.ERSVLM = 1; // Enable Receive S-VLAN Mode
}
五、基于队列/通道的VLAN标签插入
5.1 功能描述
DWC_ether_qos支持为不同的发送队列配置不同的VLAN标签,实现基于队列的VLAN标签自动插入。
5.2 寄存器配置
相关寄存器:
- MAC_Q0_VLAN_Tag ~ MAC_Q7_VLAN_Tag:队列0-7的VLAN标签
配置示例:
c
void config_queue_based_vlan() {
// 队列0:VLAN 10
MAC_Q0_VLAN_Tag = (3'h5 << 13) | 10; // Priority=5, VID=10
// 队列1:VLAN 20
MAC_Q1_VLAN_Tag = (3'h6 << 13) | 20; // Priority=6, VID=20
// 队列2:VLAN 30
MAC_Q2_VLAN_Tag = (3'h7 << 13) | 30; // Priority=7, VID=30
// 启用队列VLAN插入
MAC_VLAN_Incl.VLC = 2; // 插入模式
MAC_VLAN_Incl.VLTI = 0; // 从寄存器获取
}
5.3 应用场景
场景1:多queue环境
Queue 0 → Tenant A → VLAN 10
Queue 1 → Tenant B → VLAN 20
Queue 2 → Tenant C → VLAN 30
场景2:QoS分类
Queue 0 → Best Effort → VLAN 100, Priority 0
Queue 1 → Video → VLAN 100, Priority 5
Queue 2 → Voice → VLAN 100, Priority 7
六、VLAN标签在描述符中的处理
6.1 发送描述符
Context描述符中的VLAN相关字段:
Bit 15: CTXT - Context Type
1 = Context描述符
Bit 16: VLTI - VLAN Tag Insert
0 = 从寄存器获取VLAN标签
1 = 从控制字获取VLAN标签
Bit 23-17: VLAN Tag - 7位VLAN标签(部分)
Normal描述符:
- 用于传输数据
- 可通过Context描述符配置VLAN处理
6.2 接收描述符
Write-back描述符中的VLAN相关字段:
Bit 15: CTXT - Context Type
0 = Normal描述符
Bit 16: DE - Descriptor End
标识描述符结束
Bit 23-17: VLAN Tag - 接收到的VLAN标签
VLAN信息提取:
c
void extract_vlan_from_rx_descriptor(rx_descriptor_t *desc) {
if(desc->CTXT == 0) { // Normal描述符
uint16_t vlan_tag = desc->VLAN_Tag;
// 解析VLAN字段
uint8_t priority = (vlan_tag >> 13) & 0x7;
uint8_t cfi = (vlan_tag >> 12) & 0x1;
uint16_t vid = vlan_tag & 0xFFF;
printf("VLAN ID: %d, Priority: %d, CFI: %d\n",
vid, priority, cfi);
}
}
七、VLAN与MACsec的交互
7.1 VLAN标签位置配置
在MACsec(IEEE 802.1AE)加密帧中,VLAN标签可以位于:
- "in clear"区域:在SECTag之前(未加密)
- "not in clear"区域:在SECTag之后(加密)
配置方法:
c
void config_vlan_position_in_macsec() {
// VLAN标签在SECTag之后(加密)
MAC_VLAN_Incl.VLAN_IN_CLEAR = 0;
// VLAN标签在SECTag之前(未加密)
MAC_VLAN_Incl.VLAN_IN_CLEAR = 1;
}
7.2 应用场景
场景1:VLAN标签加密
- VLAN标签在SECTag之后
- 整个帧(包括VLAN)被加密
- 提供更强的安全性
场景2:VLAN标签明文
- VLAN标签在SECTag之前
- VLAN信息可见
- 便于网络设备处理VLAN
八、VLAN统计功能
8.1 统计寄存器
DWC_ether_qos提供VLAN相关的统计信息:
| 统计项 | 寄存器 | 说明 |
|---|---|---|
| VLAN过滤通过帧数 | MMC_TX_VLAN_PKT_OK | 成功通过VLAN过滤的帧 |
| VLAN过滤失败帧数 | MMC_RX_VLAN_PKT_FILT | VLAN过滤失败的帧 |
| VLAN标签帧总数 | MMC_TX_VLAN_PKT_GB | 发送的VLAN标签帧总数 |
8.2 统计读取示例
c
void read_vlan_statistics() {
uint64_t vlan_ok = MMC_TX_VLAN_PKT_OK;
uint64_t vlan_fail = MMC_RX_VLAN_PKT_FILT;
uint64_t vlan_total = MMC_TX_VLAN_PKT_GB;
printf("VLAN Statistics:\n");
printf(" Filter Pass: %llu\n", vlan_ok);
printf(" Filter Fail: %llu\n", vlan_fail);
printf(" Total VLAN Frames: %llu\n", vlan_total);
printf(" Pass Rate: %.2f%%\n",
(float)vlan_ok / vlan_total * 100);
}
九、完整配置示例
9.1 场景:企业网络VLAN配置
需求:
- 研发部:VLAN 10,优先级5
- 市场部:VLAN 20,优先级6
- 财务部:VLAN 30,优先级7
- 过滤失败的帧路由到队列7
c
void enterprise_vlan_config() {
// 1. 启用VLAN过滤
MAC_Packet_Filter.VTFE = 1;
// 2. 配置VLAN过滤表
config_vlan_hash_table(); // 允许VLAN 10, 20, 30
// 3. 配置队列VLAN标签
MAC_Q0_VLAN_Tag = (5 << 13) | 10; // 研发部
MAC_Q1_VLAN_Tag = (6 << 13) | 20; // 市场部
MAC_Q2_VLAN_Tag = (7 << 13) | 30; // 财务部
// 4. 配置VLAN插入
MAC_VLAN_Incl = (0x8100 << 16) | // C-VLAN
(0 << 15) | // 从寄存器获取
(2 << 13); // 插入模式
// 5. 配置VLAN过滤失败队列
MAC_RxQ_Ctrl4.VFFQE = 1;
MAC_RxQ_Ctrl4.VFFQ = 7;
// 6. 配置优先级到队列映射
config_vlan_priority_routing();
// 7. 启用接收VLAN剥离
MAC_VLAN_Tag.EVLS = 1; // 剥离外层VLAN
}
9.2 场景:运营商QinQ配置
需求:
- 外层S-VLAN:标识运营商网络
- 内层C-VLAN:标识客户网络
- 支持双层VLAN过滤
c
void carrier_qinq_config() {
// 1. 启用双层VLAN处理
// 在coreConsultant中配置:Enable Double VLAN Processing
// 2. 配置外层S-VLAN
MAC_VLAN_Tag = (7 << 13) | 100; // S-VID=100
MAC_VLAN_Incl = (0x88A8 << 16) | // S-VLAN TPID
(0 << 15) | // 从寄存器获取
(2 << 13); // 插入模式
// 3. 配置内层C-VLAN
MAC_Inner_VLAN_Tag = (3 << 13) | 10; // C-VID=10
MAC_Inner_VLAN_Incl = (0x8100 << 16) | // C-VLAN TPID
(0 << 15) | // 从寄存器获取
(2 << 13); // 插入模式
// 4. 启用双层VLAN过滤
MAC_VLAN_Tag.EDVLP = 1; // Enable Double VLAN Processing
MAC_VLAN_Tag.ERIVLT = 1; // Enable Inner VLAN Tag filtering
MAC_VLAN_Tag.ERSVLM = 1; // Enable S-VLAN Mode
// 5. 配置接收路径
MAC_VLAN_Tag.EVLS = 1; // 剥离外层VLAN
MAC_VLAN_Tag.EIVLS = 1; // 剥离内层VLAN
MAC_VLAN_Tag.EVLRXS = 1; // 在Rx状态中提供外层VLAN
MAC_VLAN_Tag.EIVLRXS = 1; // 在Rx状态中提供内层VLAN
}
十、调试和故障排查
10.1 常见问题
问题1:VLAN标签未插入
可能原因:
- MAC_VLAN_Incl.VLC配置错误
- VLTI位配置与VLAN标签来源不匹配
- 发送描述符配置错误
排查方法:
c
void debug_vlan_insert() {
printf("MAC_VLAN_Incl: 0x%08X\n", MAC_VLAN_Incl);
printf(" VLC: %d\n", (MAC_VLAN_Incl >> 13) & 0x3);
printf(" VLTI: %d\n", (MAC_VLAN_Incl >> 15) & 0x1);
printf(" VLT: 0x%04X\n", (MAC_VLAN_Incl >> 16) & 0xFFFF);
printf("MAC_VLAN_Tag: 0x%08X\n", MAC_VLAN_Tag);
}
问题2:VLAN过滤失败
可能原因:
- VLAN ID不在过滤表中
- 哈希冲突导致误判
- VTFE位未启用
排查方法:
c
void debug_vlan_filter() {
printf("MAC_Packet_Filter: 0x%08X\n", MAC_Packet_Filter);
printf(" VTFE: %d\n", MAC_Packet_Filter & 0x1);
printf("MAC_VLAN_Hash_Table: 0x%08X\n", MAC_VLAN_Hash_Table);
printf("VLAN Filter Fail Count: %llu\n", MMC_RX_VLAN_PKT_FILT);
}
10.2 调试技巧
- 使用VLAN过滤失败队列:捕获所有异常帧
- 检查统计寄存器:分析VLAN处理性能
- 验证描述符配置:确保控制字正确
- 逐步测试:先测试单层VLAN,再测试双层VLAN
十一、总结
核心要点
- VLAN操作模式:插入、替换、删除,通过VLC字段配置
- VLAN标签来源:寄存器或控制字,通过VLTI位选择
- 双层VLAN:支持QinQ,内外层独立配置
- VLAN过滤:哈希过滤和完全过滤两种模式
- 队列映射:基于VLAN优先级或队列编号
最佳实践
- 配置顺序:先配置VLAN寄存器,再启用VLAN功能
- 错误处理:启用VLAN过滤失败队列,便于调试
- 性能优化:使用哈希过滤提高处理速度
- 安全考虑:结合MACsec使用,保护VLAN信息
参考资料
- Synopsys DesignWare Cores Ethernet Quality-of-Service Databook, Chapter 3
- Synopsys DesignWare Cores Ethernet Quality-of-Service Databook, Chapter 6
- IEEE 802.1Q - VLAN Standard
- IEEE 802.1ad - QinQ Standard
作者注:本文详细讲解了DWC Ethernet QoS MAC中VLAN功能的硬件实现,所有内容均来自官方Databook。建议结合实际硬件调试,深入理解VLAN处理流程。
关键词:DWC Ethernet QoS、VLAN寄存器、VLAN插入、VLAN替换、VLAN剥离、QinQ、VLAN过滤