我的学记|刘航宇的博客 - 编程&脚本笔记
type: channel
link: https://ee.ac.cn/index.php/category/%E7%BC%96%E7%A8%8B-%E8%84%9A%E6%9C%AC%E7%AC%94%E8%AE%B0/
items: C语言编译的四个步骤
items: 面向对象在编程中的概念
items: 华为C++算法-识别有效的IP地址和掩码并进行分类统计
items: 算法-反转链表C&Python实现
items: 嵌入式软件-无需排序找数字
items: 嵌入式软件-基于C语言小端转大端
items: Verilog-位宽计算的系统函数$clog2
items: VCS、Verdi与Makefile使用简介
items: TCL脚本语言用法简介
items: 图像处理中VS2019下载及其MFC环境配置

C语言编译的四个步骤
type: item
date: 2024-03-12T22:38:36+08:00
description: 编译一个C语言程序是一个多阶段的过程。从总体上看，这个过程可以分成四个独立的阶段。预处理、编译、汇编和连接。在这篇文章中，我将逐一介绍编译下列C程序的四个阶段。/* * "Hello, World!": A classic. */ #include <stdio.h> int main(void) { puts("Hello, World!"); return 0; }预处理编译的第一个阶段称为预处理。在这个阶段，以#字符开头的行被预处理器解释为预处理器命令。这些命令形成一种简单的宏语言，有自己的语法和语义。这种语言通过提供内联文件、定义宏和有条件地省略代码的功能，来减少源代码的重复性。在解释命令之前，预处理器会做一些初始处理。这包括连接续行（以 \ 结尾的行）和剥离注释。要打印预处理阶段的结果，请向gcc传递-E选项。 gcc -E hello_world.c 考虑到上面的 "Hello, World!"的例子，预处理器将产生stdio.h头文件的内容和hello_world.c文件的内容，并将其前面的注释剥离出来。编译编译的第二个阶段被称为编译，令人困惑。在这个阶段，预处理过的代码被翻译成目标处理器架构特有的汇编指令。这些形成了一种中间的人类可读语言。这一步骤的存在允许C代码包含内联汇编指令，并允许使用不同的汇编器。一些编译器也支持使用集成汇编器，在这种情况下，编译阶段直接生成机器代码，避免了生成中间汇编指令和调用汇编器的开销。要保存编译阶段的结果，可以向gcc传递-S选项。 gcc -S hello_world.c 这将创建一个名为hello_world.s的文件，包含生成的汇编指令。汇编在这个阶段，汇编器被用来将汇编指令翻译成目标代码。输出包括目标处理器要运行的实际指令。要保存汇编阶段的结果，请向gcc传递-c选项。 gcc -c hello_world.c运行上述命令将创建一个名为hello_world.o的文件，包含程序的目标代码。这个文件的内容是二进制格式，可以用运行命令hexdump或od来检查。hexdump hello_world.o od -c hello_world.oLinux中的od(octal dump)命令用于转换输入内容为八进制。Hexdump是一个命令行工具，用于以各种方式显示文件的原始内容，包括十六进制，可用于Linux、FreeBDS、OS X和其他平台。Hexdump不是传统Unix系统或GNU命令的一部分。链接汇编阶段产生的目标代码是由处理器能够理解的机器指令组成的，但程序的某些部分是不符合顺序的或缺失的。为了产生一个可执行的程序，现有的部分必须被重新排列，并把缺失的部分补上。这个过程被称为链接。链接器将安排目标代码的各个部分，使某些部分的功能能够成功地调用其他部分的功能。它还将添加包含程序所使用的库函数指令的片段。在 "Hello，world！"程序的例子中，链接器将添加puts函数的对象代码。这一阶段的结果是最终的可执行程序。当不使用选项运行时，gcc 将把这个文件命名为 a.out。如果要给文件命名，请向 gcc 传递 -o 选项。gcc -o hello_world hello_world.c

面向对象在编程中的概念
type: item
date: 2023-12-25T22:21:00+08:00
description: 前言那么究竟什么是面向对象呢？面向过程面向对象面向对象的三大特性封装继承多态面向对象的五大基本原则单一职责原则（Single-Responsibility Principle）开放封闭原则（Open-Closed principle）里氏替换原则（Liskov-Substitution Principle）依赖倒置原则（Dependecy-Inversion Principle）接口隔离原则（Interface-Segregation Principle）面向对象编程的用途面向对象编程的应用领域前言在刚接触java、C++、Python语言的时候，就知道这是一门面向对象的语言。学不好java的原因找到了，面向对象的语言，没有对象怎么学 ::(狗头) 那么究竟什么是面向对象呢？面向对象，Object Oriented Programming，简称为OOP。说到面向对象，不得不提一嘴面向过程。面向过程面向过程是一种自顶而下的编程模式。把问题分解成一个一个步骤，每个步骤用函数实现，依次调用即可。举个生活中的例子，假如你想吃红烧肉，你需要买肉，买调料，洗肉，切肉，烧肉，装盘。需要我们具体每一步去实现，每个步骤相互协调，最终盛出来的才是正宗好吃的红烧肉。面向对象面向对象就是将问题分解成一个一个步骤，对每个步骤进行相应的抽象，形成对象，通过不同对象之间的调用，组合解决问题。还是你想吃红烧肉这个例子，不过这次不同的是你发现了一家餐馆里有红烧肉这道菜，你要做的只是去点菜，就可以吃到红烧肉。针不戳，针不戳，面向对象针不戳，你不用再去关心红烧肉繁琐的制作流程，就能吃到美味的红烧肉。我们接着往下看面向对象的三大特性封装封装就是把客观的事物封装成抽象的类，并且类可以把自己的数据和方法只让可信的类或者对象操作，对不可信的类进行信息的隐藏。简单的说就是：封装使对象的设计者与对象的使用者分开，使用者只要知道对象可以做什么就可以了，不需要知道具体是怎么实现的。封装可以有助于提高类和系统的安全性这里有点像FPGA的IP了，电子人懂！继承当多个类中存在相同属性和行为时，将这些内容就可以抽取到一个单独的类中，使得多个类无需再定义这些属性和行为，只需继承那个类即可。通过继承创建的新类称为“子类”或“派生类”，被继承的类称为“基类”、“父类”或“超类”多态多态同一个行为具有多个不同表现形式或形态的能力。是指一个类实例（对象）的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着，虽然针对不同对象的具体操作不同，但通过一个公共的类，它们（那些操作）可以通过相同的方式予以调用。菜鸟教程中的例子就很形象{message type="success" content="现实中，比如我们按下 F1 键这个动作：如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档；如果当前在 Word 下弹出的就是 Word 帮助；在 Windows 下弹出的就是 Windows 帮助和支持。同一个事件发生在不同的对象上会产生不同的结果。"/}面向对象的五大基本原则单一职责原则（Single-Responsibility Principle）简而言之，就是一个类最好只有一个能引起变化的原因，只做一件事，单一职责原则可以看做是低耦合高内聚思想的延伸，提高高内聚来减少引起变化的原因。开放封闭原则（Open-Closed principle）简而言之，就是软件实体应该是可扩展的，但是不可修改。因为修改程序有可能会对原来的程序造成错误。即对扩展开放，对修改封闭里氏替换原则（Liskov-Substitution Principle）简而言之，就是子类一定可以替换父类，子类包含其基类（父类）的功能依赖倒置原则（Dependecy-Inversion Principle）高层次的模块不应该依赖于低层次的模块，他们都应该依赖于抽象。抽象不应该依赖于具体实现，具体实现应该依赖于抽象。依赖于抽象即对接口编程，不要对实现编程接口隔离原则（Interface-Segregation Principle）简而言之，就是使用多个小的专门的接口，而不要使用一个大的总接口。例如将一个房子再分割成卧室，厨房，厕所等等，而不是将所有功能放在一起面向对象编程的用途使用封装（信息隐藏）可以对外部隐藏数据使用继承可以重用代码使用多态可以重载操作符/方法/函数，即相同的函数名或操作符名称可用于多种任务数据抽象可以用抽象实现项目易于迁移（可以从小项目转换成大项目）同一项目分工软件复杂性可控面向对象编程的应用领域人工智能与专家系统企业级应用神经网络与并行编程办公自动化系统

