%# -*- coding: utf-8-unix -*-

\chapter{FAST软件编程入门指南}
\label{app:fast}

\subsection{前言}

本文档的主要阅读对象是首次使用FAST平台进行软件开发的工作人员。
文档主要描述了FAST架构软件的主要功能、如何获取软件代码、
介绍软件架构与对应代码结构、编译运行FAST代码。
详细介绍了FAST开发的主要使用函数，
描述了用户如何使用开发函数进行自己代码的开发流程，
最后提供一个完整的开发示例说明，供初学者学习使用。

\subsection{FAST能做什么}

FAST平台的主要功能是软硬件配合的网络分组处理功能，
硬件的主要作用是解析报文，对报文进行查表匹配，
进行指定的动作处理，然后分发报文。
报文分发可以直接转发到硬件端口，也可以送到CPU端的UA进程再处理，
UA处理完成后可以再通过硬件转发从端口输出。
软件的主要功能有两点：
一是对硬件的流表与对应动作进行配置，指定报文从硬件端口转发或送到CPU端的UA；
二是处理硬件无法处理的报文，处理完成后再转发到硬件输出。

FAST软件的编程主要包括UA编程、规则管理编程和寄存器访问控制编程。
UA编程主要处理硬件送到指定软件的分组数据；
规则管理编程主要是对硬件流表的读写管理；
寄存器访问控制编程主要是对硬件各寄存器资源的访问控制。
对硬件规则的读写与对硬件其他寄存器的读写需要了解FAST架构的虚拟地址空间定义。

\subsection{FAST软件获取}
\label{subsec:app:fast_get-soft}

FAST的软硬件代码和相关工具下载请查看文章结尾的百度网盘链接。

\subsection{FAST代码结构}

\subsubsection{目录结构}

\renewcommand{\arraystretch}{1.5}
\begin{table}[!ht]
  \small
  \centering
  \caption{以太网帧格式}
  \label{tab:c:wireshark_eth-format}
  \begin{tabular}{|c|c|c|} \hline
    \heiti 序号 & \heiti 目录名称 & \heiti 说明\\ \hline
    1 & include & 开发所用头文件目录\\ \hline
    2 & lib & ua、寄存器读写库目录\\ \hline
    4 & app & fast应用工具\\ \hline
    6 & tools & 调试测试示例工具\\ \hline
    7 & src & 综合示例代码测试\\ \hline
  \end{tabular}
\end{table}

\subsubsection{头文件}

\begin{enumerate}
  \item Fast\_vaddr.h：FAST平台硬件资源访问对应的虚拟地址空间定义
  \item Fast\_type.h：FAST平台所用数据类型定义
  \item Fast\_err.h：FAST平台错误号定义
  \item Fast\_struct.h：FAST平台数据结构类型定义
  \item Fast.h：FAST平台所有开发函数定义，包含上述所有头文件
  \item 其他：不同平台的适配头文件
\end{enumerate}

\subsubsection{开发示例}

开发示例提供了一个基于FAST架构的2层交换机实例，
综合使用了libreg和libua两个库实现了一个全软件的2层交换机功能。

\begin{enumerate}
  \item L2switch

  L2switch是基于FAST架构开发一个2层以太网交换机原型，
  通过对流表默认规则的设置，将硬件所有的分组重定向到UA程序，
  然后在UA的分组处理回调函数中实现对分组的2层交换，
  然后将分组从指定端口发送或泛洪发送。
  该示例主要演示了UA编程开发的主要方法与流程、使用的相关函数，
  硬件寄存器地址的操作，以及2层交换机的实现原理。

\item xofp

  xofp是基于FAST架构开发一个SDN交换机原型，
  通过OpenFlow协议连接SDN控制器，SDN交换机默认规则是送往SDN控制器，
  SDN控制器根据报文数据，配置流表转发下发给硬件，下次进来的报文直接转发输出。
  该示例主要演示了SDN交换机OpenFLow协议、处理OpenFLow消息、
  使用的相关函数，硬件寄存器地址的操作，以及SDN交换机的实现原理。
\end{enumerate}

\subsubsection{工具}

示例程序的另一种表现就是FAST提供的一些小工具，包括单独的寄存器读写命令、规则输出命令、端口状态显示命令和端口分组计数命令。

\begin{enumerate}
  \item Reg\_rw

  该工具主要是使用libreg库实现一个对用户指定的寄存器进行读写访问操作，展示了用户如何调用libreg的相关库函数进行寄存器访问操作的方法。
  \item Rule\_hw

  该工具主要是使用libreg库和librule库实现对硬件流表的获取并分组输出操作，展示了用户如何调用librule库的相关库函数进行规则操作。该工具目前只使用了硬件规则的读取操作，更多规则的功能操作可参考综合测试程序
  \item Port\_status

  该工具主要使用libreg库实现了对各端口的协商状态、链路状态等寄存器的访问并进行各状态值的解释。主要展示了不同端口状态的寄存器地址如何计算，各状态寄存器值返回如何解释，示例代码中包含了目前支持的所有状态寄存器及对应功能说明
  \item Port\_counts

  该工具主要使用libreg库实现了对各端口分组计数的统计显示，主要展示了不同功能计数统计的寄存器虚拟地址空间定义，各端口对应功能寄存器的计算方法。示例代码中包含了目前支持的所有功能计数统计寄存器定义与说明
