Vitis HLS 学习笔记--通道的FIFO/PIPO选择

目录

1. 简介

2. 代码详解

2.1 FIFO 通道示例

2.1.1 配置默认通道

2.1.2 kernel 代码 

2.1.3 综合报告

2.1.4 depth = 32 解析

2.1.5 FIFO 通道分类

2.2 PIPO

2.2.1 配置默认通道

2.2.2  kernel 代码 

2.2.3 综合报告

2.2.4 PIPO 通道分类

3. 综合对比

3.1 数据类型和接口

3.2 数据流和数据流控制

3.3 计算函数和计算方式

4. 总结


1. 简介

本文通过调整 Vitis HLS 编译器的默认设置,选择FIFO或者PIPO作为通道缓存。首先,我们来复习一下这两种缓存的区别:

想象一下,你在一家快餐店,FIFO 缓冲器就像是排队等候的顾客队伍。第一个进入队伍的顾客会是第一个得到服务的人。这个系统的好处是,顾客(生产者)一旦排队,厨师(使用者)就可以开始准备他们的食物。但如果厨师做饭的速度赶不上顾客排队的速度,或者反过来,就会造成混乱,有可能导致整个队伍停滞不前,这就是所谓的“死锁”。

现在,让我们来看看乒乓缓冲器。这就像是有两个窗口的快餐店。当一个窗口正在为顾客准备食物时,另一个窗口就在接待新的顾客。一旦一个窗口的食物准备好了,顾客就可以从那里取餐,而厨师则转到另一个窗口继续工作。这样,即使顾客来得很快,也不会有人等得太久,因为总有一个窗口是在准备食物的。

PIPO 缓冲器的工作方式类似于一个自动调节的快餐店,无论顾客来得多快,厨师都能以相同的速度做饭,确保每个人都能及时得到食物,不会有任何拥堵或延误。

在这两种情况下,无论是 FIFO 还是 PIPO,关键点都是顾客(生产者)将订单(数据块)传递给厨师(使用者)。订单可以是一个汉堡包(单个值),也可以是一整个家庭套餐(一组 N 个值)。订单越大,厨师需要的准备空间就越多。

本文内容会参照《Vitis HLS 学习笔记--控制驱动TLP-处理deadlock_vitis hls数据流处理-CSDN博客》中对于 FIFO /PIPO 类型的分类。

2. 代码详解

2.1 FIFO 通道示例

2.1.1 配置默认通道

首先配置 FIFO 缓存,可以通过如下指令进行设定:

# Create a solution
open_solution -reset solution1 -flow_target vitis
config_dataflow -default_channel fifo -fifo_depth 2

或者通过 UI 界面设置:

2.1.2 kernel 代码 

