技术标签: fpga开发 SpinalHDL scala spinalhdl 开发语言
本篇文章仅简单介绍在spinalhdl编程中遇到的比较常见的2中类定义方式:class及case class。对于不太了解JAVA或Scala编码又开始学习SpinalHDL的人进行入门介绍。
在 SpinalHDL 中,case class 和 class 都是用来定义数据结构或对象的关键字,它们在某些方面相似,但也有一些显著的差异。
在 SpinalHDL 中,case class 自动生成的 equals 和 hashCode 函数遵循以下规则:
这样设计的目的是为了确保当两个 case class 的对象具有相同的属性值时,它们的 equals 函数返回 true,并且它们的 hashCode 函数返回的哈希值也相同。
以下是一个详细的例子,假设我们有一个简单的 case class 表示一个二维点:
case class Point(x: Int, y: Int)
对于这个 Point 类,SpinalHDL 会自动生成 equals 和 hashCode 函数。下面是它们的实现:
override def equals(obj: Any): Boolean = obj match {
case other: Point => this.x == other.x && this.y == other.y
case _ => false
}
override def hashCode: Int = {
val prime = 31
var result = 1
result = prime * result + x
result = prime * result + y
result
}
这里的 equals 函数比较了两个 Point 对象的 x 和 y 属性是否相等,而 hashCode 函数则基于 x 和 y 属性计算出一个哈希值。下面是一个示例演示如何使用这个 Point 类及其自动生成的 equals 和 hashCode 函数:
val point1 = Point(3, 4)
val point2 = Point(3, 4)
val point3 = Point(5, 6)
println(point1 == point2) // 输出 true,因为 point1 和 point2 的属性值相等
println(point1 == point3) // 输出 false,因为 point1 和 point3 的属性值不相等
val map = Map(point1 -> "A", point2 -> "B")
println(map(point1)) // 输出 "A",因为 point1 和 point2 被视为相等的键
println(map(point2)) // 输出 "B",因为 point1 和 point2 被视为相等的键
println(map(point3)) // 抛出 NoSuchElementException,因为 point3 不在 map 中
在这个示例中,我们创建了两个 Point 对象 point1 和 point2,它们的属性值相同,因此它们的 equals 函数返回 true。然后我们将这两个对象作为键存储在一个 Map 中,并成功地使用它们来检索相应的值。
在 SpinalHDL 中,case class 提供了一个 copy 函数,用于创建对象的副本并修改其中的属性。这个函数使得我们可以在不修改原始对象的情况下,方便地创建新的对象并修改其中的属性。
下面是 copy 函数的语法:
def copy(...): ThisType
其中,ThisType 是 case class 的类型。
以下是一个示例说明如何使用 case class 的 copy 函数:
假设我们有一个简单的 case class 表示一个二维点:
case class Point(x: Int, y: Int)
现在我们创建一个 Point 对象,然后我们使用 copy 函数创建一个新的 Point 对象,并修改其中的属性:
val originalPoint = Point(3, 4)
val modifiedPoint = originalPoint.copy(x = 5, y = 6)
在这个例子中,originalPoint.copy(x = 5, y = 6) 创建了一个新的 Point 对象,其 x 属性被修改为 5,y 属性被修改为 6。原始的 originalPoint 对象保持不变。
我们也可以只修改其中的部分属性,而不是全部属性。例如,我们可以只修改 x 属性,而保持 y 属性不变:
val modifiedX = originalPoint.copy(x = 10)
这个例子中,originalPoint.copy(x = 10) 创建了一个新的 Point 对象,其 x 属性被修改为 10,而 y 属性保持不变。
通过 copy 函数,我们可以方便地创建对象的副本并修改其中的属性,而不需要手动重复构造对象的其他部分。这使得代码更加简洁和易于维护。
1.数据传输对象(DTO):当需要表示简单的数据结构或数据传输对象时,通常使用 case class 更合适。例如,表示一个寄存器的配置参数或一个数据包的头部信息等。
case class RegisterConfig(address: Int, width: Int)
2.模式匹配:如果需要在模式匹配中使用对象,并且希望能够方便地提取对象的属性值,推荐使用 case class。因为它们自动生成了 unapply 函数,可以轻松地进行模式匹配。
val config = RegisterConfig(0x1000, 32)
config match {
case RegisterConfig(addr, width) => println(s"Address: $addr, Width: $width")
case _ => println("Unknown configuration")
}
3.不可变性:如果需要创建不可变的数据结构,即对象创建后不可修改其属性值,建议使用 case class。因为 case class 的属性默认是不可变的。
1.需要可变性:如果需要创建可变的数据结构,即对象创建后可以修改其属性值,推荐使用 class。因为 class 的属性可以是可变的。
class Counter(var value: Int) {
def increment(): Unit = value += 1
}
2.更多的控制和灵活性:如果需要更多的控制和灵活性,例如自定义 equals 和 hashCode 函数、手动实现复制函数等,建议使用 class。因为在 class 中这些行为需要手动实现,可以更灵活地控制。
class Person(name: String, age: Int) {
// Custom equals method
override def equals(obj: Any): Boolean = obj match {
case other: Person => this.name == other.name && this.age == other.age
case _ => false
}
// Custom hashCode method
override def hashCode(): Int = {
val prime = 31
var result = 1
result = prime * result + name.hashCode
result = prime * result + age
result
}
// Manual copy method
def copy(name: String = this.name, age: Int = this.age): Person = new Person(name, age)
}
3.需要继承:如果需要创建可继承的类层次结构,即定义一个基类,并创建多个子类来扩展其功能,建议使用 class。因为 class 支持继承,可以更灵活地构建类的层次结构。
class Shape(var color: String) {
def draw(): Unit = println(s"Drawing a $color shape")
}
class Circle(color: String, var radius: Double) extends Shape(color) {
override def draw(): Unit = println(s"Drawing a $color circle with radius $radius")
}
val circle = new Circle("red", 5.0)
综上所述,根据具体的需求和场景选择合适的关键字是很重要的,这样可以使代码更加清晰、易于理解和维护。
1.不需要使用 new 关键字:在实例化 case class 时,不需要使用 new 关键字。可以直接使用类名和参数列表来创建对象。
case class Point(x: Int, y: Int)
val point = Point(3, 4) // 实例化一个 Point 对象,无需使用 new 关键字
2.参数列表可以省略括号:如果 case class 的构造函数没有参数,可以省略括号。
case class EmptyClass()
val empty = EmptyClass // 无需括号
3.自动生成的函数:case class 自动生成了一个伴生对象,并在其中定义了一个 apply及unapply函数,用于创建对象。这样使得可以直接使用类名和参数列表来创建对象,就像调用一个工厂函数一样。
1.需要使用 new 关键字:在实例化 class 时,需要使用 new 关键字。
class Point(var x: Int, var y: Int)
val point = new Point(3, 4) // 使用 new 关键字实例化 Point 对象
2.参数列表不能省略括号:无论构造函数是否有参数,都需要使用括号来表示构造对象时传递的参数。
class EmptyClass()
val empty = new EmptyClass() // 需要括号
3.没有自动生成的 apply 函数:class 没有自动生成的伴生对象和 apply 函数,因此无法像 case class 那样使用类名和参数列表来创建对象。
总的来说,case class 在实例化时更简洁和方便,而 class 则需要使用 new 关键字,并且不能像 case class 那样省略括号。
unapply 函数是 case class 自动生成的一个伴生对象函数,用于在模式匹配中提取对象的属性值。它的主要作用是将对象的属性值提取出来,并将其作为元组返回。
例如,对于一个简单的 case class 定义:
case class Person(name: String, age: Int)
Person 类的伴生对象会自动生成一个 unapply 函数,其定义类似于:
object Person {
def unapply(person: Person): Option[(String, Int)] =
Some(person.name, person.age)
}
在模式匹配中,可以使用 unapply 函数提取 Person 对象的属性值:
val person = Person("Alice", 30)
person match {
case Person(name, age) => println(s"Name: $name, Age: $age")
case _ => println("Unknown person")
}
在这个例子中,unapply 函数将 Person 对象的 name 和 age 属性值提取出来,并作为元组返回,然后在模式匹配中被成功地匹配。
与 unapply 函数不同,apply 函数是用于创建对象的,而不是用于模式匹配。case class 会自动生成一个伴生对象,并在其中定义了一个 apply 函数,用于创建对象。
val person = Person.apply("Alice", 30) // 使用 apply 方法创建 Person 对象
因此,在 case class 中,apply 函数是用于创建对象的,而 unapply 函数是用于模式匹配的。
文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文
文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作 导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释: cwy_init/init_123..._达梦数据库导入导出
文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js
文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6
文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输
文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...
文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure
文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割
文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答
文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。
文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入
文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf