TCL --- 列表_part0

0 回顾

字符串的篇幅较长,内容较多。需要明确的是,Tcl字符串的内容远远不止于此,我只是摘选了一些常见的内容。接下来我们一起聊一聊Tcl的列表。在这里我们将讨论列表的创建,修改,获取,搜索,排序以及拼接等内容。文章字数太多不利于同学们阅读,为此列表的内容我计划分成两篇文章介绍。

1 创建

列表简单来说就是多个元素的集合,该集合中的元素可以是数字,字符串,空字符串,甚至是列表(即,列表中嵌套列表)。并且列表中的元素可以重复出现。创建列表最简单的方式如下:

set list_x {John Anne Mary Jim}
# 创建列表list_x,列表中包含了4个元素,分别是John, Anne, Mary, Jim。
# 每个元素之间用空格分隔,并且采用大括号将这4个元素组成列表。
# 因此,大括号并不是列表的一部分。列表的实际内容仅仅是其中的4个元素。

通常我们采用list,concat,lrepeat命令创建列表。为了便于理解,每个命令我将分成三个步骤去介绍每一个命令,分别是命令的格式,基础示例,示例解析和特别说明。

  • list命令格式
    list element0 element1 sub_list0 sub_list1

  • 基础示例

    set list1 [list a b {c d}]
    # 创建列表list1, 列表中包含了a b c d等元素。
    # 结果: a b {c d}
    set list1 [list a b a {} {c d}]
    # 列表list1包含了a b a {} {c d} 
    
  • 解析
    {c d}为子列表。换句话说,命令list是将所有的参数拼接成一个列表,不会做任何的处理。第2个例子展示了利用list命令创建一个包含空白字符的子列表。另外需要特别说明的是:列表中a元素重复出现,这是符合要求的。后文会介绍如何移除列表中的重复元素。
    [Note] 推荐采用list的方式创建列表。

  • concat命令格式:
    concat element0 element1 sub_list0 sub_list1

  • 基础示例:

    set new_list [concat a b {} {c d} {e f  } {g {h i}}]
    # 列表sub_list1中包含了两个元素:e f; f后面跟了一个空格。
    # 结果: a b c d e f g {h i}
    
  • 解析
    concat的工作原理:把各个列表或者字符串拼接成一个单一的列表,并且移除空列表和列表中的空元素。
    {}:concat解析这个列表时发现没有元素,不会返回任何元素到列表new_list中。
    {c d}:concat解析这个列表中的元素a和元素b,并且其作为列表new_list中的两个元素。
    {e f }:concat解析出这个列表中的元素e和元素f,元素f后面跟随的空元素将被移除。最后,将元素e和元素f作为列表new_list的两个元素。
    {g {h i}}: concat解析出这个列表中包含了元素g和子列表{h i},并将解析好的元素和子列表作为列表new_list的元素。
    总结:concat会将字符或者不包含子列表的列表做简单的拼接,并且子列表保留。
    [Note]个人不喜欢用这种方式创建列表,虽说它可以提供移除空字符的一些便利,但是工作原理有些复杂,不如list来的直接,干脆。

有时候我们需要创建一个列表,列表中的元素是周期出现的,若采用list去做创建,则代码往往是冗余的。如:

set new_lsit [list {a b c} {a b c} {a b c} {a b c} {a b c} {a b c} {a b c}]
# 若要创建1000个,那岂不是要写麻了🤣

显然,采用list的方式创建时非常冗余的。Tcl提供了一种便捷的方式快速的创建此种类型的列表,即lrepeat。

  • lrepeat的命令格式:
    lrepeat loop_num element0 element1 sub_list0 sub_list1
  • 基础示例:
    set new_list0 [lrepeat 3 a b c]
    # 结果: 列表new_list0: {a b c a b c a b c};
    # a b c元素重复出现3此
    set new_list1 [lrepeat 3 a {b c}]
    # 结果:列表new_list0: {a {b c} a {b c} a {b c}}
    
  • 解析
    lrepeat可以理解成list的升级版,保持子列表和元素不变,仅仅是元素或者子列表重复n次。对于元素是周期性出现的列表而言,强烈推荐这种用法。

2 修改

