首页
📁归档
⏳时光机
🚩友链
📫留言
📧订阅本站
推荐
📕考研课程
🏜️ 免费壁纸
❤ 捐助本站
💰资助名单
🎵音乐实验
Search
1
【NPN/PNP三极管】放大电路饱和失真和截止失真的区别
20,020 阅读
2
论文写作中如何把word里面所有数字和字母替换为新罗马字体
10,576 阅读
3
【高数】形心计算公式讲解大全
9,019 阅读
4
Vivado-FPGA Verilog烧写固化教程
7,811 阅读
5
【概论】一阶矩、二阶矩原点矩,中心矩区别与概念
7,787 阅读
🪶微语&随笔
励志美文
我的随笔
写作办公
📡电子&通信
嵌入式&系统
通信&信息处理
编程&脚本笔记
⌨️IC&系统
FPGA&ASIC
VLSI&IC验证
EDA&虚拟机
💻电子&计算机
IP&SOC设计
机器学习
软硬件算法
登录
/
注册
TCL脚本(共2篇)
找到
2
篇与
TCL脚本
相关的结果
数字IC设计项目高性能FFT芯片设计(前后端全流程)
本项目为数字集成电路设计基于 SMIC 0.18 μm 工艺,完成了 16 点基-4 FFT 算法 的芯片级前后端全流程设计,涵盖 RTL 建模、功能仿真、逻辑综合(DC)与物理实现(ICC)。最终芯片工作频率达到 135 MHz,面积 4.6953 mm²,单次 FFT 能耗 5.25×10⁻⁶ mJ。文章末尾有芯片设计源码与项目其他文件,包含:前端RTL源码、中端DC源码脚本、后端ICC源码脚本、项目报告、参考文件等。目录 一、设计规范简介1.1 功能描述 1.2 性能指标能耗计算 单位面积功耗时间操作次数 二、电路性能分析与结构设计2.1 性能估算面积开销 运算性能 吞吐率 带宽 工作频率 2.2 硬件结构图 2.3 流水线时序设计 2.4 时序过程 三、RTL 模型与仿真验证3.1 验证方法 3.2 验证平台搭建 3.3 仿真验证结果3.3.1 功能点验证 3.3.2 第一组数据验证对比 3.3.3 测试向量 3.3.4 仿真性能统计 四、逻辑综合策略与结果4.1 逻辑综合流程参数设置 4.2 逻辑综合结果时序结果 面积结果 功耗结果 资源使用 设计违例 五、物理实现与结果分析5.1 ICC 设计步骤5.1.1 数据设定 (Data Setup) 5.1.2 布局规划 (Floorplanning) 5.1.3 布局 (Placement) 5.1.4 时钟树综合 (CTS) 5.1.5 布线 (Routing) 5.2 ICC 设计结果5.2.1 芯片概貌 5.2.2 时序结果 5.2.3 面积结果 5.2.4 功耗结果 5.2.5 芯片压降 5.2.6 拥塞分析 5.2.7 资源使用 5.2.8 设计违例 六、设计总结6.1 遇到的主要问题 6.2 相应的解决思路 6.3 关键优化点乘法器优化(频率提升核心) 七、参考资料 八、项目工程下载(源码、文档和参考文献) 一、设计规范简介 1.1 功能描述 本次课程设计的核心目标是实现一个 高能效的快速傅里叶变换(FFT)电路,具体规格如下: 算法:16 点基-4 FFT 算法 输入数据:实部与虚部均为 17 bit(1 bit 符号位 + 8 bit 整数位 + 8 bit 小数位) 旋转因子:实部与虚部均为 8 bit(1 bit 符号位 + 7 bit 数据位) 数据格式:定点数表示,如图 1 所示 图 1 16 点基-4 FFT 蝶形运算原理图 pasted_1785032408236_t4qsdl.png图片 图 2 数据表示方式(17 bit 输入 / 8 bit 旋转因子) 运算数据的实部虚部均为17 bit 而旋转因子的实部与虚部为8 bit pm2xSTf.png图片1.2 性能指标 评价指标具体数值完成一次 16 点 FFT 运算的能耗5.25118208 × 10⁻⁶ mJ单位面积功耗时间 FFT 操作次数4.1987688 × 10⁴ 次/(mm²·mW·s)工作频率135.135135 MHz面积开销4.695300 mm²能耗计算 单次 FFT 能耗 = 每秒能耗 / 每秒 FFT 运算次数 = 44.3512 mW/s / (1 / (16 × 7.4 ns)) = 5.251182 × 10⁻⁶ mJ单位面积功耗时间操作次数 单位面积功耗时间 FFT 操作次数 = 每秒 16 点 FFT 运算次数 / (面积 × 功耗 × 时间) = (1 / (16 × 7.4 ns)) / (4.535456 mm² × 44.3512 mW × 1 s) = 4.1987688 × 10⁴ 次/(mm²·mW·s)二、电路性能分析与结构设计 2.1 性能估算 面积开销 芯片共有 90 个管脚,布局尺寸计算如下: 长:76 μm × 23 + 210 μm × 2 = 2168 μm = 2.168 mm 宽:76 μm × 22 + 210 μm × 2 = 2092 μm = 2.092 mm 总面积:2.168 mm × 2.092 mm = 4.535456 mm² pasted_1785034131111_9ws1vf.png图片 运算性能 每秒可完成 FFT 运算次数 = 1 s / (16 × 7.4 ns) = 8.445946 × 10⁶ 次吞吐率 芯片采用 串行输入输出 方式,每 16 个周期完成一次信号输入,从输入第 13 个信号时开始蝶形运算,经过 8 个周期完成运算,再经过 16 个周期输出: 单次运算周期:13 + 8 + 16 = 37 个周期 单次运算吞吐率:1 / (37 × 7.4 ns) = 3.65 × 10⁶ 次/秒 由于采用了 三级流水线 架构,在连续运算足够多的情况下: 流水运算周期:16 个周期(每周期输出一个结果) 流水吞吐率:1 / (16 × 7.4 ns) = 8.45 × 10⁶ 次/秒 带宽 存储器时钟频率和数据输入速度保持恒定,峰值带宽与平均带宽相同: 带宽 = 17 bit × 2(实部+虚部) × 135.135135 MHz = 4.595 Gbps工作频率 工作频率 = 1 / 时钟周期 = 1 / 7.4 ns = 135.135135 MHz2.2 硬件结构图 图 3 电路硬件结构图 pm2x9k8.png图片 整个电路分为以下模块:模块名称文件名模块类型功能描述控制模块ctrl时序逻辑控制电路中各个模块的运行时序串转并模块s_p时序逻辑将串行输入的数据整合为并行并改变顺序复选器mux时序逻辑按照 FFT 级数选择进入运算模块的数据中转存储模块reg1时序逻辑存储并转发两级 FFT 运算间的运算数据运算模块(蝶形运算)butterfly组合逻辑进行四输入蝶形运算乘法器multi16组合逻辑计算数据与旋转因子间的乘法并转串模块p_s时序逻辑将并行输出的数据分解为串行并倒位序2.3 流水线时序设计 时钟周期1-1617-2122-2930-37input第 N 组输入数据到 REG1第 N+1 组输入数据到 REG1——calculate—两级蝶形运算两级蝶形运算—output——第 N-1 组从 REG3 输出第 N 组从 REG3 输出2.4 时序过程 pasted_1785033901140_9815cd.png图片 三、RTL 模型与仿真验证 3.1 验证方法 FFT 芯片的验证包括两部分: 输出结果验证:通过 testbench 输入数据,将仿真输出与理论正确结果对比 流水线验证:一次输入多组数据,观察电路是否能流水式执行 3.2 验证平台搭建 在 ModelSim SE-64 10.1c 中,将所有模块的 Verilog 代码通过 top 文件连接,撰写 testbench 从指定文本文件读入输入数据并进行计算。 3.3 仿真验证结果 3.3.1 功能点验证 取三组输入作为测试向量,验证输出结果正确性与流水线功能。 图 4 测试波形(流水线时序验证) pmR29yj.png图片 从波形可见,p_s 模块数据按预期工作:每周期进入 4 个数据,逐个输出,上一组数据输出完毕后下一周期立即输出下一组,流水式执行衔接良好。3.3.2 第一组数据验证对比 X[k]理论输出(实部)实际输出(实部)理论输出(虚部)实际输出(虚部)X[0]0_00010010_000000000_00010001_110010000_00010010_000000000_00010001_10111000X[1]0_00000101_011011100_00000101_010111101_11110000_101110101_11110000_11100101X[2]1_11110110_000000001_11110110_001001100_00000100_001111100_00000100_00101101X[3]0_00000010_000011010_00000010_000000110_00001010_010111000_00001010_00111100X[4]0_00001000_000000000_00000111_111011001_11110110_000000001_11110110_00101000X[5]1_11110110_001100001_11110111_010011010_00000011_100100000_00000011_10000100X[6]0_00000011_001010110_00000011_000111000_00000010_111000000_00000001_01100101X[7]0_00000000_011011010_00000000_011101000_00000010_111000000_00000010_11100001X[8]0_00000010_000000000_00000010_000010001_11111110_000000001_11111110_00001000X[9]0_00000101_111000010_00000101_110100101_11111111_111101011_11111111_11110111X[10]1_11110110_000000001_11110110_001001101_11111011_110000011_11111011_11010001X[11]0_00001001_010000100_00001001_001001110_00000100_111100110_00000100_11100010X[12]1_11111100_000000001_11111100_000111001_11111010_000000001_11111010_00011000X[13]1_11111101_011111111_11111101_100010110_00000011_101111110_00000011_10110000X[14]0_00001000_110101000_00001000_101100001_11111110_100101011_11111110_10011101X[15]1_11110100_010000101_11110100_011010101_11110101_110011111_11110101_11110001图 5 Transcript 输出 pasted_1785032904139_lcns0m.png图片 通过对比可知,实际输出与理论输出的实部与虚部均十分接近,在误差范围内可认为相等(存在数据位宽限制等不可避免的截断误差)。第二组、第三组数据经同样验证,结果一致。3.3.3 测试向量 第一组测试向量: 0000000010000000000000000000000000 0000000000000000000000000000000000 0000000010000000000000000000000000 0000000000000000000000000000000000 0000000110000000000000000000000000 0000000100000000000000010000000000 0000000010000000000000100000000000 0000000100000000000000001000000000 0000000010000000000000000000000000 0000000100000000000000010000000000 0000000010000000000000000000000000 0000000000000000000000000000000000 0000000010000000000000000000000000 0000000010000000000000000000000000 0000000010000000000000000000000000 0000000010000000000000000000000000第二组测试向量: 全 1 序列(16 个相同数据) 第三组测试向量: 仅第一个数据为 1,其余为 0 3.3.4 仿真性能统计 ✅ 成功实现 16 点 FFT 算法,输出结果与理论值一致 ✅ 每周期输入一个数据,每周期输出一个数据 ✅ 成功实现流水式运行 ✅ 多次测试输出结果稳定,未出现错误 四、逻辑综合策略与结果 4.1 逻辑综合流程 使用 Synopsys Design Compiler (DC) 进行逻辑综合,主要流程: 指定库文件 → 读入设计(转GTECH) → 定义环境 → 设计约束 → 编译策略 → 综合优化 → 存储网表pasted_1785034037920_i4ehar.png图片 参数设置 参数数值工艺SMIC 0.18 μm时钟周期7.4 ns输入输出延迟3.7 ns工作电压1.62 V运行温度125 ℃面积约束0(无约束)互连模型balanced_tree时钟延时4.3 ns不确定时钟0.5 ns过渡时钟0.2 ns端口最大过渡3 ns4.2 逻辑综合结果 时序结果 项目时间 (ns)clock clk (rise edge)7.40clock network delay (ideal)4.30clock uncertainty-0.50clk slack (MET)0.007.4 ns 时钟符合设计要求,换算工作频率约为 135 MHz。 面积结果 项目数量项目面积 (μm²)Ports70Combinational area1,483,546.41Nets140Non-combinational area153,599.85Cells71Net Interconnect area2,089,790.57References3Total Area3,726,936.83功耗结果 项目数值Global Operating Voltage1.62 VCell Internal Power756.0848 mWNet Switching Power375.6310 mWTotal Dynamic Power1.1317 WCell Leakage Power28.5281 μW资源使用 项目数量Hierarchical Cell85 个Hierarchical Port5,373 个Leaf Cell16,473 个Buf/Inv Cell2,815 个Total Number of Nets18,037 个设计违例 ✅ 电路逻辑综合结果中无设计违例 五、物理实现与结果分析 5.1 ICC 设计步骤 使用 Synopsys IC Compiler (ICC) 进行物理实现,分为四个阶段: pasted_1785034197720_i2g0so.png图片 5.1.1 数据设定 (Data Setup) 读入逻辑和时序库文件、SDC 约束文件、门级网表,以及 IO 物理库文件、TF 工艺文件、TLU+ 文件等物理数据。 5.1.2 布局规划 (Floorplanning) 定义所有管脚(输入输出、时钟、初始信号)及电源组 添加电源环,生成基本布局规划 尝试标准单元虚拟布局(create_fp_placement -timing_driven) 分析拥塞(面积利用率设置不高,拥塞问题较少) 形成电源网络,分析时序,调整电源管脚位置使电压降 < 10% 5.1.3 布局 (Placement) 遵循 NDR 规则,特殊信号线(如 clock)加宽、加大间距 执行 place_opt 进行布局 布局优化(解决建立时间违例) 拥塞分析与优化 5.1.4 时钟树综合 (CTS) 生成时钟网络,加入 buffer 解决负载和 slew time 问题 执行 clock_opt 单时钟同步设计,CTS 工作较简单 5.1.5 布线 (Routing) 全局布线 → 轨道分配 → DRC 修复 先对时钟布线(route_group -all_clock_nets) 解决 verify_lvs 报错(查阅资料后确认无实质影响) 解决虚拟机内存不足导致的闪退问题(清理空间后完成) 5.2 ICC 设计结果 5.2.1 芯片概貌 图 6 芯片概貌 pasted_1785032971553_0fu3op.png图片 顶部和底部各 22 个管脚 左边和右边各 23 个管脚 共 10 组电源管脚:4 组为管脚供电(分列四周),6 组为内核供电(上下各 1 组,左右各 2 组) 5.2.2 时序结果 阶段input (ns)output (ns)clk (ns)data_setup——0.48data_setup_zic1.430.481.19floorplan0.95-1.02-1.39placement1.010.590.19cts_only_psyn1.260.820.62cts_only_cts1.260.830.36route_initial1.350.880.65route_final1.350.850.015.2.3 面积结果 图 7 芯片面积测量 pasted_1785033659880_dlug93.png图片 | 项目 | 面积或长度 |Combinational Area1,477,888.21 μm²Non-combinational Area154,248.50 μm²Net Area0.00 μm²Net X Length620,954.62 nmNet Y Length666,869.44 nmCell Area1,632,136.71 μm²Design Area1,632,136.71 μm²Net Length1,287,824.00 nmTotal Area4,695,300.72 μm²芯片尺寸:2092.38 μm × 2244.00 μm 5.2.4 功耗结果 项目数值Global Operating Voltage1.62 VCell Internal Power33.6445 mWNet Switching Power12.7067 mWTotal Dynamic Power44.3512 mWCell Leakage Power26.0446 μW5.2.5 芯片压降 图 8 芯片压降分析 pasted_1785033682302_ytnfda.png图片 工作电压:1.62 V 最大压降:129.6 mV 压降比例:129.6 / 1620 = 8% < 10%,符合设计要求 5.2.6 拥塞分析 图 9 拥塞分析(Placement / Detail Route / Track Assignment / Global Route) pasted_1785033720429_zl0o70.png图片 由于芯片面积较大,面积利用率设置不高,拥塞问题较少,顺利解决。5.2.7 资源使用 项目数量或利用率Module Cells16,240 个Pins91,614 个IO Pad Cells90 个IO Pins70 个Nets18,151 个Average Pins Per Net3.0535 个Total Std Cell Area514,936.70 μm²Total Pad Cell Area1,612,800.00 μm²Core Size1612.38 × 1764.00 = 2,844,238.32 μm²Pad Core Size1672.38 × 1824.00 = 3,050,421.12 μm²Chip Size2092.38 × 2244.00 = 4,695,300.72 μm²Std cells utilization18.10%Cell/Core Ratio18.10%Cell/Chip Ratio45.32%Number of Cell Rows350图 10 单元密度分布 pasted_1785033741857_8nbcxl.png图片5.2.8 设计违例 ✅ 最终设计无设计违例 六、设计总结 6.1 遇到的主要问题 管脚数量过多,芯片面积过大 电路计算结果不正确 时序模块数据传输不同步,拖慢运算速度 芯片工作频率过低 芯片电压降问题 6.2 相应的解决思路 问题解决方案管脚过多、面积过大采用串并转换减少管脚;改用基-4 FFT 算法并复用蝶形运算模块减小面积计算结果不正确手动计算关键中间数据,逐一比对各模块输入输出,逐个修复 bug数据传输不同步在时序电路传输标志信号的同时,用组合逻辑电路传输数据,避免同步问题工作频率过低重写乘法器:将乘法运算改为组合逻辑状态机判断旋转因子,采用移位方法完成乘法,频率从 82 MHz 提升至 135 MHz电压降问题增加供电管脚数量并调整位置,最大压降控制在 8%(何卫锋老师指导)6.3 关键优化点 乘法器优化(频率提升核心) 优化前: assign mul = in_17bit[16:0] * in_8bit[7:0]; // 直接乘法,延迟大优化后: always @ (in_17bit or in_8bit) begin case (in_8bit) 8'b00000000: neg_mul = 25'b0; 8'b01111111: neg_mul = (in_17bit << 7); // ... 其他旋转因子用移位实现 endcase end通过 set_flatten true 和 compile –map high 策略,将工作频率从 82 MHz 大幅提升至 135 MHz。 七、参考资料 [1] FFT 算法相关教材与文献 [2] Synopsys Design Compiler 用户手册 [3] Synopsys IC Compiler 用户手册 [4] SMIC 0.18 μm 工艺设计套件文档 项目成果总结: 本项目完整实践了数字 IC 设计的前后端全流程,从 RTL 编码、功能仿真、逻辑综合到物理实现,成功将 16 点基-4 FFT 算法芯片化。通过架构优化(串并转换、流水线)、算法优化(基-4 复用)、电路优化(组合逻辑移位乘法器),在 SMIC 0.18 μm 工艺下实现了 135 MHz 工作频率、4.70 mm² 面积、5.25×10⁻⁶ mJ 单次运算能耗的优异性能指标。八、项目工程下载(源码、文档和参考文献) 包含:前端RTL源码、中端DC源码脚本、后端ICC源码脚本、项目报告、参考文件等
FPGA&ASIC
IP&SOC设计
VLSI&IC验证
# VLSI
# ASIC/FPGA
# TCL脚本
# SOC设计
刘航宇
1天前
0
17
4
TCL脚本语言用法简介
前言(TCL综述) TCL(Tool Command Language)是一种解释执行的脚本语言(Scripting Language)。 它提供了 通用的编程能力:支持变量、过程和控制结构;同时 TCL还拥有一个功能强大的固有的核心命令集。 由于TCL的解释器是用一个C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作一个C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,可以很容易就在C\C++应用程序中嵌入TCL,而且每个应用程序都可以根据自己的需要对TCL语言进行扩展。我们可以针对某一特定应用领域对TCL语言的核心命令集进行扩展,加入适合于自己的应用领域的扩展命令,如果需要,甚至可以加入新的控制结构,TCL解释器将把扩展命令和扩展控制结构与固有命令和固有控制结构同等看待。扩展后的TCL语言将可以继承TCL 核心部分的所有功能,包括核心命令、控制结构、数据类型、对过程的支持等。根据需要,我们甚至可以屏蔽掉TCL的某些固有命令和固有控制结构。通过对TCL的扩展、继承或屏蔽,用户用不着象平时定义一种计算机语言那样对词法、语法、语义、语用等各方面加以定义,就可以方便的为自己的应用领域提供一种功能完备的脚本语言。 TCL良好的可扩展性使得它能很好地适应产品测试的需要,测试任务常常会由于设计和需求的改变而迅速改变,往往让测试人员疲于应付。利用TCL的可扩展性,测试人员就可以迅速继承多种新技术,并针对产品新特点迅速推出扩展TCL命令集,以用于产品的测试中,可以较容易跟上设计需求的变化。 另外,因为TCL是一种比C\C++ 语言有着更高抽象层次的语言,使用TCL可以在一种更高的层次上编写程序,它屏蔽掉了编写C\C++程序时必须涉及到的一些较为烦琐的细节,可以大大地提高开发测试例的速度。而且, 使用TCL语言写的测试例脚本,即使作了修改,也用不着重新编译就可以调用TCL解释器直接执行。可以省却不少时间。TCL 目前已成为自动测试中事实上的标准。 目录 前言(TCL综述) 语法脚本,命令和单词符号 置换(substitution) 变量置换variable subtitution 命令置换command substitution反斜杠置换backslash substitution 双引号和花括号 变量简单变量 数组 append和incr expr List concat lindex llength linsert lreplace lrange lappend lsearch 控制流 if 循环命令:while 、for 、 foreach while for break和continue命令 switch source 过程(procedure) 附录(Tcl的安装) 直接打开终端(terminal),输入 sudo apt install tcl即可进行安装,这里的截图是笔者安装成功后的实例。 图片 之后输入tclsh即可 语法 脚本,命令和单词符号 一个TCL脚本可以包含一个或多个命令。命令之间必须用换行符或分号隔开,下面的两个脚本都是合法的: set a 1 set b 2 或使用分号隔开 set a 1;set b 2TCL解释器对一个命令的求值过程分为两部分:分析和执行。在分析阶段,TCL 解释器运用规则把命令分成一个个独立的单词,同时进行必要的置换(substitution); 在执行阶段,TCL 解释器会把第一个单词当作命令名,并查看这个命令是否有定义,如果有定义就激活这个命令对应的C/C++过程,并把所有的单词作为参数传递给该命令过程,让命令过程进行处理。 置换(substitution) TCL解释器在分析命令时,把所有的命令参数都当作字符串看待,例如: %set x 10 //定义变量x,并把x的值赋为10 10 %set y x+100 //y的值是x+100,而不是我们期望的110 x+100上例的第二个命令中,x被看作字符串x+100的一部分,如果我们想使用x的值’10’ ,就必须告诉 TCL解释器:我们在这里期望的是变量x的值,而非字符’x’。怎么告诉TCL解释器呢,这就要用到TCL语言中提供的置换功能。置换功能分为三种.TCL提供三种形式的置换:变量置换、命令置换和反斜杠置换。每种置换都会导致一个或多个单词本身被其他的值所代替。置换可以发生在包括命令名在内的每一个单词中,而且置换可以嵌套。 变量置换variable subtitution 变量置换由一个$符号标记,变量置换会导致变量的值插入一个单词中。例如之前的一个例子 %set x 10 //定义变量x,并把x的值赋为10 10 %set y x+100 //y的值是x+100,而不是我们期望的110 x+100 %set y $x+100 //y的值是我们期望的110 110命令置换command substitution 命令置换是由[]括起来的TCL命令及其参数,命令置换会导致某一个命令的所有或部分单词被另一个命令的结果所代替。例如: %set y [expr $x+100] 110这里当TCL解释器遇到字符’[‘时,它就会把随后的expr作为一个命令名,从而激活与expr对应的C/C++过程,并把expr和变量置换后得到的10+100传递给该命令过程进行处理。 反斜杠置换backslash substitution TCL语言中的反斜杠置换类似于C语言中反斜杠的用法,主要用于在单词符号中插入诸如换行符、空格、[、$等被TCL解释器当作特殊符号对待的字符。 %set msg money\ \$3333\ \nArray\ a\[2] //这个命令的执行结果为: money $3333 Array a[2]双引号和花括号 除了使用反斜杠外,TCL提供另外两种方法来使得解释器把分隔符和置换符等特殊字符当作普通字符,而不作特殊处理,这就要使用双引号和花括号({})。 TCL解释器对双引号中的各种分隔符将不作处理,但是对换行符 及$和[]两种置换符会照常处理。而在花括号中,所有特殊字符都将成为普通字符,失去其特殊意义,TCL解释器不会对其作特殊处理。 %set y "$x ddd" 100 ddd %set y {/n$x [expr 10+100]} /n$x [expr 10+100]注释 TCL中的注释符是#,#和直到所在行结尾的所有字符都被TCL看作注释,TCL解释器对注释将不作任何处理。不过,要注意的是,#必须出现在TCL解释器期望命令的第一个字符出现的地方,才被当作注释。 %set a 100 # Not a comment wrong # args: should be "set varName ?newValue?" %set b 101 ; # this is a comment 101变量 变量分为简单变量和数组 简单变量 一个 TCL 的简单变量包含两个部分:名字和值。名字和值都可以是任意字符串。 % set a 2 2 set a.1 4 4 % set b $a.1 2.1在最后一个命令行,我们希望把变量a.1的值付给b,但是TCL解释器在分析时只把$符号之后直到第一个不是字母、数字或下划线的字符(这里是’.’)之间的单词符号(这里是’a’)当作要被置换的变量的名字,所以TCL解释器把a置换成2,然后把字符串“2.1”付给变量b。这显然与我们的初衷不同。 当然,如果变量名中有不是字母、数字或下划线的字符,又要用置换,可以用花括号把变量名括起来。例如: %set b ${a.1} 4数组 数组是一些元素的集合。TCL的数组和普通计算机语言中的数组有很大的区别。在TCL中,不能单独声明一个数组,数组只能和数组元素一起声明。数组中,数组元素的名字包含两部分:数组名和数组中元素的名字,TCL中数组元素的名字(下标〕可以为任何字符串。 例如: set day(monday) 1 set day(tuesday) 2 set a monday set day(monday) 1 set b $day(monday) //b 的值为 1 ,即 day(monday) 的值。 set c $day($a) //c 的值为 1 ,即 day(monday) 的值。其他命令 unset % unset a b day(monday)上面的语句中删除了变量a、b和数组元素day(monday),但是数组day并没有删除,其他元素还存在,要删除整个数组,只需给出数组的名字。 append和incr 这两个命令提供了改变变量的值的简单手段。 append命令把文本加到一个变量的后面,例如: % set txt hello hello % append txt "! How are you" hello! How are youincr命令把一个变量值加上一个整数。incr要求变量原来的值和新加的值都必须是整数。 expr 可以进行基本的数学函数计算 %expr 1 + 2*3 7List list这个概念在TCL中是用来表示集合的。TCL中list是由一堆元素组成的有序集合,list可以嵌套定 义,list每个元素可以是任意字符串,也可以是list。下面都是TCL中的合法的list: {} //空list {a b c d} {a {b c} d} //list可以嵌套list是TCL中比较重要的一种数据结构,对于编写复杂的脚本有很大的帮助 list 语法: list ? value value…? 这个命令生成一个list,list的元素就是所有的value。例: % list 1 2 {3 4} 1 2 {3 4}使用置换将其相结合 % set a {1 2 3 4 {1 2}} 1 2 3 4 {1 2} % puts $a 1 2 3 4 {1 2}concat 语法:concat list ?list…? 这个命令把多个list合成一个list,每个list变成新list的一个元素。 % set a {1 2 3} 1 2 3 % set b {4 5 6} 4 5 6 % concat $a $b 1 2 3 4 5 6lindex 语法:lindex list index 返回list的第index个(0-based)元素。例: % lindex {1 2 {3 4}} 2 3 4llength 语法:llength list 返回list的元素个数。例 % llength {1 2 {3 4}} 3 % set a {1 2 3} 1 2 3 % llength $a 3linsert 语法:linsert list index value ?value…? 返回一个新串,新串是把所有的value参数值插入list的第index个(0-based)元素之前得到。例: % linsert {1 2 {3 4}} 1 7 8 {9 10} 1 7 8 {9 10} 2 {3 4} % linsert {1 2 {3 4}} 1 {1 2 3 {4 5}} 1 {1 2 3 {4 5}} 2 {3 4} % set a {1 2 3} 1 2 3 % linsert $a 1 {2 3 4} 1 {2 3 4} 2 3lreplace 语法:lreplace list first last ?value value …? 返回一个新串,新串是把list的第firs (0-based)t到第last 个(0-based)元素用所有的value参数替换得到的。如果没有value参数,就表示删除第first到第last个元素。例: % lreplace {1 7 8 {9 10} 2 {3 4}} 3 3 1 7 8 2 {3 4} % lreplace {1 7 8 2 {3 4}} 4 4 4 5 6 1 7 8 2 4 5 6 % set a {1 2 3} 1 2 3 % lreplace $a 1 2 4 5 6 7 1 4 5 6 7 % lreplace $a 1 end 1lrange 语法:lrange list first last 返回list的第first (0-based)到第last (0-based)元素组成的串,如果last的值是end。就是从第first个直到串的最后。 例: % lrange {1 7 8 2 4 5 6} 3 end 2 4 5 6 % set a {1 2 3} 1 2 3 % lrange $a 0 end 1 2 3lappend 语法:lappend varname value ?value…? 把每个value的值作为一个元素附加到变量varname后面,并返回变量的新值,如果varname不存在,就生成这个变量。例: % set a {1 2 3} 1 2 3 % lappend a 4 5 6 1 2 3 4 5 6lsearch 语法:lsearch ?-exact? ?-glob? ?-regexp? list pattern 返回list中第一个匹配模式pattern的元素的索引,如果找不到匹配就返回-1。-exact、-glob、 -regexp是三种模式匹配的技术。-exact表示精确匹配;-glob的匹配方式和string match命令的匹配方式相同;-regexp表示正规表达式匹配。缺省时使用-glob匹配。例: % set a { how are you } how are you % lsearch $a y* 2 % lsearch $a y? -1-all 返回一个列表,返回的列表中的数值就是字符在列表中的位置 默认全局匹配,返回第一个字符在列表中的位置,其位缺省状态 % lsearch {a b c d e} c 2 % lsearch -all {a b c a b c} c 2 5 % lsearch {a b c d c} c 2匹配不到返回-1 % lsearch {a b c d e} g -1控制流 主要是对于所有的控制流,包括 if、while、for、foreach、switch、break、continue 等以及过程, if 语法: if test1 body1 ?elseif test2 body2 elseif…. ? ?else bodyn? TCL先把test1当作一个表达式求值,如果值非0,则把body1当作一个脚本执行并返回所得值,否则把test2当作一个表达式求值,如果值非0,则把body2当作一个脚本执行并返回所得值……。例如: if { $x>0 } { ..... }elseif{ $x==1 } { ..... }elseif { $x==2 } { .... }else{ ..... }if { $x<0 } { puts "x is smaller than zero" } elseif {$x==1} { puts "x is equal 1" } elseif {$x==2} { puts "x is equal 2" } else { puts "x is other" } 这里需要注意的是, if 和{之间应该有一个空格,否则TCL解释器会把’if{‘作为一个整体当作一个命令名,从而导致错误。 ‘{‘一定要写在上一行,因为如果不这样,TCL 解释器会认为if命令在换行符处已结 束,下一行会被当成新的命令,从而导致错误的结果需要将}{ 分开写, 否则会报错extra characters after close-brace 循环命令:while 、for 、 foreach while 语法为: while test body 参数test是一个表达式,body是一个脚本,如果表达式的值非0,就运行脚本,直到表达式为0才停止循环,此时while命令中断并返回一个空字符串。 例如:假设变量 a 是一个链表,下面的脚本把a 的值复制到b: % #首先生成一个集合 % set a {1 2 3 4} 1 2 3 4 % set b " " % #计算生成集合的长度(从0开始这里需要减去1例如:0-3一共有四个数) % set i [expr [llength $a] -1] 3 #接下来进行判断,将集合a中的元素全部按顺序写入b中 % while {$i>=0} { #思考执行该行代码替换会有怎样的结果打印出来 #lappend b [lindex $a $i] lappend b [lindex $a [expr [llength $a] - 1 - $i]] incr i -1 } #打印观察结果 % puts $b 1 2 3 4对代码进行分析 set 变量a为一个list,b为一个空list 然后计算列表里有几个元素,将其减一后的值赋值给i,这里减一的目的是从零开始计数会多一个 开始进行循环,首先i的值是4大于0,表达式为真,开始执行脚本。 脚本为将数组a的第i个位置的元素添加到b list 里,然后给i减一同时进行下一次判断即可。 最后输出b的值 for 语法为: for init test reinit body 参数init是一个初始化脚本,第二个参数test是一个表达式,用来决定循环什么时候中断,第三个参数reinit是一个重新初始化的脚本,第四个参数body也是脚本,代表循环体。下例与上例作用相同:(注意这里复制打印顺序的不同) % set a {1 2 3 4} 1 2 3 4 % set b " " % for {set i [expr [llength $a] -1]} {$i>=0} {incr i -1} { lappend b [lindex $a $i] } % puts $b 4 3 2 1例 % for {set i 0} {$i<4} {incr i} { puts "I is: $i " } I is: 0 I is: 1 I is: 2 I is: 3 foreach 这个命令有两种语法形式 1, foreach varName list body 第一个参数varName是一个变量,第二个参数list 是一个表(有序集合),第三个参数body是循环体。每次取得链表的一个元素,都会执行循环体一次。 下例与上例作用相同: % set a {1 2 3 4} 1 2 3 4 % set b " " % foreach i $a { set b [linsert $b 0 $i] } % puts $b 4 3 2 1% foreach var {a b c d e f} { puts $var } a b c d e f2, foreach varlist1 list1 ?varlist2 list2 ...? Body 这种形式包含了第一种形式。第一个参数varlist1是一个循环变量列表,第二个参数是一个列表list1,varlist1中的变量会分别取list1中的值。body参数是循环体。 ?varlist2 list2 …?表示可以有多个变量列表和列表对出现。例如: set x {} foreach {i j} {a b c d e f} { lappend x $j $i }这时总共有三次循环,x的值为”b a d c f e”。 % foreach i {a b c} j {d e f g} { puts $i puts $j } a d b e c f gset x {} foreach i {a b c} j {d e f g} { lappend x $i $j }这时总共有四次循环, x的值为”a d b e c f {} g set x {} foreach i {a b c} {j k} {d e f g} { lappend x $i $j $k }这时总共有三次循环,x的值为”a d e b f g c {} {}”。 例子: 图片 break和continue命令 在循环体中,可以用break和continue命令中断循环。其中break命令结束整个循环过程,并从循环中跳出,continue只是结束本次循环 这里有一个特别好的例子 说明:这里首先进行给一个list,然后使用foreach循环进行写入数据当遇见break时候直接退出了循环,而continue仅仅只是跳出此次循环继续向b里写入数 % set b {} % set a {1 2 3 4 5} 1 2 3 4 5 % foreach i $a { if {$i == 4} break set b [linsert $b 0 $i] } % puts $b 3 2 1% set b {} % set a {1 2 3 4 5} 1 2 3 4 5 % foreach i $a { if {$i == 4} continue set b [linsert $b 0 $i] } % puts $b 5 3 2 1switch 和 C 语言中 switch 语句一样,TCL 中的 switch 命令也可以由 if 命令实现。只是书写起来较为烦琐。 switch 命令的语法为: switch ? options? string { pattern body ? pattern body …?} 注意这里进行的是字符匹配 图片 set x a; set t1 0;set t2 0;set t3 0; switch $x { a - b {incr t1} c {incr t2} default {incr t3} } puts "t1=$t1,t2=$t2,t3=$t3"x=a时执行的是t1加2 其中 a 的后面跟一个’-’表示使用和下一个模式相同的脚本。default 表示匹配任意值。一旦switch 命令 找到一个模式匹配,就执行相应的脚本,并返回脚本的值,作为 switch 命令的返回值。 source source 命令读一个文件并把这个文件的内容作为一个脚本进行求值 以上边的switch第一段代码为例 使用VIM新建一个文件,写入文件后保存退出 vim switch1.tcl键入wish然后输入source switch1.tcl 图片 过程(procedure) TCL 支持过程的定义和调用,在 TCL 中,过程可以看作是用 TCL 脚本实现的命令,效果与 TCL的固有命令相似。我们可以在任何时候使用 proc 命令定义自己的过程,TCL 中的过程类似于 C中的函数。 TCL 中过程是由 proc 命令产生的: 例如: % proc add {x y } {expr $x+$y} roc 命令的第一个参数是你要定义的过程的名字,第二个参数是过程的参数列表,参数之间用空格隔开,第三个参数是一个 TCL 脚本,代表过程体。 proc 生成一个新的命令,可以象固有命令一样调用: % add 1 2 3
编程&脚本笔记
# TCL脚本
刘航宇
3年前
0
1,767
0