华为C++算法-识别有效的IP地址和掩码并进行分类统计
type: item
date: 2023-12-11T22:36:00+08:00
description: 问题注意：输入描述：输出描述：示例需要注意的细节思路具体实现代码问题请解析IP地址和对应的掩码，进行分类识别。要求按照A/B/C/D/E类地址归类，不合法的地址和掩码单独归类。所有的IP地址划分为 A,B,C,D,E五类A类地址从1.0.0.0到126.255.255.255;B类地址从128.0.0.0到191.255.255.255;C类地址从192.0.0.0到223.255.255.255;D类地址从224.0.0.0到239.255.255.255；E类地址从240.0.0.0到255.255.255.255私网IP范围是：从10.0.0.0到10.255.255.255从172.16.0.0到172.31.255.255从192.168.0.0到192.168.255.255子网掩码为二进制下前面是连续的1，然后全是0。（例如：255.255.255.32就是一个非法的掩码）（注意二进制下全是1或者全是0均为非法子网掩码）注意：类似于【0...】和【127...】的IP地址不属于上述输入的任意一类，也不属于不合法ip地址，计数时请忽略私有IP地址和A,B,C,D,E类地址是不冲突的输入描述：多行字符串。每行一个IP地址和掩码，用~隔开。输出描述：统计A、B、C、D、E、错误IP地址或错误掩码、私有IP的个数，之间以空格隔开。示例{message type="success" content="输入：10.70.44.68~255.254.255.01.0.0.1~255.0.0.0192.168.0.2~255.255.255.019..0.~255.255.255.0输出：1 0 1 0 0 2 1说明：10.70.44.68~255.254.255.0的子网掩码非法，19..0.~255.255.255.0的IP地址非法，所以错误IP地址或错误掩码的计数为2；1.0.0.1~255.0.0.0是无误的A类地址；192.168.0.2~255.255.255.0是无误的C类地址且是私有IP；所以最终的结果为1 0 1 0 0 2 1 "/}示例2{message type="success" content="输入：0.201.56.50~255.255.111.255127.201.56.50~255.255.111.255输出：0 0 0 0 0 0 0说明：类似于【0...】和【127...】的IP地址不属于上述输入的任意一类，也不属于不合法ip地址，计数时请忽略 "/}需要注意的细节类似于【0...】和【127...】的IP地址不属于上述输入的任意一类，也不属于不合法ip地址，计数时可以忽略私有IP地址和A,B,C,D,E类地址是不冲突的，也就是说需要同时+1如果子网掩码是非法的，则不再需要查看IP地址全零【0.0.0.0】或者全一【255.255.255.255】的子网掩码也是非法的思路按行读取输入，根据字符‘~’ 将IP地址与子网掩码分开查看子网掩码是否合法。合法，则继续检查IP地址非法，则相应统计项+1，继续下一行的读入查看IP地址是否合法合法，查看IP地址属于哪一类，是否是私有ip地址；相应统计项+1非法，相应统计项+1具体实现判断IP地址是否合法，如果满足下列条件之一即为非法地址数字段数不为4存在空段，即【192..1.0】这种某个段的数字大于255判断子网掩码是否合法，如果满足下列条件之一即为非法掩码不是一个合格的IP地址在二进制下，不满足前面连续是1，然后全是0在二进制下，全为0或全为1如何判断一个掩码地址是不是满足前面连续是1，然后全是0？将掩码地址转换为32位无符号整型，假设这个数为b。如果此时b为0，则为非法掩码将b按位取反后+1。如果此时b为1，则b原来是二进制全1，非法掩码如果b和b-1做按位与运算后为0，则说明是合法掩码，否则为非法掩码代码注意getline函数可以指定分割字符串的字符// 引入输入输出流、字符串、字符串流和向量等头文件 #include<iostream> #include<string> #include<sstream> #include<vector> // 使用标准命名空间 using namespace std; // 定义一个函数，判断一个字符串是否是合法的IP地址 bool judge_ip(string ip){ // 定义一个整数变量，记录IP地址的段数 int j = 0; // 定义一个字符串流对象，用于分割IP地址 istringstream iss(ip); // 定义一个字符串变量，用于存储IP地址的每一段 string seg; // 使用循环，以'.'为分隔符，获取IP地址的每一段 while(getline(iss,seg,'.…