列表创建好后,我们当然需要去修改它。毕竟极少数情况下,列表是保持不变的。通常在脚本中我们需要修改列表。和修改列表相关的命令有:lrange、linsert、lreplace、lset、lappend。其中,linsert,lset,lappend是修改列表最常见的方法。

  • lrange命令格式
    lrange list0 begin_idx end_idx
    lrange命令返回列表list0中指定范围的元素。起始边界为begin_idx;终止边界为end_idx。

  • 基础示例

    # 创建列表x_list
    set x_list [list a b {c d} e]
    # 获取index 1 ~ index 3的元素
    lrange $x_list 1 3
    # 结果:b {c d} e
    # 获取index 0 ~ index 1的元素
    lrange $x_list 0 1
    # 结果:a b
    
    
  • 解析
    为了便于理解上述给出的基础示例。我们给出index和list mapping table。如下表所示:

    Index0123
    Sublist/Elementab{c d}e

    表中,index表示list中各个元素或者子子列表对应的索引。如元素a对应的index为0;子列表{c d}对应的index为2。
    第一个示例的目的是获取index 1 ~ index 3的元素。即,表格index 1对应元素b, index 2对应子列表{c d},index 3对应元素e。第二个示例也可以从index和list mapping表中轻易地获得基础示例中的结果。

  • linsert命令格式
    linsert list0 location element0 element1 element2
    linsert将 element0 element1 element2插入list0的指定locatiaon,实现列表的扩展。基于此,咱们可以通过linsert的方式push元素到列表,可以push到开头,也可以push到结尾。类似的用法在ASIC和FPAG的flow中是非常常见的。

  • 基础示例

    # 创建列表x_list
    set x_list [list a b {c d} e]
    # 在列表x_list的第2个元素后插入元素X Y Z
    # 或者理解成在x_list index 2前 index1 后插入元素X Y Z
    linsert $x_list 2 X Y Z
    # 结果:a b X Y Z{c d} 
    
  • 解析
    如果location是index0,则表示在x_list的开头插入,实现push_front的功能;若location是end,则在x_list的结尾插入,实现push_back的功能;若location是index1,则在index0和index1之间插入。为了便于理解,给出插入前和插入后的表格对比。
    插入前的列表:

    Index0123
    Sublist/Elementab{c d}e

    插入后的结果:

    Index0123456
    Sublist/ElementabXYZ{c d}e
  • lreplace命令格式
    lreplace list0 index_begin index_end element0 sub_list0
    将list0的index_begin和index_end之前的列表替换成element0和sub_list0。即,index_begin和index_end之间的元素被删除。若要删除index_begin和index_end之间的元素,那么 element0 sub_list0为空即可。也就是说不要有 element0 sub_list0。

  • 基础示例

    # 创建列表list0
    set list0 [list a b {c d} e}]
    # 删除index 3对应的元素
    lreplace $list0 3 3
    # 结果: a b {c d}
    # 元素b和子列表{c d}被替换成子列表{W X} 元素Y 元素Z
    lreplace $list0 1 2 {W X} Y Z
    # 结果a {W X} Y Z e
    
  • 解析
    index_begin和index_en一定是成对出现的。若指向某一个元素,则index_begin=index_end;否则index_begin表示起始位置,index_end表示终止位置。
    替换前的列表:

    Index0123
    Sublist/Elementab{c d}e

    示例1替换后的列表:

    Index012
    Sublist/Elementab{c d}

    示例2替换后的列表

    Index01234
    Sublist/Elementa{W X}YZe

    值得注意的是,lreplace在创建新列表的时候必须赋值原始列表中的元素;若列表的非常的大,那么复制原始列表将会严重的影响机器的性能。因此,对于大列表而言,不建议使用lreplace。为了解决这个问题,Tcl提供了lset命令。如下所述。

  • lset命令格式
    单个元素的替换:lset list0 index0 element0
    将list0中的index0对应的元素修改成elements。
    嵌套元素的替换:lset list0 index0 index1 element0
    将list0中index0对应子列表中的index1对应的元素进行替换
    其中,list0一定是列表名,不是变量名。即,写成list0即可,一定不要写成$list0。

  • 基础示例

    # 创建列表person
    set person [list {Jane Doe} 32 female]
    # 将元素32修改成30
    lset person 1 30
    # 结果:{Jane Doe} 30 female
    # 将子列表中的元素Done修改成Johnson
    lset person 0 1 johnson
    # 结果:{Jane Johnson} 30 female
    
  • 解析
    无论是子列表中的元素,还是列表本身的元素,采用lset每次只能修改其中的一个。换句话说lset修改的最小粒度是元素。另外,值得注意的是,lset不能创建新的列表,只能用于修改已经存在的列表。如果列表中的元素不存在,那么Tcl解析器将返回error。

  • lappend命令格式
    lappend list_name element0 sub_list0
    将element0和sub_list0添加到名字为list_name的列表的末尾,类似push_end,并且依次可以push多个元素或者子列表。

  • 基础示例

    # 创建列表x_list
    set x_list [list a b {c d} e]
    # 将元素XX和子列表{YY ZZ}push到列表x_list的末尾
    lappend x_list XX {YY ZZ}
    # 结果:a b {c d} e XX {YY ZZ}
    
  • 解析
    lappend后跟的第一个参数不是列表本身,而是列表的名字。它和lset都是传递列表名,而不是列表本身。另外,lappend同样使用于大型列表。