\end{enumerate}

\subsubsection{综合测试}

综合测试程序包含了libreg、librule和libua库的综合开发示例功能。
首先，用户可以通过此程序了解各功能库的主要使用函数及调用顺序；
其次，用户可以通过此程序的编译参数了解不同功能库如何添加对应库功能支持

subsection{FAST代码运行}

\subsubsection{软硬件环境}
FAST平台由湖南新实网络科技有限公司提供销售、售后与技术支持。
该平台可通过采购或免费申请试用获取，用户拿到FAST平台后，
默认系统环境均已支持FAST硬件基础版本与FAST软件基础版本。
FAST硬件基础版本是指基于FAST的标准5组流水线功能；
FAST软件基础版本是指FAST－XXX软件版本。

如果用户只需要了解软件功能，可以不用FAST设备，
FAST软件可在通用Linux系统中进行编译与测试，
没有硬件平台的软件代码，不能进行硬件寄存器读写、规则配置与报文收发功能。

\subsubsection{代码获取}
参考第\ref{subsec:app:fast_get-soft}节

\subsubsection{代码编译}
\begin{enumerate}
  \item 解压代码
  \begin{code}[console]
    openbox@OpenBox~/hnxs$ tar –zxf fast-0.4.5.tar.gz
    openbox@OpenBox~/hnxs$ cd fast-0.4.5
    openbox@OpenBox~/hnxs/fast-0.4.5$ ls
    aclocal.m4  config.guess   config.sub    include     ltmain.sh    NEWS
    app         config.h       configure     INSTALL     Makefile     README
    AUTHORS     config.h.in    configure.ac  install-sh  Makefile.am  src
    ChangeLog   config.log     COPYING       lib         Makefile.in  stamp-h1
    compile     config.status  depcomp       libtool     missing      tools
  \end{code}
  \item 配置环境
  \begin{code}[console]
    openbox@OpenBox~/hnxs/fast-0.4.5$ ./configure
    checking for a BSD-compatible install... /usr/bin/install -c
    checking whether build environment is sane... yes
    checking for a thread-safe mkdir -p... /bin/mkdir -p
    checking for gawk... gawk
    checking whether make sets $(MAKE)... yes
    checking whether make supports nested variables... yes
    checking whether make supports nested variables... (cached) yes
    checking for gcc... gcc
    checking whether the C compiler works... yes
    checking for C compiler default output file name... a.out
    checking for suffix of executables...
    checking whether we are cross compiling... no
    checking for suffix of object files... o
    checking whether we are using the GNU C compiler... yes
    checking whether gcc accepts -g... yes
    checking for gcc option to accept ISO C89... none needed
    checking whether gcc understands -c and -o together... yes
    checking for style of include used by make... GNU
    checking dependency style of gcc... gcc3
    checking whether NLS is requested... yes
    checking for intltool >= 0.35.0... 0.50.2 found
    checking for intltool-update... /usr/bin/intltool-update
    checking for intltool-merge... /usr/bin/intltool-merge
    checking for intltool-extract... /usr/bin/intltool-extract
    checking for xgettext... /usr/bin/xgettext
    checking for msgmerge... /usr/bin/msgmerge
    checking for msgfmt... /usr/bin/msgfmt
    checking for gmsgfmt... /usr/bin/msgfmt
    checking for perl... /usr/bin/perl
    checking for perl >= 5.8.1... 5.18.2
    checking for XML::Parser... ok
    checking how to run the C preprocessor... gcc -E
    checking for grep that handles long lines and -e... /bin/grep
    checking for egrep... /bin/grep -E
    checking for ANSI C header files... yes
    checking for sys/types.h... yes
    checking for sys/stat.h... yes
    checking for stdlib.h... yes
    checking for string.h... yes
    checking for memory.h... yes
    checking for strings.h... yes
    checking for inttypes.h... yes
    checking for stdint.h... yes
    checking for unistd.h... yes
    checking locale.h usability... yes
    checking locale.h presence... yes
    checking for locale.h... yes
    checking for LC_MESSAGES... yes
    checking libintl.h usability... yes
    checking libintl.h presence... yes
    checking for libintl.h... yes
    checking for ngettext in libc... yes
    checking for dgettext in libc... yes
    checking for bind_textdomain_codeset... yes
    checking for msgfmt... (cached) /usr/bin/msgfmt
    checking for dcgettext... yes
    checking if msgfmt accepts -c... yes
    checking for gmsgfmt... (cached) /usr/bin/msgfmt
    checking for xgettext... (cached) /usr/bin/xgettext
    checking build system type... x86_64-unknown-linux-gnu
    checking host system type... x86_64-unknown-linux-gnu
    checking how to print strings... printf
    checking for a sed that does not truncate output... /bin/sed
    checking for fgrep... /bin/grep -F
    checking for ld used by gcc... /usr/bin/ld
    checking if the linker (/usr/bin/ld) is GNU ld... yes
    checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
    checking the name lister (/usr/bin/nm -B) interface... BSD nm
    checking whether ln -s works... yes
    checking the maximum length of command line arguments... 1572864
    checking whether the shell understands some XSI constructs... yes
    checking whether the shell understands "+="... yes
    checking how to convert x86_64-unknown-linux-gnu file names to x86_64-unknown-linux-gnu format... func_convert_file_noop
    checking how to convert x86_64-unknown-linux-gnu file names to toolchain format... func_convert_file_noop
    checking for /usr/bin/ld option to reload object files... -r
    checking for objdump... objdump
    checking how to recognize dependent libraries... pass_all
    checking for dlltool... no
    checking how to associate runtime and link libraries... printf %s\n
    checking for ar... ar
    checking for archiver @FILE support... @
    checking for strip... strip
    checking for ranlib... ranlib
    checking command to parse /usr/bin/nm -B output from gcc object... ok
    checking for sysroot... no
    checking for mt... mt
    checking if mt is a manifest tool... no
    checking for dlfcn.h... yes
    checking for objdir... .libs
    checking if gcc supports -fno-rtti -fno-exceptions... no
    checking for gcc option to produce PIC... -fPIC -DPIC
    checking if gcc PIC flag -fPIC -DPIC works... yes
    checking if gcc static flag -static works... yes
    checking if gcc supports -c -o file.o... yes
    checking if gcc supports -c -o file.o... (cached) yes
    checking whether the gcc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
    checking whether -lc should be explicitly linked in... no
    checking dynamic linker characteristics... GNU/Linux ld.so
    checking how to hardcode library paths into programs... immediate
    checking whether stripping libraries is possible... yes
    checking if libtool supports shared libraries... yes
    checking whether to build shared libraries... yes
    checking whether to build static libraries... yes
    checking that generated files are newer than configure... done
    configure: creating ./config.status
    config.status: creating Makefile
    config.status: creating include/Makefile
    config.status: creating libreg/Makefile
    config.status: creating libua/Makefile
    config.status: creating librule/Makefile
    config.status: creating po/Makefile.in
    config.status: creating src/Makefile
    config.status: creating tools/Makefile
    config.status: creating tools/port_counts/Makefile
    config.status: creating tools/port_status/Makefile
    config.status: creating tools/rule/Makefile
    config.status: creating tools/reg_rw/Makefile
    config.status: creating tools/debug/Makefile
    config.status: creating examples/Makefile
    config.status: creating examples/l2switch/Makefile
    config.status: creating config.h
    config.status: executing depfiles commands
    config.status: executing default-1 commands
    config.status: executing libtool commands
    config.status: executing po/stamp-it commands
  \end{code}
  \item 编译
  \begin{code}[console]
    openbox@OpenBox~/hnxs/fast-0.4.5$ make
    make  all-recursive
    make[1]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5'
    Making all in include
    make[2]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/include'
    make[2]: 没有什么可以做的为 `all'。
    make[2]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/include'
    Making all in libreg
    make[2]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/libreg'
      CC       main_libreg.o
      AR       libreg.a
    make[2]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/libreg'
    Making all in libua
    make[2]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/libua'
      CC       main_libua.o
      AR       libua.a
    make[2]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/libua'
    Making all in librule
    make[2]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/librule'
      CC       main_librule.o
      AR       librule.a
    make[2]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/librule'
    Making all in tools
    make[2]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/tools'
    Making all in port_counts
    make[3]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/tools/port_counts'
      CC       main_port_counts.o
      CCLD     port_counts
    make[3]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/tools/port_counts'
    Making all in port_status
    make[3]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/tools/port_status'
      CC       main_port_status.o
      CCLD     port_status
    make[3]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/tools/port_status'
    Making all in rule
    make[3]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/tools/rule'
      CC       main_rule.o
      CCLD     rule
    make[3]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/tools/rule'
    Making all in reg_rw
    make[3]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/tools/reg_rw'
      CC       main_reg_rw.o
      CCLD     reg_rw
    make[3]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/tools/reg_rw'
    Making all in debug
    make[3]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/tools/debug'
      CC       main_debug.o
      CCLD     debug
    make[3]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/tools/debug'
    make[3]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/tools'
    make[3]: 没有什么可以做的为 `all-am'。
    make[3]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/tools'
    make[2]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/tools'
    Making all in po
    make[2]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/po'
    make[2]: 没有什么可以做的为 `all'。
    make[2]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/po'
    Making all in src
    make[2]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/src'
      CC       fast-main.o
      CCLD     fast
    make[2]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/src'
    Making all in examples
    make[2]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/examples'
    Making all in l2switch
    make[3]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/examples/l2switch'
      CC       main_l2switch.o
      CCLD     l2switch
    make[3]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/examples/l2switch'
    make[3]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5/examples'
    make[3]: 没有什么可以做的为 `all-am'。
    make[3]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/examples'
    make[2]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5/examples'
    make[2]: 正在进入目录 `/home/openbox/hnxs/fast-0.4.5'
    make[2]: 没有什么可以做的为 `all-am'。
    make[2]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5'
    make[1]:正在离开目录 `/home/openbox/hnxs/fast-0.4.5'
  \end{code}
\end{enumerate}

\subsubsection{代码执行}
\begin{enumerate}
  \item 读硬件版本号
  \begin{code}[console]
    openbox@OpenBox~/hnxs/fast-0.4.5$  reg_rw rd 0
  \end{code}
\end{enumerate}

\subsection{FAST数据结构}
\subsubsection{UM数据结构}
\begin{code}[c]
  struct um_metadata{
    #if defined(__LITTLE_ENDIAN) /*INTER*/
    #ifdef FAST_10
      u64 ts:32,      /**< @brief 报文接收的时间戳 @note 如果用户需要使用表示更大的时间，建议存储在第二拍数据中（user[2]字段）*/
        ts2:12,
        flowID:14,    /**< @brief 流ID号*/
        priority:3,    /**< @brief 报文优先级*/
        discard:1,    /**< @brief 指示报文是否丢弃 @note 默认为0，表示不丢弃，置1时表示丢弃*/
        pktdst:1,    /**< @brief 报文的输出目的方向 @note 0表示输出到网络端口，1表示输出到CPU*/
        pktsrc:1;    /**< @brief 报文的输入源方向 @note 0表示网络端口输入，1表示从CPU输入*/
      u64 outport:16,    /**< @brief 报文输出端口号 @note 以bitmap形式表示，1表示从0号端口输出；8表示从3号端口输出*/
        seq:12,      /**< @brief 报文接收时的序列号 @note 每个端口独立维护一个编号*/
        dstmid:8,    /**< @brief 报文下次处理的目的模块编号*/
        srcmid:8,    /**< @brief 报文上次处理时的模块编号*/
        len:12,      /**< @brief 报文长度 @note 最大可表示4095字节，但FAST平台报文缓存区最大为2048，完整以太网报文的MTU不要超过1500*/
        inport:4,    /**< @brief 输入端口号 @note 取值：0——15，最多表示16个输入端口*/
        ttl:4;      /**< @brief 报文通过模块的TTL值，每过一个处理模块减1*/
    #elif FAST_20
      u64 ts:32,      /**< @brief 时间戳*/
        reserve:17,    /**< @brief 保留*/
        pktsrc:1,    /**< @brief 分组的来源，0为网络接口输入，1为CPU输入，此比特位在进入硬件时会交换到pkttype位置，保留位为18位*/
        flowID:14;    /**< @brief 流ID*/
      u64  seq:8,      /**< @brief 分组接收序列号*/
        pst:8,      /**< @brief 标准协议类型（参考硬件定义）*/
        dstmid:8,      /**< @brief 下一个处理分组的模块ID*/
        srcmid:8,      /**< @brief 最近一次处理分组的模块ID*/
        len:12,      /**< @brief 报文长度*/
        discard:1,    /**< @brief 丢弃位*/
        priority:3,    /**< @brief 分组优先级*/
        outport:6,    /**< @brief 单播：分组输出端口ID，组播/泛洪：组播/泛洪表地址索引*/
        outtype:2,    /**< @brief 输出类型，00：单播，01：组播，10：泛洪，11：从输入接口输出*/
        inport:6,    /**< @brief 分组的输入端口号*/
        pktdst:1,    /**< @brief 分组目的，0为网络接口输出，1为送CPU*/
        pkttype:1;    /**< @brief 报文类型，0：数据报文，1：控制报文。硬件识别报文类别后，会将pktsrc位交换到此，恢复硬件数据格式*/
    #endif
      u64 user[2];    /**< @brief 用户自定义metadata数据格式与内容 @remarks 此字段由可用户改写，但需要保证数据大小严格限定在16字节*/
    #elif defined(__BIG_ENDIAN)  /**/
      #error  "(__BIG_ENDIAN)Please fix <asm/byteorder.h>"
    #else
      #error  "Please fix <asm/byteorder.h>"
    #endif
    };
\end{code}
\subsubsection{FAST报文数据结构}
\begin{code}[c]
  struct fast_packet
  {
    union
    {
      struct um_metadata um;    /**< @brief UM模块数据格式定义 @see ::um_metadata */
      struct ctl_metadata cm;    /**< 控制报文格式定义*/
      struct common_metadata md;  /**< 公共控制信息，报文类型（0：数据，1：控制）*/
    };
  #ifdef OPENBOX_S28
    u16 flag;          /**< @brief 2字节对齐标志，主要是为了IP地址的对齐 @note 此标志在内核会用来标记是一个FAST类型报文，建议用户不使用些字段*/
  #endif
    u8 data[1514];        /**< @brief 完整以太网报文数据，暂时不含CRC数据*/
  };
\end{code}
\subsubsection{流关键字数据结构}
\begin{code}[c]
  struct flow /*2017/06/01开始启用，支持IPv6*/
  {
    u8  dmac[ETH_ALEN];  /**< @brief Ethernet source address. */
    u8  smac[ETH_ALEN];  /**< @brief Ethernet destination address. */
    u16 tci;      /**< @brief 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
    u16 type;      /**< @brief Ethernet frame type. */
    //eth-16;

    u8  proto;      /**< @brief IP protocol or lower 8 bits of ARP opcode. */
    u8  tos;      /**< @brief IP ToS. */
    u8  ttl;      /**< @brief IP TTL/hop limit. */
    u8  port:6,      /**< @brief Input Port*/
      frag:2;      /**< @brief One of OVS_FRAG_TYPE_*. */
    //ip=16+4;

    union {
      struct {
        u32 src;  /**< @brief IP source address. */
        u32 dst;  /**< @brief IP destination address. */
        //addr;

        union {
          struct {
            u16 sport;    /**< @brief TCP/UDP/SCTP source port. */
            u16 dport;    /**< @brief TCP/UDP/SCTP destination port. */
            u16 flags;    /**< @brief TCP flags. */
          } tp;        /**< @brief IPv4的传输层端口与标志信息*/
          struct {
            u8 sha[ETH_ALEN];  /**< @brief ARP source hardware address. */
            u8 tha[ETH_ALEN];  /**< @brief ARP target hardware address. */
          } arp;          /**< @brief ARP的MAC地址信息*/
        };              /**< @brief IPv4传输层信息与ARP信息共用体，二者互斥存在*/
      } ipv4;              /**< @brief IPv4协议相关字段*/
      struct {
        struct in6_addr src;  /**< @brief IPv6 source address. */
        struct in6_addr dst;  /**< @brief IPv6 destination address. */
        //addr;

        u32 label;        /**< @brief IPv6 flow label. */
        union {
          struct {
            u16 sport;    /**< @brief TCP/UDP/SCTP source port. */
            u16 dport;    /**< @brief TCP/UDP/SCTP destination port. */
            u16 flags;    /**< @brief TCP flags. */
          } tp;        /**< @brief IPv6的传输层端口与标志信息*/
          /*512位宽表项不够，暂不使用ND协议中的targetIP!!!为方便以后扩展，将其移至末尾*/
  #if 0
          struct {
            u8 sll[ETH_ALEN];  /**< @brief ND source link layer address. */
            u8 tll[ETH_ALEN];  /**< @brief ND target link layer address. */
            struct in6_xaddr target;  /**< @brief ND target address. */
          } nd;
  #endif
        };            /**< @brief IPv6传输层信息与ND信息共用体，二者互斥存在*/
        u16 nil;
      } ipv6;            /**< @brief IPv6协议相关字段*/
    };
  };
\end{code}
\subsubsection{流数据结构}
\begin{code}[c]
  struct fast_rule{
    struct flow key;    /**< @brief 规则内容存储结构 @see ::flow*/
    struct flow mask;    /**< @brief 规则掩码设置结构,与key一一对应 @see ::flow*/
    u32 priority;      /**< @brief 规则的优先级设置*/
    u32 valid;        /**< @brief 规则的有效标志设置,要删除一条规则只需要将此标记置0,并更新到硬件*/
    u32 action;        /**< @brief 规则所对应的执行动作，动作由两部分组成，高4位为ACTION枚举类型，低28位不此类型对应的值 @see ::ACTION*/
    /*此前数据必须要写入到硬件*/
    u32 flow_stats_len;    /**< @brief 流表信息长度*/
    u64 *flow_stats_info;   /**< @brief 流表信息存储指针位置*/
  #ifdef OPENBOX_S4
    u32 *tmp;        /*在S4系统中，上面变量指针大小为32*/
  #endif
    u64 cookie;        /**< @brief 用来存储流的cookie信息，软件使用*/
    u64 cookie_mask;    /**< @brief 用来存储流cookie的掩码信息，软件使用*/
    u32 md5[4];        /**< @brief MD5值 @note 用来区分规则的唯一性，软件生成规则时即会判断是否规则重复*/
    u32 pad[18];      /**< @brief 总长256B，此为确保数据结构大小做的填充*/
  };
\end{code}

\subsection{FAST开发函数}

\subsubsection{硬件资源与寄存器}
\begin{enumerate}
  \item int fast\_init\_hw(u64 addr,u64 len);
  硬件资源初始化。所有涉及到需要操作硬件寄存器、流表和动作等均需要先调用此函数，
  实例化硬件资源，使软件具体访问硬件的能力。

  参数：addr，在PCIe标准设备中表示硬件资源的内存映射地址，
  通常情况下，用户可以直接填写0。

  参数：len，在PCIe标准调和中表示硬件资源的内存地址空间大小，
  通常情况下，用户可以直接填写0。

  返回值：0表示硬件资源实例化正常，非零表示失败，
  如果失败将无法对硬件进行任何操作，程序将自动退出。
  \item void fast\_distroy\_hw(void);
  硬件资源销毁。PCIe设备主要是撤销对硬件资源的映射，
  OpenBox-S4主要是对设备发送关闭报文。
  一般情况下，用户程序退出时应该调用此函数，释放资源。
  但是我们内部并没有做特殊的资源处理，均为系统可自动回收功能，
  即使用户不调用此函数，当程序正常退出时，系统也能正常回收资源，
  不影响程序再次运行使用。
  \item u64 fast\_reg\_rd(u64 regaddr);
  寄存器读函数。读取用户指定寄存器位置的值返回。
  该函数内部会根据平台的不同使用不同的方法去访问硬件寄存器。
  PCIe的平台以正常寄存器方式访问，而NetMagic~08需要使用NMAC报文进行交互获取。
  这些不同的操作方式均已对用户屏蔽，用户无需关心该函数底层如何交互与实现，
  仅需调用此函数接口来操作寄存器读操作即可。

  参数：regaddr，表示用户将要读取的寄存器地址，
  该地址即FAST平台所定义的虚拟地址空间，该值均使用相对偏移值的方式使用，
  即FAST平台定义的值，对PCIe平台设备访问需要使用绝对地址访问才可以正常访问寄存器，
  相对地址转绝对地址已经封装在函数内部，
  用户对所有寄存器的操作均可只用相对值访问即可。

  返回值：返回该寄存器对应的值。该值的位宽是64位，
  在所有FAST平台寄存器的位宽均为64位。
  \item void fast\_reg\_wr(u64 regaddr,u64 regvalue);
  寄存器写函数。将指定值写对硬件对应的寄存器位置。
  该函数内部会根据平台的不同使用不同的方法去操作硬件寄存器。
  PCIe的平台以正常寄存器方式访问，而NetMagic~08需要使用NMAC报文进行交互获取。
  这些不同的操作方式均已对用户屏蔽，用户无需关心该函数底层如何交互与实现，
  仅需调用此函数接口来操作寄存器写操作即可。

  参数：regaddr，表示用户将要写入的寄存器地址，
  该地址即FAST平台所定义的虚拟地址空间，该值均使用相对偏移值的方式使用，
  即FAST平台定义的值，
  对PCIe平台设备访问需要使用绝对地址访问才可以正常访问寄存器，
  相对地址转绝对地址已经封装在函数内部，
  用户对所有寄存器的操作均可只用相对值访问即可。

  参数：regvalue，表示要写入上述参数寄存器位置的值。该值的位宽为64位。
\end{enumerate}
\subsubsection{流表操作}
\begin{enumerate}
  \item void init\_rule(u32 default\_action);
  初始化规则。初始化规则函数的主要功能是在软件分配软件规则空间，
  将硬件规则空间清零，设置硬件规则的默认动作。
  该函数只在进行流表操作的应用中进行调用，而且只能被调用一次。
  如果有多个应用需要操作规则，
  整个系统在关机或重启前所有的应用中也只能由一个应用来调用一次。
  主要是该函数会将硬件所有存在流表清空。用户需要自己非常清楚对流表的操作，
  在一次流表清空后才可以对流表进行其他操作。

  参数：default\_action，默认动作。当所有的流表均不能匹配时，
  硬件需要执行一个默认的动作来完成这个分组的处理，
  流表可以为空，但默认动作必须要有。详细的动作含义可以参考动作说明。

  \item int fast\_add\_rule(struct fast\_rule *rule);
  添加一条规则。添加规则函数，先将用户的规则添加在软件流表中，
  经过各种比较判断后，看是否能正常添加，有无冲突或是否存在，
  如果可以正常添加，则先将规则存储在软件，
  然后将此条软件规则数据通过寄存器方式下发到硬件对应规则的存储位置。

  参数：rule，规则对象，内部具体字段参考流规则数据结构定义

  返回值：返回当前添加规则成功后此条规则所在的索引号
  \item int fast\_modify\_rule(struct fast\_rule *rule,int idx);
  修改一条已经存在的规则。此函数主要是用来修改已经存在的规则，
  对其关键字、掩码、动作和优先级等进行修改。
  该函数的索引值参数如果指向一条空规则，其操作流程将类似于添加规则函数。

  参数：rule，新规则对象，内部具体字段参考流规则数据结构定义

  参数：idx，旧规则所在索引号

  返回值：返回输入参数的idx值说明操作成功，其他值说明操作失败
  \item int fast\_del\_rule(int idx);
  删除一条已经存储的规则，或将一条规则置为无效。
  只需要提供规则所在的索引号，软件可通过索引号定位到对应硬件存储规则位置，
  将规则的有效位置为无效，实现一条规则的删除。
  删除硬件成功后，将其对应的软件规则进行清除。

  参数：idx，删除规则所在索引号

  返回值：返回输入参数的idx值说明操作成功，其他值说明操作失败
  \item int read\_hw\_rule(struct fast\_rule *rule,int index);
  读取一条硬件规则。将存储在硬件规则内对应索引的数据读取出来，
  还原为一条软件规则数据，存储在用户提供的指针位置。

  参数：rule，将要存储规则的内存指针

  参数：idx，将要读取规则所在硬件的索引号

  返回值：返回输入参数的idx值说明操作成功，其他值说明操作失败
  \item void print\_hw\_rule(void);
  打印硬件所有规则信息。此函数是上一个读取硬件规则条目信息的集合操作，
  将所有硬件流规则信息一一读出，并打印输出在屏幕上。
\end{enumerate}
\subsubsection{UA编程}
\begin{enumerate}
  \item int fast\_ua\_init(int mid,fast\_ua\_recv\_callback callback);
  UA编程初始化函数。
  \item void fast\_ua\_destroy(void);
  UA编程注销函数。
  \item int fast\_ua\_send(struct fast\_packet *pkt,int pkt\_len);
  UA程序发送报文函数
  \item void fast\_ua\_recv();
  启动UA接收用户特征流程序函数
\end{enumerate}

\subsection{FAST虚拟地址空间定义}

\subsubsection{debug um}
\begin{code}[c]
  #define FAST_DEBUG_UM_DC   FAST_HW_UM+0x0000
  #define FAST_DEBUG_UM_GPP   FAST_HW_UM+0x2000
  #define FAST_DEBUG_UM_GKE   FAST_HW_UM+0x4000
  #define FAST_DEBUG_UM_GME   FAST_HW_UM+0x6000
  #define FAST_DEBUG_UM_GAC   FAST_HW_UM+0x8400
  #define FAST_DEBUG_UM_GOE   FAST_HW_UM+0xA000

  /*GPP模块*/
  #define FAST_DEBUG_GPP_GPP_STATUS         FAST_DEBUG_UM_GPP+0x00
  #define FAST_DEBUG_GPP_IN_GPP_DATA_COUNT   FAST_DEBUG_UM_GPP+0x08
  #define FAST_DEBUG_GPP_OUT_GPP_MD_COUNT      FAST_DEBUG_UM_GPP+0x10
  #define FAST_DEBUG_GPP_OUT_GPP_PHY_COUNT   FAST_DEBUG_UM_GPP+0x18
  #define FAST_DEBUG_GPP_OUT_GPP_CACHE_COUNT  FAST_DEBUG_UM_GPP+0x20

  /*GKE模块*/
  #define FAST_DEBUG_GKE_GKE_STATUS         FAST_DEBUG_UM_GKE+0x00
  #define FAST_DEBUG_GKE_IN_GKE_MD_COUNT      FAST_DEBUG_UM_GKE+0x08
  #define FAST_DEBUG_GKE_IN_GKE_PHY_COUNT      FAST_DEBUG_UM_GKE+0x10
  #define FAST_DEBUG_GKE_OUT_GKE_MD_COUNT      FAST_DEBUG_UM_GKE+0x18
  #define FAST_DEBUG_GKE_OUT_GKE_PHY_COUNT   FAST_DEBUG_UM_GKE+0x20
  #define FAST_DEBUG_GKE_OUT_GKE_KEY_COUNT   FAST_DEBUG_UM_GKE+0x28

  /*GME*/
  #define FAST_DEBUG_GME_GME_STATUS         FAST_DEBUG_UM_GME+0x00
  #define FAST_DEBUG_GME_IN_GME_MD_COUNT      FAST_DEBUG_UM_GME+0x08
  #define FAST_DEBUG_GME_IN_GME_PHY_COUNT      FAST_DEBUG_UM_GME+0x10
  #define FAST_DEBUG_GME_IN_GME_KEY_COUNT      FAST_DEBUG_UM_GME+0x18
  #define FAST_DEBUG_GME_IN_GME_INDEX_COUNT   FAST_DEBUG_UM_GME+0x20
  #define FAST_DEBUG_GME_OUT_GME_MD_COUNT      FAST_DEBUG_UM_GME+0x28
  #define FAST_DEBUG_GME_OUT_GME_PHY_COUNT   FAST_DEBUG_UM_GME+0x30
  #define FAST_DEBUG_GME_OUT_GME_KEY_COUNT   FAST_DEBUG_UM_GME+0x38

  /*GAC*/
  #define FAST_DEBUG_GAC_GAC_STATUS         FAST_DEBUG_UM_GAC+0x00
  #define FAST_DEBUG_GAC_IN_GAC_DATA_COUNT   FAST_DEBUG_UM_GAC+0x08
  #define FAST_DEBUG_GAC_IN_GAC_MD_COUNT      FAST_DEBUG_UM_GAC+0x10
  #define FAST_DEBUG_GAC_IN_GAC_PHY_COUNT      FAST_DEBUG_UM_GAC+0x18
  #define FAST_DEBUG_GAC_OUT_GAC_DATA_COUNT   FAST_DEBUG_UM_GAC+0x20
  #define FAST_DEBUG_GAC_OUT_GAC_PHY_COUNT   FAST_DEBUG_UM_GAC+0x28

  /*GOE*/
  #define FAST_DEBUG_GOE_GOE_STATUS         FAST_DEBUG_UM_GOE+0x00
  #define FAST_DEBUG_GOE_IN_GOE_DATA_COUNT   FAST_DEBUG_UM_GOE+0x08
  #define FAST_DEBUG_GOE_IN_GOE_PHY_COUNT      FAST_DEBUG_UM_GOE+0x10
  #define FAST_DEBUG_GOE_OUT_GOE_DATA_COUNT   FAST_DEBUG_UM_GOE+0x18


  #define FAST_DEBUG_DC_DATA_CACHE_STATUS      FAST_DEBUG_UM_DC+0x00
  #define FAST_DEBUG_DC_IN_DATA_CACHE_COUNT   FAST_DEBUG_UM_DC+0x08
  #define FAST_DEBUG_DC_OUT_DATA_CACHE_COUNT  FAST_DEBUG_UM_DC+0x10
\end{code}
\subsubsection{端口计数}
\begin{code}[c]
  #define FAST_COUNTS_RECV_BYTE_L      0x200   /**< 计数寄存器：成功接收字节数低位计数*/
  #define FAST_COUNTS_RECV_BYTE_H      0x204   /**< 计数寄存器：成功接收字节数高位计数*/
  #define FAST_COUNTS_SEND_BYTE_L      0x208   /**< 计数寄存器：成功发送字节数低位计数*/
  #define FAST_COUNTS_SEND_BYTE_H      0x20C   /**< 计数寄存器：成功发送字节数高位计数*/

  #define FAST_COUNTS_RECV_PKT_L      0x290   /**< 计数寄存器：接收成功的计数*/
  #define FAST_COUNTS_RECV_PKT_H      0x294   /**< 计数寄存器：接收成功的计数*/
  #define FAST_COUNTS_SEND_PKT_L      0x2D8   /**< 计数寄存器：发送成功的计数*/
  #define FAST_COUNTS_SEND_PKT_H      0x2DC   /**< 计数寄存器：发送成功的计数*/
\end{code}
\subsubsection{硬件流表}
\begin{code}[c]
  #define FAST_OFP_FLOW_STATS_PKTS     0x8B000        /*流表报文计数*/
  #define FAST_OFP_FLOW_STATS_BYTES   0x8B800      /*流表字节计数*/
\end{code}
\subsubsection{硬件动作}
\begin{code}[c]
  #define BASE_ADDR      0x43C00000  /**< 硬件资源起始地址*/
  #define REG_LEN      0x01000000   /**< 硬件资源可用空间*/
  #define FAST_ACTION_REG_ADDR 0x88000   /**< 写动作寄存器*/
  #define FAST_DEFAULT_RULE_ADDR 0x1F8   /**< 默认动作(匹配不到规则时执行默认动作)地址*/
\end{code}

\subsection{FAST开发流程}
基于FAST的软件代码开发有三种方式：
第一种是基于FAS的源代码进行修改，整个代码结构不变，
可以直接使用原工程中的所有Makefile文件，
修改完成后，直接make即可得到用户所需要的可执行文件；
第二种是用户根据自己需要，选择需要的开发库与头文件，
自己重新组织新的代码目录与结构进行开发，
原工程中的Makefile文件均不可再用，需要自己重写Makefile文件。

\subsubsection{修改源码开发}

\begin{enumerate}
  \item 已FAST的rule\_rw工具作为修改示例，
  修改fast/tools/rule\_rw/目录下main\_rule.c文件
  \item 修改main\_rule\_test函数，
  将两个端口互转硬件规则改为根据以太网类型来转发
  \begin{code}[c]
    void main_rule_test(int argc,char *argv[])
    {
      int i = 0,p1 = 1,p2 = 2,idx1 = 0,idx2 = 1;
      struct fast_rule rule[2] = {{0},{0}};//初始化两条全空的规则

      if(argc == 3)
      {
        p1 = atoi(argv[1]);
        p2 = atoi(argv[2]);
      }
      else if(argc == 4)
      {
        p1 = atoi(argv[1]);
        p2 = atoi(argv[2]);
        idx1 = atoi(argv[3]);
      }
      else if(argc == 5)
      {
        p1 = atoi(argv[1]);
        p2 = atoi(argv[2]);
        idx1 = atoi(argv[3]);
        idx2 = atoi(argv[4]);
      }

      //给规则的各字段赋值
      rule[i].key.type = htole16(0x0800); //以太网类型
      rule[i].key.port = p1;
      rule[i].priority =0xA;
      rule[i].action = ACTION_PORT<<28|p2;//动作字段的涵义请参考fast_type.h
      rule[i].md5[0] = i+1;

      //给规则对应字段设置掩码，掩码为1表示使用，为0表示忽略
      rule[i].mask.type = 0xFFFF;
      rule[i].mask.port = 0x3F;/*6bit*/

      //fast_add_rule(&rule[i]);
      //fast_modify_rule(&rule[i],0);

      i++;

      rule[i].key.type = htole16(0x0800);
      rule[i].key.port = p2;
      rule[i].priority =0xB;
      rule[i].action = ACTION_PORT<<28|p1;//动作字段的涵义请参考fast_type.h
      rule[i].md5[0] = i+1;

      //给规则对应字段设置掩码，掩码为1表示使用，为0表示忽略
      rule[i].mask.type = 0xFFFF;
      rule[i].mask.port = 0x3F;

      //fast_add_rule(&rule[i]);
      //fast_modify_rule(&rule[i],1);


      if(argc == 3 || argc == 1)
      {
        fast_modify_rule(&rule[i-1],idx1);
        fast_modify_rule(&rule[i],idx2);
        printf("Row[%d],Port[%d]----->Port[%d]\n",idx1,p1,p2);
        printf("Row[%d],Port[%d]----->Port[%d]\n",idx2,p2,p1);
      }
      else if(argc == 4)
      {
        p1 = atoi(argv[1]);
        p2 = atoi(argv[2]);
        idx1 = atoi(argv[3]);
        fast_modify_rule(&rule[i-1],idx1);
        printf("Row[%d],Port[%d]----->Port[%d]\n",idx1,p1,p2);
      }
      else if(argc == 5)
      {
        p1 = atoi(argv[1]);
        p2 = atoi(argv[2]);
        idx1 = atoi(argv[3]);
        idx2 = atoi(argv[4]);
        fast_modify_rule(&rule[i-1],idx1);
        fast_modify_rule(&rule[i],idx2);
        printf("Row[%d],Port[%d]----->Port[%d]\n",idx1,p1,p2);
        printf("Row[%d],Port[%d]----->Port[%d]\n",idx2,p2,p1);
      }
    }
  \end{code}

  修改保存退出，打开命令终端，跳到fast目录下，执行交叉编译

  \begin{code}[console]
    # cd fast-0.4.5
    # make
  \end{code}

  编译完成后将把tools/rule\_hw目录下rule\_hw工具放到OpenBox-S4设备，
  执行修改过的rule\_hw工具
  \begin{code}[console]
    # ./rule_hw 0 1
  \end{code}

\end{enumerate}

\subsubsection{新增代码开发}

用户基于FAST架构代码增加自己的软件代码，
建议按照完整项目工程管理的方法进行，
使用Anjuta开发软件进行修改和配置，
具体请参考《OpenBox-S4开发环境搭建手册》第三章anjuta 部署fast源代码。

\subsubsection{交叉编译代码}
OpenBox-S4平台为ARM嵌入式平台，
所运行软件需要通过交叉编译后才可放入平台运行。
软件代码的交叉编译请参考《OpenBox-S4开发环境搭建手册》第二章的2.4节测试交叉编译。