算法-反转链表C&Python实现
type: item
date: 2023-07-22T15:21:41+08:00
description: 描述基础数据结构知识回顾题解C++篇题解Python篇描述给定一个单链表的头结点pHead(该头节点是有值的，比如在下图，它的val是1)，长度为n，反转该链表后，返回新链表的表头。数据范围： 0≤n≤1000要求：空间复杂度 O(1) ，时间复杂度 O(n) 。如当输入链表{1,2,3}时，经反转后，原链表变为{3,2,1}，所以对应的输出为{3,2,1}。以上转换过程如下图所示：基础数据结构知识回顾空间复杂度 O (1) 表示算法执行所需要的临时空间不随着某个变量 n 的大小而变化，即此算法空间复杂度为一个常量，可表示为 O (1)。例如，下面的代码中，变量 i、j、m 所分配的空间都不随着 n 的变化而变化，因此它的空间复杂度是 O (1)。int i = 1; int j = 2; ++i; j++; int m = i + j;时间复杂度 O (n) 表示算法执行的时间与 n 成正比，即此算法时间复杂度为线性阶，可表示为 O (n)。例如，下面的代码中，for 循环里面的代码会执行 n 遍，因此它消耗的时间是随着 n 的变化而变化的，因此这类代码都可以用 O (n) 来表示它的时间复杂度。for (i=1; i<=n; ++i) { j = i; j++; }题解C++篇可以先用一个vector将单链表的指针都存起来，然后再构造链表。此方法简单易懂，代码好些。// 定义一个Solution类 class Solution { public: // 定义一个函数，接收一个链表的头节点指针，返回一个反转后的链表的头节点指针 ListNode* ReverseList(ListNode* pHead) { // 如果头节点指针为空，直接返回空指针 if (!pHead) return nullptr; // 定义一个vector，用于存储链表中的每个节点指针 vector<ListNode*> v; // 遍历链表，将每个节点指针放入vector中 while (pHead) { v.push_back(pHead); pHead = pHead->next; } // 反转vector，也可以逆向遍历 reverse(v.begin(), v.end()); // 取出vector中的第一个元素，作为反转后的链表的头节点指针 ListNode *head = v[0]; // 定义一个当前节点指针，初始化为头节点指针 ListNode *cur = head; // 从第二个元素开始遍历vector，构造反转后的链表 for (int i=1; i<v.size(); ++i) { // 当前节点的下一个指针指向下一个节点 cur->next = v[i]; // 当前节点后移 cur = cur->next; } // 切记最后一个节点的下一个指针指向nullptr cur->next = nullptr; // 返回反转后的链表的头节点指针 return head; } };初始化：3个指针1）pre指针指向已经反转好的链表的最后一个节点，最开始没有反转，所以指向nullptr2）cur指针指向待反转链表的第一个节点，最开始第一个节点待反转，所以指向head3）nex指针指向待反转链表的第二个节点，目的是保存链表，因为cur改变指向后，后面的链表则失效了，所以需要保存接下来，循环执行以下三个操作1）nex = cur->next, 保存作用2）cur->next = pre 未反转链表的第一个节点的下个指针指向已反转链表的最后一个节点3）pre = cur， cur = nex; 指针后移，操作下一个未反转链表的第一个节点循环条件，当然是cur != nullptr循环结束后，cur当然为nullptr，所以返回pre，即为反转后的头结点这里以1->2->3->4->5 举例：// 定义一个Solution类 class Solution { public: // 定义一个函数，接收一个链表的头节点指针，返回一个反转后的链表的头节点指针 ListNode* ReverseList(ListNode* pHead) { // 定义一个前驱节点指针，初始化为nullptr ListNode *pre = nullptr; // 定义一个当前节点指针，初始化为头节点指针 ListNode *cur = pHead; // 定义一个后继节点指针，初始化为nullptr ListNode *nex = nullptr; // 遍历链表，反转每个节点的指向 while (cur) { // 记录当前节点的下一个节点 nex = cur->next; // 将当前节点的下一个指针指向前驱节点 cur->next = pre; //…