### Vivado Checkpoint (DCP) 文件生成及 `report_utilization` 使用方法 #### 1. DCP 文件的生成 在 Vivado 中,Checkpoint (DCP, Design CheckPoint) 是一种中间文件格式,用于保存综合或实现阶段的设计状态。它允许用户将设计的不同部分分别处理并集成到最终设计中。 可以通过以下方式生成 DCP 文件: - **综合阶段** 综合完成后,可以使用以下命令导出 DCP 文件: ```tcl write_checkpoint -force design_synth.dcp ``` - **实现阶段** 实现完成后,同样可以导出 DCP 文件: ```tcl write_checkpoint -force design_impl.dcp ``` 这些 DCP 文件可以在后续流程中被重新加载,从而节省时间[^1]。 --- #### 2. 加载 DCP 文件的方法 Vivado 提供两种主要的方式加载 DCP 文件:`read_checkpoint` 和 `open_checkpoint`。 - **`read_checkpoint`** 此命令仅读取 DCP 文件而不自动链接设计。需要手动调用 `link_design` 命令完成设计连接。 ```tcl read_checkpoint C:/path/to/design_synth.dcp link_design -top top_module -part xc7z020clg484-1 ``` 如果项目中有多个 DCP 文件,则需通过 `current_project` 切换当前活动项目[^2]。 - **`open_checkpoint`** 自动创建一个新的内存中的工程实例,并初始化为指定的 DCP 文件内容。 ```tcl open_checkpoint C:/path/to/design_synth.dcp -part xc7a100tcsg324-1 ``` 这两种方法的选择取决于具体需求。如果只需要分析特定模块的状态而无需完整的上下文环境,推荐使用 `open_checkpoint`[^2]。 --- #### 3. 资源利用率报告 (`report_utilization`) `report_utilization` 是 Vivado 的一个重要工具,用于评估 FPGA 设计所占用资源的情况(如 LUTs、FFs、DSP Slices 等)。以下是其基本用法和日志解释: - **基础语法** ```tcl report_utilization -file utilization_report.rpt ``` 上述命令会生成一个名为 `utilization_report.rpt` 的文本文件,其中包含详细的资源利用情况。 - **高级选项** 支持更多参数调整输出细节,例如按层次结构显示资源分布: ```tcl report_utilization -hierarchical_depth all -cell my_top_instance/submodule_inst ``` - **日志说明** 报告通常分为以下几个部分: - 总体资源统计:展示整个芯片上的可用资源数量及其分配比例。 - Example: `"Slice Logic Utilization"` 表明逻辑单元的实际消耗百分比。 - 层次化视图:当启用 `-hierarchical_depth` 参数时提供更细粒度的信息。 - 特定组件详情:针对某些复杂 IP 或自定义模块单独列出它们的影响。 注意,在运行此命令之前应确保已完成至少一次成功的综合操作;否则可能无法获得准确数据。 --- #### 4. 结合 TCl Shell 进行自动化脚本编写 为了简化重复性的任务,建议采用 TCL Script 来管理从生成 DCP 至获取资源报表的过程。下面是一个简单的例子演示如何串联以上功能点: ```tcl # 设置顶层模块名与目标器件型号 set TOP_MODULE "myTop" set PART_NAME "xc7a100tcsg324-1" # 合成设计 synth_design -top $TOP_MODULE -part $PART_NAME write_checkpoint -force ${TOP_MODULE}_post_synth.dcp # 创建实现流 opt_design place_design route_design # 导出实现后的 DCP 并生成资源利用率报告 write_checkpoint -force ${TOP_MODULE}_post_route.dcp report_utilization -file ${TOP_MODULE}_utilization.rpt ``` 上述脚本不仅实现了全流程控制还保留了重要环节的结果以便后期审查[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值