#include <hls_stream.h>
#include <hls_vector.h>extern "C" {void diamond(hls::vector<uint32_t, 16>* vecIn, hls::vector<uint32_t, 16>* vecOut, int size) {
// The depth setting is required for pointer to array in the interface.
#pragma HLS INTERFACE m_axi port = vecIn depth = 32
#pragma HLS INTERFACE m_axi port = vecOut depth = 32hls::stream<hls::vector<uint32_t, 16>> c0, c1, c2, c3, c4, c5;assert(size % 16 == 0);#pragma HLS dataflowload(vecIn, c0, size);compute_A(c0, c1, c2, size);compute_B(c1, c3, size);compute_C(c2, c4, size);compute_D(c3, c4, c5, size);store(c5, vecOut, size);
}
}void load(hls::vector<uint32_t, 16>* in, hls::stream<hls::vector<uint32_t, 16>>& out, int size) {
Loop_Ld:for (int i = 0; i < size; i++) {
#pragma HLS performance target_ti = 32
#pragma HLS LOOP_TRIPCOUNT max = 32out.write(in[i]);}
}void compute_A(hls::stream<hls::vector<uint32_t, 16>>& in, hls::stream<hls::vector<uint32_t, 16>>& out1,hls::stream<hls::vector<uint32_t, 16>>& out2, int size) {
Loop_A:for (int i = 0; i < size; i++) {
#pragma HLS performance target_ti = 32
#pragma HLS LOOP_TRIPCOUNT max = 32hls::vector<uint32_t, 16> t = in.read();out1.write(t * 3);out2.write(t * 3);}
}void compute_B(hls::stream<hls::vector<uint32_t, 16>>& in, hls::stream<hls::vector<uint32_t, 16>>& out,int size) {
Loop_B:for (int i = 0; i < size; i++) {
#pragma HLS performance target_ti = 32
#pragma HLS LOOP_TRIPCOUNT max = 32out.write(in.read() + 25);}
}void compute_C(hls::stream<hls::vector<uint32_t, 16>>& in, hls::stream<hls::vector<uint32_t, 16>>& out,int size) {
Loop_C:for (unsigned int i = 0; i < size; i++) {
#pragma HLS performance target_ti = 32
#pragma HLS LOOP_TRIPCOUNT max = 32out.write(in.read() * 2);}
}
void compute_D(hls::stream<hls::vector<uint32_t, 16>>& in1, hls::stream<hls::vector<uint32_t, 16>>& in2,hls::stream<hls::vector<uint32_t, 16>>& out, int size) {
Loop_D:for (unsigned int i = 0; i < size; i++) {
#pragma HLS performance target_ti = 32
#pragma HLS LOOP_TRIPCOUNT max = 32out.write(in1.read() + in2.read());}
}void store(hls::stream<hls::vector<uint32_t, 16>>& in, hls::vector<uint32_t, 16>* out, int size) {
Loop_St:for (int i = 0; i < size; i++) {
#pragma HLS performance target_ti = 32
#pragma HLS LOOP_TRIPCOUNT max = 32out[i] = in.read();}
}

数据流说明:

  • load:读取数组 vecIn -> c0;
  • compute_A:从流 c0 读取数据,计算后写入 c0 × 3 -> c1, c0 ×3 -> c2;
  • compute_B:从流 c1 读取数据,计算后写入 c1 + 25 -> c3;
  • compute_C:从流 c2 读取数据,计算后写入 c2 × 2 -> c4;
  • compute_D:从流 c3 和 c4 读取数据,计算后写入 c3 + c4 -> c5;
  • store:数组 c5 -> vecOut。 

2.1.3 综合报告

查看综合后的报告:

================================================================
== HW Interfaces
================================================================
* M_AXI
+------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
| Interface  | Data Width | Address Width | Latency | Offset | Register | Max Widen | Max Read     | Max Write    | Num Read    | Num Write   |
|            | (SW->HW)   |               |         |        |          | Bitwidth  | Burst Length | Burst Length | Outstanding | Outstanding |
+------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
| m_axi_gmem | 512 -> 512 | 64            | 64      | slave  | 0        | 512       | 16           | 16           | 16          | 16          |
+------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+

具有较大的位宽,512bit,符合 hls::vector<uint32_t, 16> 定义,512=32*16。

* SW-to-HW Mapping
+----------+---------------+-----------+----------+------------------------------------+
| Argument | HW Interface  | HW Type   | HW Usage | HW Info                            |
+----------+---------------+-----------+----------+------------------------------------+
| vecIn    | m_axi_gmem    | interface |          |                                    |
| vecIn    | s_axi_control | register  | offset   | name=vecIn_1 offset=0x10 range=32  |
| vecIn    | s_axi_control | register  | offset   | name=vecIn_2 offset=0x14 range=32  |
| vecOut   | m_axi_gmem    | interface |          |                                    |
| vecOut   | s_axi_control | register  | offset   | name=vecOut_1 offset=0x1c range=32 |
| vecOut   | s_axi_control | register  | offset   | name=vecOut_2 offset=0x20 range=32 |
| size     | s_axi_control | register  |          | name=size offset=0x28 range=32     |
+----------+---------------+-----------+----------+------------------------------------+

vecIn 和 vecOut 均是绑定到 m_axi_gmem 的 AXI 主接口。 

2.1.4 depth = 32 解析

void diamond(vecOf16Words* vecIn, vecOf16Words* vecOut, int size) {
#pragma HLS INTERFACE m_axi port = vecIn depth = 32
#pragma HLS INTERFACE m_axi port = vecOut depth = 32hls::stream<vecOf16Words> c0, c1, c2, c3, c4, c5;assert(size % 16 == 0);#pragma HLS dataflowload(vecIn, c0, size);compute_A(c0, c1, c2, size);compute_B(c1, c3, size);compute_C(c2, c4, size);compute_D(c3, c4, c5, size);store(c5, vecOut, size);
}

#pragma HLS INTERFACE m_axi port = vecIn depth = 32 指令用于设置接口属性。这里的 depth=32 是一个指令选项,它指定了AXI接口的深度,也就是FPGA与外部内存交互时可以访问的数据量。这个数值应该与设计打算在单次事务中处理的数据量相匹配。由于 TB 中的 std::vector 容器 test 和 outcome 的大小都被初始化为32,这表明设计打算在一个事务中处理32个向量,因此 depth 被设置为32。

这个深度值对于优化数据传输非常重要,因为它影响了接口生成的FIFO缓冲区的大小,以及FPGA与外部内存之间的突发传输能力。正确设置深度可以帮助提高性能,减少延迟,并确保数据的连续流动。

2.1.5 FIFO 通道分类

FIFO 有三种类型:

  • Streams (including hls::streams and streamed arrays),用户创建。
  • Scalar propagation FIFOs,工具推断。
  • Streams of blocks,用户创建。

对照 Dataflow 报告,本 kernel 的 FIFO 通道有两种类型 Stream 和 ScalarProp,从变量的名字也可以看出,c0、c1、c2、c3、c4、c5是由用户创建的,而其余的变量则由 HLS 工具推断生成。

2.2 PIPO

2.2.1 配置默认通道

首先配置 PIPO 缓存,可以通过如下指令进行设定:

# Create a solution

open_solution -reset solution1 -flow_target vitis

或者通过 UI 界面设置:

2.2.2  kernel 代码 

void diamond(unsigned char vecIn[100], unsigned char vecOut[100]) {unsigned char c1[100], c2[100], c3[100], c4[100];
#pragma HLS dataflowfuncA(vecIn, c1, c2);funcB(c1, c3);funcC(c2, c4);funcD(c3, c4, vecOut);
}void funcA(unsigned char* in, unsigned char* out1, unsigned char* out2) {
Loop0:for (int i = 0; i < 100; i++) {
#pragma HLS pipeline rewind
#pragma HLS unroll factor = 2unsigned char t = in[i] * 3;out1[i] = t;out2[i] = t;}
}void funcB(unsigned char* in, unsigned char* out) {
Loop0:for (int i = 0; i < 100; i++) {
#pragma HLS pipeline rewind
#pragma HLS unroll factor = 2out[i] = in[i] + 25;}
}void funcC(unsigned char* in, unsigned char* out) {
Loop0:for (unsigned char i = 0; i < 100; i++) {
#pragma HLS pipeline rewind
#pragma HLS unroll factor = 2out[i] = in[i] * 2;}
}void funcD(unsigned char* in1, unsigned char* in2, unsigned char* out) {
Loop0:for (int i = 0; i < 100; i++) {
#pragma HLS pipeline rewind
#pragma HLS unroll factor = 2out[i] = in1[i] + in2[i] * 2;}
}

数据流说明:

  • compute_A:计算后写入 vecIn × 3 -> c1, vecIn ×3 -> c2;
  • compute_B:计算后写入 c1 + 25 -> c3;
  • compute_C:计算后写入 c2 × 2 -> c4;
  • compute_D:计算后写入 c3 + c4 × 2 -> vecOut.

2.2.3 综合报告

================================================================
== HW Interfaces
================================================================
* M_AXI
+------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
| Interface  | Data Width | Address Width | Latency | Offset | Register | Max Widen | Max Read     | Max Write    | Num Read    | Num Write   |
|            | (SW->HW)   |               |         |        |          | Bitwidth  | Burst Length | Burst Length | Outstanding | Outstanding |
+------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
| m_axi_gmem | 512 -> 512 | 64            | 64      | slave  | 0        | 512       | 16           | 16           | 16          | 16          |
+------------+------------+---------------+---------+--------+----------+-----------+--------------+--------------+-------------+-------------+
* SW-to-HW Mapping
+----------+---------------+-----------+----------+------------------------------------+
| Argument | HW Interface  | HW Type   | HW Usage | HW Info                            |
+----------+---------------+-----------+----------+------------------------------------+
| vecIn    | m_axi_gmem    | interface |          |                                    |
| vecIn    | s_axi_control | register  | offset   | name=vecIn_1 offset=0x10 range=32  |
| vecIn    | s_axi_control | register  | offset   | name=vecIn_2 offset=0x14 range=32  |
| vecOut   | m_axi_gmem    | interface |          |                                    |
| vecOut   | s_axi_control | register  | offset   | name=vecOut_1 offset=0x1c range=32 |
| vecOut   | s_axi_control | register  | offset   | name=vecOut_2 offset=0x20 range=32 |
| size     | s_axi_control | register  |          | name=size offset=0x28 range=32     |
+----------+---------------+-----------+----------+------------------------------------+

报告中 HW Interface 与 Mapping 具有相同的内容,这里不再赘述。

2.2.4 PIPO 通道分类

PIPO 有三种类型:

  • PIPO,用户创建。
  • Task Level FIFOs (TLF),工具推断。
  • Input and output ports to the upper level,用户创建。

对照 Dataflow 报告,本 kernel 的 PIPO 通道类型只有一种:PIPO,c1、c2、c3、c4均由用户创建。变量 vecOut_c 则由 HLS 工具推断生成为 FIFO 类型。

 

3. 综合对比

3.1 数据类型和接口

第一段代码:

  • 使用 hls::vector<uint32_t, 16> 类型来表示向量。
  • 使用 hls::stream 数据结构来处理数据流。
  • 显式指定 AXI 主接口。

第二段代码:

  • 使用基本的 unsigned char 类型的数组,每个数组包含 100 个元素。
  • 没有使用 hls::stream,而是使用普通的数组传递数据。
  • 输入输出接口使用普通的 C-style 指针和数组,没有显式的接口设置。

3.2 数据流和数据流控制

第一段代码:

  • 使用 HLS dataflow pragma (#pragma HLS dataflow) 来启用数据流优化
  • 显式指定使用 hls::stream 作为函数间的通信方式,函数之间的数据通过流进行传递。

第二段代码:

  • 同样使用 HLS dataflow pragma (#pragma HLS dataflow) 来启用数据流优化,但数据传递是通过普通的数组来进行。
  • 函数间的数据通过数组传递,没有使用 hls::stream。

3.3 计算函数和计算方式

第一段代码:

  • 函数更为通用,接受 hls::vector 类型的数据,并且处理过程中使用了 HLS 的各种特性如 pipeline 和 unroll。
  • 每个函数处理 hls::vector<uint32_t, 16> 类型的数据,并将结果写入另一个 hls::vector。

第二段代码:

  • 函数接受基本类型的指针和数组作为输入输出,处理过程中使用 pipeline 和 unroll 进行优化。
  • 每个函数处理 unsigned char 类型的数据,并将结果写入另一个 unsigned char 数组。

4. 总结

本文对比了在 Vitis HLS 编译器中配置 FIFO 和 PIPO 缓冲器的方法和效果。通过两段代码示例,我们展示了使用这两种通道缓存的不同实现方式。

在 FIFO 示例中,代码使用 hls::vector<uint32_t, 16> 和 hls::stream 数据结构,通过 AXI 主接口进行数据传输,并用 #pragma HLS dataflow 启用数据流优化,每个函数通过流进行通信。这种方式更通用,但需要显式指定流和接口设置。

在 PIPO 示例中,代码采用 unsigned char 数组进行数据传递,使用 C-style 指针和数组,没有显式接口设置,通过数组传递数据,同样使用 #pragma HLS dataflow 启用数据流优化。这种方式更简单直接,但缺乏流的灵活性。

PIPO 缓冲器可以自动调节,确保任务之间的重叠执行,而不会出现死锁。而显式手动串流的 FIFO 通道虽然可以更快地开始重叠执行,但需要小心调整队列大小,以避免死锁问题。

综合来看,FIFO 适用于复杂的通用数据处理场景,而 PIPO 更适合简单的数据传递需求。正确选择和配置这两种缓冲器,可以优化系统性能,减少延迟。本文帮助读者理解 FIFO 和 PIPO 的适用场景和配置方法,从而在设计中做出最佳选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/22895.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024年带你揭秘FL Studio 21破解版,2024年最新FL21内置汉化破解补丁

截止目前&#xff0c;FL Studio最新版是FL Studio 21.2.3.4004版本&#xff0c;想必很多朋友已经迫不及待了&#xff0c;那么今天这篇文章我将带大家详细的介绍FL Studio 21.2.3 Build 4004新特点以及如何下载&#xff0c;安装和激活。 PS.本次为你带来的是fl studio21破解版&a…

【python】Modulenotfounderror: no module named ‘open_clip’

成功解决“ModuleNotFoundError: No module named ‘open_clip’”错误的全面指南 在Python编程中&#xff0c;如果你遇到了“ModuleNotFoundError: No module named ‘open_clip’”这个错误&#xff0c;它意味着你的Python环境中没有安装名为open_clip的模块&#xff0c;或者…

grep、sed、awk

grep&#xff1a;文本过滤工具 sed: 文本编辑工具 awk: 格式化文本 grep -n 显示行号 -i 忽略大小写 -v 取反 -o 只保留关键消息 # 找出文件的空行 grep ^$ test.txt -n # 找出文件非空行内容 grep ^$ test.txt -n -v # 找出文件非空行内容&#xff0c;并且排除注释&#xff…

8个免费下载音乐的网站,建议收藏!

1、My Free MP3 tools.liumingye.cn/music/ 一个好用且免费的在线音乐播放和下载网站&#xff0c;几乎收录了所有国内外大火的歌手和歌曲&#xff0c;可以通过歌手列表找单曲&#xff0c;也可以直接搜索歌手或歌曲名&#xff0c;下面还有一些热门搜索&#xff0c;可以直接播放…

Windows下SVN文件损坏,启动服务报错1067

之前碰到过一次&#xff0c;忘记最后怎么解决的了&#xff0c;只记得大概原理和原因&#xff0c;以及解决办法。 1067错误码&#xff0c;很多地方都会碰到&#xff0c;mysql也会有&#xff0c;看来应该是windows系统的错误码。跟具体程序无关。所以直接百度“SVN”、“1067”…

HarmonyOS App开发造轮子--自定义圆形图片

思路&#xff1a; 1、对比之前自己在其他程序开发中自定义组件的思路&#xff0c;首先寻找父组件Image和Component相关的Api&#xff0c;看看是否具备OnDraw方法。 2、了解Canvas相关Api操作&#xff0c;特别是涉及到位图的操作。 通过翻阅大量资料&#xff0c;发现了两个关…

不是,有了这套IP地址管理开源系统谁还用Excel啊

号主&#xff1a;老杨丨11年资深网络工程师&#xff0c;更多网工提升干货&#xff0c;请关注公众号&#xff1a;网络工程师俱乐部 中午好&#xff0c;我的网工朋友。 作为网工的我们想必都很清楚IP地址管理的重要性以及其复杂性&#xff0c;传统的Excel表格虽然在某些情况下能…

基于STM32开发的智能农业灌溉控制系统

目录 引言环境准备智能农业灌溉控制系统基础代码实现&#xff1a;实现智能农业灌溉控制系统 4.1 土壤湿度传感器数据读取4.2 水泵控制4.3 环境监测与数据记录4.4 用户界面与多功能显示应用场景&#xff1a;农业灌溉与环境监测问题解决方案与优化收尾与总结 1. 引言 随着农业…

html写一个table表

HTML代码&#xff1a; <div class"table_box w-full"><div class"title_top">XX表</div><div class"title_btm">(<input class"input input_1" type"text">xxxx)</div><table class…

【Linux】内存级文件

目录 C语言关于文件操作的函数 Linux关于文件操作的系统调用 完善myshell C语言缓冲区 其实我们在C语言就学过文件操作&#xff0c;但是从语言的角度&#xff0c;我们只是说会用了关于文件的一些操作和函数&#xff0c;但其实它究竟是怎么回事我们其实并不明白&#xff0c;…

rust学习(字节数组转string)

最新在写数据传输相关的操作&#xff0c;发现string一个有趣的现象&#xff0c;代码如下&#xff1a; fn main() {let mut data:[u8;32] [0;32];data[0] a as u8;let my_str1 String::from_utf8_lossy(&data);let my_str my_str1.trim();println!("my_str len is…

STM32实验之USART串口发送+接受数据(二进制/HEX/文本)

涉及三个实验&#xff1a; 1.USART串口发送和接收数据 我们使用的是将串口封装成为一个Serial.c模块.其中包含了 void Serial_Init(void);//串口初始化 void Serial_SendByte(uint8_t Byte);//串口发送一个字节 void Serial_SendArray(uint8_t *Array,uint16_t Length);//…

【C语言】详解函数(上)(庖丁解牛版)

文章目录 1. 前言2. 函数的概念3.库函数3.1 标准库和头文件3.2 库函数的使用3.2.1 头文件的包含3.2.2 实践 4. 自定义函数4.1 自定义函数的语法形式4.2 函数的举例 5. 形参和实参5.1 实参5.2 形参5.3 实参和形参的关系 6. return 语句6. 总结 1. 前言 一讲到函数这块&#xff…

栈排序00

题目链接 栈排序 题目描述 注意点 对栈进行排序使最小元素位于栈顶最多只能使用一个其他的临时栈存放数据不得将元素复制到别的数据结构&#xff08;如数组&#xff09;中栈中的元素数目在[0, 5000]范围内 解答思路 本题是要实现一个小顶堆&#xff0c;可以直接使用Priori…

上位机图像处理和嵌入式模块部署(f407 mcu中的udp server开发)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 既然lwip已经port到407上面了&#xff0c;接下来其实就可以做一些测试了。本身lwip支持tcp、udp&#xff0c;也支持client和server&#xff0c;既然…

【数据分享】中国第三产业统计年鉴(1991-2022)

大家好&#xff01;今天我要向大家介绍一份重要的中国第三产业统计数据资源——《中国第三产业统计年鉴》。这份年鉴涵盖了从1991年到2022年中国第三产业统计全面数据&#xff0c;并提供限时免费下载。&#xff08;无需分享朋友圈即可获取&#xff09; 数据介绍 每年的《中国…

2004NOIP普及组真题 3. FBI树

线上OJ 地址&#xff1a; [04NOIP普及组] FBI树 本题的意思是&#xff1a;给定一个 01字符串 &#xff08;对应一棵完全二叉树的最后一层叶子节点&#xff09;&#xff0c;将树的每一个节点的值用字母“F、B、I”表示。规则&#xff08;如下图所示&#xff09;为&#xff1a; 1…

Spring AI 第二讲 之 Chat Model API 第二节Ollama Chat

通过 Ollama&#xff0c;您可以在本地运行各种大型语言模型 (LLM)&#xff0c;并从中生成文本。Spring AI 通过 OllamaChatModel 支持 Ollama 文本生成。 先决条件 首先需要在本地计算机上运行 Ollama。请参阅官方 Ollama 项目 README&#xff0c;开始在本地计算机上运行模型…

curl 92 HTTP/2 stream 5 was not closed cleanly: CANCEL

source ~/.bash_profile flutter clean Command exited with code 128: git fetch --tags Standard error: 错误&#xff1a;RPC 失败。curl 92 HTTP/2 stream 5 was not closed cleanly: CANCEL (err 8) 错误&#xff1a;预期仍然需要 2737 个字节的正文 fetch-pack: unexpec…

GPT革命:AI如何重塑我们的未来!

GPT革命&#xff1a;AI如何重塑我们的未来&#xff01; &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 博客首页 怒放吧德德 To记录领地 &#x1f31d;分享…