嵌入式软件-无需排序找数字
type: item
date: 2023-07-21T14:10:00+08:00
description: 题目示例解答code1code2题目有1000个整数，每个数字都在1～200之间，数字随机排布。假设不允许你使用任何排序方法将这些整数有序化，你能快速找到从0开始的第450小的数字吗？（从小到大第450位）示例{card-describe title="输入"}[184, 87, 178, 116, 194, 136, 187, 93, 50, 22, 163, 28, 91, 60, 164, 127, 141, 27, 173, 137, 12, 169, 168, 30, 183, 131, 63, 124, 68, 136, 130, 3, 23, 59, 70, 168, 194, 57, 12, 43, 30, 174, 22, 120, 185, 138, 199, 125, 116, 171, 14, 127, 92, 181, 157, 74, 63, 171, 197, 82, 106, 126, 85, 128, 137, 106, 47, 130, 114, 58, 125, 96, 183, 146, 15, 168, 35, 165, 44, 151, 88, 9, 77, 179, 189, 185, 4, 52, 155, 200, 133, 61, 77, 169, 140, 13, 27, 187, 95, 140, 196, 171, 35, 179, 68, 2, 98, 103, 118, 93, 53, 157, 102, 81, 87, 42, 66, 90, 45, 20, 41, 130, 32, 118, 98, 172, 82, 76, 110, 128, 168, 57, 98, 154, 187, 166, 107, 84, 20, 25, 129, 72, 133, 30, 104, 20, 71, 169, 109, 116, 141, 150, 197, 124, 19, 46, 47, 52, 122, 156, 180, 89, 165, 29, 42, 151, 194, 101, 35, 165, 125, 115, 188, 57, 144, 92, 28, 166, 60, 137, 33, 152, 38, 29, 76, 8, 75, 122, 59, 196, 30, 38, 36, 194, 19, 29, 144, 12, 129, 130, 177, 5, 44, 164, 14, 139, 7, 41, 105, 19, 129, 89, 170, 118, 118, 197, 125, 144, 71, 184, 91, 100, 173, 126, 45, 191, 106, 140, 155, 187, 70, 83, 143, 65, 198, 108, 156, 5, 149, 12, 23, 29, 100, 144, 147, 169, 141, 23, 112, 11, 6, 2, 62, 131, 79, 106, 121, 137, 45, 27, 123, 66, 109, 17, 83, 59, 125, 38, 63, 25, 1, 37, 53, 100, 180, 151, 69, 72, 174, 132, 82, 131, 134, 95, 61, 164, 200, 182, 100, 197, 160, 174, 14, 69, 191, 96, 127, 67, 85, 141, 91, 85, 177, 143, 137, 108, 46, 157, 180, 19, 88, 13, 149, 173, 60, 10, 137, 11, 143, 188, 7, 102, 114, 173, 122, 56, 20, 200, 122, 105, 140, 12, 141, 68, 106, 29, 128, 151, 185, 59, 121, 25, 23, 70, 197, 82, 31, 85, 93, 173, 73, 51, 26, 186, 23, 100, 41, 43, 99, 114, 99, 191, 125, 191, 10, 182, 20, 137, 133, 156, 195, 5, 180, 170, 74, 177, 51, 56, 61, 143, 180, 85, 194, 6, 22, 168, 105, 14, 162, 155, 127, 60, 145, 3, 3, 107, 185, 22, 43, 69, 129, 190, 73, 109, 159, 99, 37, 9, 154, 49, 104, 134, 134, 49, 91, 155, …

嵌入式软件-基于C语言小端转大端
type: item
date: 2023-07-19T22:27:00+08:00
description: 意义题目示例解答1.char指针，按字节替换2.利用union联合体共用内存空间特性，使用char数组来改变3. 使用按位与运算保留以获取每个字节，然后按位左移到正确位置并拼接4. 使用预定义好的宏函数意义大端小端转化对嵌入式系统有意义，因为不同的处理器或者通信协议可能采用不同的字节序来存储或者传输数据。字节序是指一个多字节数据在内存中的存放顺序，它有两种主要的形式：大端：最高有效位（MSB）存放在最低的内存地址，最低有效位（LSB）存放在最高的内存地址。小端：最低有效位（LSB）存放在最低的内存地址，最高有效位（MSB）存放在最高的内存地址。例如，一个32位的整数0x12345678，在大端系统中，它的内存布局是：而在小端系统中，它的内存布局是：如果一个嵌入式系统需要和不同字节序的设备或者网络进行交互，就需要进行字节序的转换，否则会导致数据错误或者通信失败。例如，TCP/IP协议族中的所有层都采用大端字节序来表示数据包头中的16位或32位的值，如IP地址、包长、校验和等。如果一个嵌入式系统使用小端字节序的处理器，并且想要建立一个TCP连接，就需要将IP地址等信息从小端转换为大端再发送出去，否则对方无法正确解析。题目输入一个数字n，假设它是以小端模式保存在机器的，请将其转换为大端方式保存时的值。示例输入：1返回值：16777216解答1.char指针，按字节替换/* * @param n int整型 * @return int整型 */ int convert(int n ) { // write code here int tmp = 0x00000000; //开辟新的int空间用于接收转化结果 unsigned char *p = &tmp, *q = &n; p[0] = q[3]; p[1] = q[2]; p[2] = q[1]; p[3] = q[0]; return tmp; }2.利用union联合体共用内存空间特性，使用char数组来改变/** * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可 * * * @param n int整型 * @return int整型 */ typedef union { int i; unsigned char c[4] } inc_u; int convert(int n ) { // write code here inc_u x; //这里也可以用新开辟空间进行置换 x.i = n; //利用按位异或运算可叠加、可还原性 x.c[0] ^= x.c[3], x.c[3] ^= x.c[0], x.c[0] ^= x.c[3]; //首尾两字节对调 x.c[1] ^= x.c[2], x.c[2] ^= x.c[1], x.c[1] ^= x.c[2]; //中间两字节对调 return x.i; /* 按位<<到正确位置，并用|拼装 return (x.c[0]<<24)|(x.c[1]<<16)|(x.c[2]<<8)|x.c[3]; */ }3. 使用按位与运算保留以获取每个字节，然后按位左移到正确位置并拼接/** * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可 * * * @param n int整型 * @return int整型 */ int convert(int n ) { // write code here return (((n & 0xff000000)>>24) | ((n & 0x00ff0000)>>8 ) | ((n & 0x0000ff00)<<8 ) | ((n & 0x000000ff)<<24); //按位与时，遇0清零，遇1保留 ); }4. 使用预定义好的宏函数本条方法参考 https://www.codeproject.com/Articles/4804/Basic-concepts-on-Endianness文中提到网络上常用的套接字接口（socket API）指定了一种称为网络字节顺序的标准字节顺序，这个顺序其实就是大端模式；而当时，同时代的 x86 系列主机反而是小端模式。所以就促使产生了如：{message type="success" content="ntohs() convert Network order TO Host order in Short (16 bit 大转小);ntohl() convert Network order TO Host order in Long (32 bit 大转小);htons() convert Host order TO Network order in Short (16 bit 小转大);htonl() c…

Verilog-位宽计算的系统函数$clog2
type: item
date: 2023-03-13T23:51:00+08:00
description: 一、什么是$clog2clog2 这是一个系统函数，第一次出现于Verilog-2005版本中，在IEEE中处在17.11.1节的math functions中，因为log2是2进制的对数，所以这个系统函数在电路设计的计算位宽时体现出了自身的方便性，需要注意的是，这里的$clog2是向上取整的一个系统函数,比如{message type="success" content="$clog2(5) 虽然真实的值为2.3，但经过向上取整后，最后的输出为3"/}二、$clog2的优势和案例在老的IEEE verilog版本中，假如不用clog2去计算位宽，我们可能需要如下的function函数来进行位宽计算，这个函数本身很好理解，即通过移位去检测depth的位宽，之后我们需要再将计算得到的数字使用在端口定义的过程中。function integer clog2( input integer depth ); begin if(depth == 0) clog2 = 1; else if(depth != 0) for(clog2 = 0; depth > 0;clog2 = clog2 + 1) depth = depth >> 1; end endfunction但是引入$clog2后，原function可以简化为如下的过程,很显然，通过对系统函数 $clog2的使用，我们大大减少了设计时端口宽度定义时需要code的量。module clog2(a,b); parameter depth = 2034; input [$clog2(depth)-1:0] a; output [$clog2(depth)-1:0]b; //details about the design endmodule 三、额外补充在Xlinix的官网的“44586 - 13.2 Verilog $clog2 function implemented improperly”中，作者发现了13.2版本的Xlinix的ISE对clog2系统函数的错误计算，按照文章中所言：“The $clog2 function returns the ceiling of the logarithm to the base e (natural logarithm) rather than the ceiling of the logarithm to the base 2.”意味着13.2版本的ISE以e为底计算clog2，而非以2为底，官方的回复是ISE 13.2 仅支持Verilog-2001，这个问题在ISE 14.1中进行了修复，所以读者假如使用的开发套件是老版本的，或者不支持Verilog-2005，都有可能因为使用clog2产生问题，需注意。具体额外补充参考如下。44586 - 13.2 Verilog $clog2 function implemented improperly

VCS、Verdi与Makefile使用简介
type: item
date: 2023-02-09T22:12:00+08:00
description: 前期工作1、.fsdb文件在使用Makefile文件前，先在测试文件中加入这样一句。initial begin $fsdbDumpfile("tb.fsdb");//这个是产生名为tb.fsdb的文件 $fsdbDumpvars; end需要注意：对于用于仿真的testbench，需要额外建立一个 initial 块，调用产生有关 fsdb 格式的波形文件：首先调用 fsdbDumpfile 函数，产生一个叫 .fsdb 的波形文件然后调用 fsdbDumpvars 函数，声明需要保存那些信号的波形，括号内不加任何参数，则默认全部保存。2、 filelist.f文件filelist.f里存放所有需要仿真的.v文件。创建filelist.f的方法：find -name "*.v" >filelist.f1. Makefile作用？编写makefile文件本质上是帮组make如何一键编译，进行批处理，makefile文件包含的规则命令使我们不需要繁琐的操作，提高了开发效率。Makefile可以根据指定的依赖规则和文件是否有修改来执行命令。常用来编译软件源代码，只需要重新编译修改过的文件，使得编译速度大大加快。2. Makefile应用利用Makefile 实现简单的前端设计流程，包括VCS编译，Verdi仿真，DC综合，后续流程待补充。目录结构#use "make" for help help: @echo "make help" @echo "make com to compile" @echo "make sim to run simulation" @echo "make clean to delete temporary files" #need to midify design name design_name = div_top fsdb_name = $(design_name).fsdb # use command "make com" to run vsc and product fsdb file com: cd RTL && vcs \ -full64 \ -f flist.f \ -debug_all \ -l com.log \ +v2k \ -P ${Verdi_HOME}/share/PLI/VCS/LINUXAMD64/novas.tab ${Verdi_HOME}/share/PLI/VCS/LINUXAMD64/pli.a # cd RTL && ./simv -l sim.log +fsdbfile+$(fsdb_name) #simulation:product fsdb file and sim log sim: ./RTL/simv cd RTL && ./simv -l sim.log +fsdbfile+$(fsdb_name) # use verdi to observe the waveform verdi: cd RTL && verdi \ +v2k \ -f flist.f \ -ssf $(fsdb_name) & #use fsdb file # run dc for synthesize syn: cd dc_script && dc_shell -64bit -topographical -f top_syn.tcl | tee -i syn.log #delete all files except .v and makefile clean: #rm -rf `ls | grep -v "Makefile"|grep -v "flist.f" | grep -v "\.v" | grep -v "dc_script"` make -C RTL clean make -C dc_script clean/RTL目录下MakeFile#delete temporary files clean: rm -rf `ls | grep -v "Makefile"|grep -v "flist.f" | grep -v "\.v"` dc_script目录下Makefile#delete temporary files clean: rm -rf `ls | grep -v "Makefile"|grep -v "script" | grep -v ".*.tcl"` make com ：调用vcs编译make sim：调用vcs仿真make verdi 波形,shifrt+l可刷新重新编译结果make clean 删除所有子目录下的临时生成文件 详细命令 执行“make vcs” 编译仿真执行“make verdi” 打开波形verdi常用快捷键ctrl+w: 添加信号到波…

TCL脚本语言用法简介
type: item
date: 2023-02-06T13:17:14+08:00
description: 前言（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 目前已成为自动测试中事实上的标准。{mtitle title="目录"/}前言（TCL综述）语法脚本，命令和单词符号置换(substitution)变量置换variable subtitution命令置换command substitution反斜杠置换backslash substitution双引号和花括号变量简单变量数组append和increxprListconcatlindexllengthlinsertlreplacelrangelappendlsearch控制流if循环命令：while 、for 、 foreachwhileforbreak和continue命令switchsource过程（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命令置…

图像处理中VS2019下载及其MFC环境配置
type: item
date: 2021-07-06T12:02:00+08:00
description: 安装环境配置按照教程下载vs软件https://mp.weixin.qq.com/s/dsqCMhO7r8zZ83Fc74gWbQ完成后找到点击下载必须的mfc环境等待下载完毕即可