P4->NetFPGA 工作流程概述
前言
结构
本页面介绍了P4-> NetFPGA工作流程的以下几个方面:
- SimpleSumeSwitch Architecture
- Xilinx P4-SDNet
- Workflow Steps
- Writing P4 Programs
- Testing P4 Programs
- Debugging P4 Programs
- P4->NetFPGA Extern Library
- API / CLI
- Limitations
SimpleSumeSwitch Architecture
SimpleSumeSwitch是目前为NetFPGA SUME定义的P4架构。 体系结构描述可以在/opt/Xilinx/SDNet/<version_number>/data/p4include/sume_switch.p4
中找到,或者您已经安装了Xilinx SDNet的地方。 该体系结构由单个解析器,单个匹配操作管道和单个解析器组成。 如下所示:
- sume_metadata: 对应于SUME reference_switch设计中的tuser总线,定义如下:
struct sume_metadata_t {
bit<16> dma_q_size; // measured in 32-byte words
bit<16> nf3_q_size; // measured in 32-byte words
bit<16> nf2_q_size; // measured in 32-byte words
bit<16> nf1_q_size; // measured in 32-byte words
bit<16> nf0_q_size; // measured in 32-byte words
bit<8> send_dig_to_cpu; // send digest_data to CPU
bit<8> drop;
port_t dst_port; // one-hot encoded: {DMA, NF3, DMA, NF2, DMA, NF1, DMA, NF0}
port_t src_port; // one-hot encoded: {DMA, NF3, DMA, NF2, DMA, NF1, DMA, NF0}
bit<16> pkt_len; // (bytes) unsigned int
}
其中:
pkt_len
- 数据包的大小(不包括FCS的以太网前缀),单位为字节。src_port
- 数据包到达的端口。 例如,如果数据包到达端口nf1,则该字段将被设置为0b00000100。dst_port
- 应由用户的P4程序设置,以指示哪个或哪些端口(如果有的话)数据包应该被发送出去。 例如,要从端口nf0和nf2发送数据包,该字段应设置为0b00010001。drop
- 如果该字段的最低有效位被设置为1,则该分组将被丢弃。send_dig_to_cpu
- 如果该字段的最低有效位设置为1,则摘要数据将通过DMA发送到CPU。_q_size
- 每个输出队列的大小,以32字节的单词来衡量(向上舍入)。 这是P4程序开始处理数据包时输出队列的大小。
- digest_data: 这个总线的格式是由P4程序员定义的。 唯一的限制是它必须被定义为80位宽。 例如,要实现一个L2学习开关,可以将digest_data总线配置如下:
struct digest_data_t {
bit<8> src_port;
bit<48> eth_src_addr;
bit<24> unused;
}
- user_metadata: 这个总线的格式也由P4程序员定义。 它可以用来将解析器的任何附加信息传递给M / A流水线,并从M / A流水线传递给解析器。
- in/out control: 这些信号用于添加/删除表和读/写控制寄存器的条目。
Xilinx P4-SDNet工具链创建一个HDL模块,然后将其封装在一个小封装中,并插入到NetFPGA SUME参考交换机架构中,如下图所示。
Xilinx P4-SDNet
赛灵思P4-SDNet编译器是P4-> NetFPGA工作流程的核心。 它将面向SimpleSumeSwitch体系结构的P4程序编译成一个具有标准AXI-Stream数据包接口和AXI-Lite控制接口的HDL模块。 SDNet输出设计为100G速率,因此能够轻松处理SUME参考开关设计中的总计40G速率。 有关赛灵思SDNet工具的更多信息,请参见此处。
Workflow Steps
1)修改$ SUME_FOLDER / tools / settings.sh
以确保将P4_PROJECT_NAME
环境变量设置为您想要处理的项目的名称。 运行$ source settings.sh
2)编写P4程序和commands.txt - commands.txt文件允许你添加条目到你在P4程序中定义的表中。
3)写 gen_testdata.py
4)运行P4-SDNet编译器生成最终的HDL和初始仿真框架:
$ cd $P4_PROJECT_DIR && make
5)运行SDNet模拟:
$ cd $P4_PROJECT_DIR/nf_sume_sdnet_ip/SimpleSumeSwitch
$ ./vivado_sim.bash
注意:如果您想启动Vivado GUI并查看HDL波形(这是一个非常有用的调试工具),您也可以运行vivado_sim_waveform.bash。
如果这个模拟通过很好! 如果没有,你将需要修改你的P4程序或你的gen_testdata.py脚本。
6)生成可在NetFPGA SUME模拟中使用的脚本来配置表条目。
$ cd $P4_PROJECT_DIR
$ make config_writes
7)在包装模块中包装SDNet输出并作为SUME库核心进行安装:
$ cd $P4_PROJECT_DIR
$ make uninstall_sdnet && make install_sdnet
8)设置SUME模拟。 $ NF_DESIGN_DIR / test / sim_switch_default目录包含负责运行SUME模拟的run.py脚本,请检查一下。 您将看到它读取由步骤3中的gen_testdata.py脚本生成的测试数据包,并将数据包应用于SUME接口。 我们所需要做的就是将步骤6中生成的config_writes.py脚本复制到这个目录中。
$ cd $NF_DESIGN_DIR/test/sim_switch_default && make
9)运行SUME模拟:
$ cd $SUME_FOLDER
$ ./tools/scripts/nf_test.py sim --major switch --minor default
注意:您也可以使用--gui选项运行上述命令来启动Vivado GUI并查看HDL波形。 再次,一个非常有用的调试工具。
10)编译比特流:
$ cd $NF_DESIGN_DIR && make
11)检查以确保设计符合时间要求。 看到这个this FAQ,看看如何做到这一点。
12)编程FPGA。 将比特流文件和config_writes.sh
脚本复制到$ NF_DESIGN_DIR / bitfiles
目录中。
$ cd $NF_DESIGN_DIR/bitfiles
$ cp ../hw/project/simple_sume_switch.runs/impl_1/top.bit ./ && mv top.bit ${P4_PROJECT_NAME}.bit
$ cp $P4_PROJECT_DIR/testdata/config_writes.sh ./
$ sudo bash
# bash program_switch.sh
注意:确保配置写入全部成功。 如果这是自上次断电以来首次对FPGA进行编程,则可能需要重新启动。
13)在真实硬件上测试设计! 转到$ P4_PROJECT_DIR / sw / CLI
目录并运行P4_SWITCH_CLI.py
脚本。 这启动了一个交互式命令行界面,您可以使用该界面与您的交换机进行交互(例如,读取/写入寄存器,添加/删除表条目等)。 输入help以查看可用命令的列表。
Writing P4 Programs
该库目前支持Xilinx P4 _ 16前端编译器请参阅此处以获取有关P4_16语言当前标准的链接。 有关开源P4语言联盟的更多信息,请访问P4.org。
赛灵思P4-SDNet指定了一些可用于P4程序的附加注释:
@Xilinx_MaxPacketRegion()
- for parser/deparser; 声明解析器/解析器需要支持的最大数据包大小(以位为单位)。@Xilinx_MaxLatency()
- 对于extern; 外部函数需要完成的最大时钟周期数。@Xilinx_ControlWidth()
- 为extern; 地址空间的大小分配给一个extern函数。@Xilinx_ExternallyConnected()
- 表格; 将表的请求和响应元组拉到设计的顶层
有关使用和编写extern函数的更多信息,请参见“P4-> NetFPGA Extern库”一节。
Table Types:在P4程序中可以使用3种表格类型:
exact
- 完全匹配指定的标题或元数据字段,然后执行所需的操作。 可以指定多个键匹配。ternary
- 完全匹配指定键的一些所需子集。lpm
- 匹配与指定key共享最长前缀匹配的表中的条目。 只能指定一个键。
Commands.txt: 这是$ P4_PROJECT_DIR / src /
目录中的文件,其中包含用于填充表条目的命令。 以下是用于填充每种类型的表中的表项的命令的格式:
Table Type | Command |
---|---|
exact |
table_cam_add_entry <table_name> <action_name> <keys (space separated)> => <action_data (space separated)> |
ternary |
table_tcam_add_entry <table_name> <entry_address> <action_name> <key1/mask1 ... keyN/maskN> => <action_data (space separated)> |
lpm |
table_lpm_add_entry <table_name> <action_name> <key>/<prefix_length> => <action_data (space separated)> |
上面列出的命令是commands.txt文件中唯一支持的命令。
注意:使用lpm表时,SDNet编译器将生成一个bash脚本(即create_ip_forward.bash
),在启动SDNet仿真之前必须运行它。
Testing P4 Programs
通过在$ P4_PROJECT_DIR / testdata
目录中写入一个gen_testdata.py
脚本来测试你的P4程序。 gen_testdata.py脚本大量使用python scapy模块。 Scapy是一个非常方便和易于使用的数据包操作库。 它允许你定义任意数据包,甚至定义你自己的头类型(例如switch_calc_headers.py
)。 gen_testdata.py
脚本必须生成:
src.pcap
- 数据包通过P4交换机dst.pcap
- 数据包在P4交换机的输出处预期Tuples_in.txt
- 与src.pcap中的每个数据包关联的metadataTuples_expect.txt
- 与dst.pcap中的每个数据包关联的metadata
上面列出的文件用于初始SDNet模拟。 文件$ P4_PROJECT_DIR / testdata / sss_sdnet_tuples.py
可以用于为针对SimpleSumeSwitch体系结构的项目创建Tuples_in.txt
和Tuples_expect.txt
文件。 对于每个数据包,更新sume_tuple_in
,dig_tuple_in
,sume_tuple_expect
和dig_tuple_expect
,随后调用write_tuples()
向Tuples _ *.txt
文件添加一行。
建议gen_testdata.py脚本也会生成:
nf0_applied.pcap
...nf3_applied.pcap
nf0_expected.pcap
...nf3_expected.pcap
这些是可以在SUME模拟中使用的文件。 该脚本还应该为每个应用数据包设置时间字段,以便可以按正确的顺序应用SUME模拟。
$ {NF_DESIGN_DIR} /test/sim_switch_default/run.py
脚本运行一个SUME模拟。 它可以使用由gen_tesdata.py
脚本生成的数据包追踪,或者可以创建新数据包并使用它们。 有关编写run.py
脚本的更多详细信息,请参阅此处。
Debugging P4 Programs
如果你做了足够的P4开发,你可能需要在某个时候进行调试。 本节旨在为您提供一些调试技巧。
SDNet产生你的P4程序的两个实现:一个HDL实现和一个C ++模型。 为了将C ++模型的输出与HDL实现的输出进行比较,请执行以下操作:
$ cd $P4_PROJECT_DIR && make_cpp_test
$ cd nf_sume_sdnet_ip/SimpleSumeSwitch/
$ ./vivado_sim.bash
这对于测试SDNet编译器的功能非常有用,因为C ++模型的输出应该始终与HDL实现的输出相匹配。 所以如果运行上面的模拟失败这可能表明SDNet编译器的问题,但它也可能表明另一个问题。 例如,也许你正在试图添加更多的条目比你的表可以容纳? 要解决这个问题,你需要在你的P4程序中增加你的表的大小。 或者,也许你正在使用的任何外部函数的C ++模型是不正确的实现?
SDNet C ++模型有用的另一个原因是因为它产生了经过处理的数据包的PCAP文件Packet_expect.pcap。 要生成C ++模型输出PCAP文件,请执行以下操作:
$ cd $P4_PROJECT_DIR && make
$ cd nf_sume_sdnet_ip/SimpleSumeSwitch/SimpleSumeSwitch.TB/
$ ./compile
$ ./SimpleSumeSwitch
生成的PCAP文件被称为Packet_expect.pcap
,可以与gen_testdata.py
脚本生成的dst.pcap文件进行比较。 运行C ++模型产生非常详细的输出,描述如何处理数据包。 例如,您可以看到整个设计中元数据字段的修改方式,以及您定义的表中是否存在命中或未命中。
一旦确定Packet_expect.pcap
文件与dst.pcap
文件相匹配,您可以运行SDNet模拟,将HDL实现输出与dst.pcap
中的数据包以及Tuple_expect.txt
中的预期元数据进行比较:
$ cd $P4_PROJECT_DIR && make
$ cd nf_sume_sdnet_ip/SimpleSumeSwitch/
$ ./vivado_sim.bash
如果运行此模拟会导致元数据错误(即期望元组与实际元组之间不匹配),则可以轻松地检查哪些字段不匹配:
$ ./vivado_sim.bash
...
expected < tuple_out_sume_metadata > = < 00000000000000000000000100040041 >
actual < tuple_out_sume_metadata > = < 00000000000000000000000100040040 >
...
$ $P4_PROJECT_DIR/testdata/sss_sdnet_tuples.py --parse 00000000000000000000000100040041 sume
Parsed Tuple:
-----------------------
dma_q_size = 0
nf3_q_size = 0
nf2_q_size = 0
nf1_q_size = 0
nf0_q_size = 0
send_dig_to_cpu = 0
drop = 1
dst_port = 00000000
src_port = 00000100
pkt_len = 65
如果SDNet模拟通过但SUME模拟失败,该怎么办?
如果你遇到这个问题,我会建议检查几件事情:
1)确保你已经运行:
$ cd $P4_PROJECT_DIR && make config_writes
$ cd $NF_DESIGN_DIR/test/sim_major_minor && make
$ cd $P4_PROJECT_DIR && make install_sdnet
2)请注意,数据包的接收顺序很重要。 如果您的nf _ * _ expected.pcap文件中的数据包包含的数据包顺序与实际收到的数据包不同,则会导致模拟失败。
3)运行SUME模拟更长时间 - 如果您看到所有预期的数据包尚未到达,则可能是问题所在。
4)确保所有配置写入在应用测试数据包之前已经完成。
5)要检查SUME模拟的实际和预期数据包,请检查$ NF_DESIGN_DIR / test / nf _ * _ expected.axi
和$ NF_DESIGN_DIR / test / nf _ * _ logged.axi
。
6)通过在$ NF_DESIGN_DIR / hw / tcl / simple_sume_switch_sim.tcland
的底部添加--gui选项来运行SUME仿真,将信号添加到仿真波形。
P4->NetFPGA Extern Library
外部功能旨在让P4程序员在其P4程序中使用自定义逻辑。 对于NetFPGA平台,这些外部函数必须用HDL(或者可能是一些生成HDL的高级语言)来实现。 为了从P4开发者中抽象出HDL细节,P4-> NetFPGA工作流提供了一个可用于P4程序的外部函数库。 有状态的原子实验是受Domino原子的启发。 下表介绍了当前支持的外部函数:
Stateful Atomic Extern Functions
Name | Description |
---|---|
RW | 读写状态 |
RAW | Read, add to, or overwrite state |
PRAW | Either perform RAW or don't perform RAW based on predicate |
ifElseRAW | Two RAWs, one each for when a predicate is true or false |
Sub | IfElseRAW with stateful subtraction capability |
IMPORTANT:每个有状态原子(即寄存器)只能在P4代码中被访问一次。 多次调用extern函数会生成原子的多个实例,您可能会得到意想不到的结果。
注:Vivado将寄存器的大小限制为1百万个条目。 所以这些有状态原子的索引宽度必须小于20位。
Stateless Extern Functions
Name | Description |
---|---|
IP Checksum | Given an IP header, compute the IP checksum |
LRC | longitudinal redundancy check, simple hash function |
timestamp | generate timestamp (measure in clock cycles, granularity of 5ns) |
Adding new extern functions:
为工作流添加新的外部函数是非常容易的。 实现extern函数,只需将条目添加到$ {SUME_SDNET} /bin/extern_data.py
文件中即可。 每个条目是一个有两个键的Python字典:
- “template_file” - 指定相对于
$ {SUME_SDNET} / templates
目录的extern函数模板的路径。 - “replacements” - 一个python字典,指定要在模板文件中替换的模式,以及替换模式的模式。 支持的命令是:
Command | Description |
---|---|
"module_name" | The name of the module. This is determined by the P4-SDNet compiler and so must be replaced in the template file. |
"extern_name" | The full name of the extern function. This is also determined by the P4-SDNet compiler and so must be replaced in the template file. |
"prefix_name" | The name given to the extern function by the P4 programmer. For example, for an extern function called pktCnt_reg_raw the "prefix_name" would be called pktCnt . |
"addr_width" | The size (in bits) of the address space allocated to the extern function. Specified by the P4 programmer using the @Xilinx_ControlWidth annotation. |
"input_width(field)" | The width (in bits) of a particular input field. |
"output_width(field)" | The width (in bits) of a particular output field. |
如果一个P4程序实例化一个控制宽度大于0的外部函数,P4-> NetFPGA工具将自动生成一个可以在外部模块中使用的<prefix_name> _cpu_regs
模块。 它的目的是使用AXI-Lite接口轻松暴露控制寄存器。
强烈建议实现新的外部函数的用户将其贡献给P4-> NetFPGA项目,以便其他人也可以使用它们。
API / CLI
python和C API都是由工作流生成的,而且可以用来操作P4程序中实例化的寄存器和表。 C API由P4-SDNet直接生成,文件被复制到$ {P4_PROJECT_DIR} / sw / API
目录中。 python API只是这些C API函数的一个包装,使开发人员可以很容易地编写python程序来控制它们的开关设计。 python API文件名为p4_tables_api.py
和p4_regs_api.py
,位于$ {P4_PROJECT_DIR} / sw / CLI
目录中。
这些工具生成一个交互式命令行环境,可用于查询有关生成的P4设计的编译时间信息,以及在物理交换机加载到FPGA时与交换机进行交互。 运行:$ ./P4_SWITCH_CLI.py进入环境,输入help查看命令列表及其文档。
Limitations
-
lpm和三元表只有轻微的测试。 为这些表类型开发项目和测试用例需要社区支持。
-
从控制面访问的寄存器必须限制在32位宽。 因为这是SUME控制数据总线的宽度。
-
所有的P4设计必须至少有一个表或外部功能与控制界面。 这是因为HDL包装模块目前假定SimpleSumeSwitch模块将具有AXI-Lite控制接口。
-
有关更多问题,请参阅github issue页面。