【SpinalHDL快速入门】3、Scala 快速入门

news2024/11/27 5:25:49

SpinalHDL本质上来讲是Scala语言的一个库,所以需要先学习Scala,才能在此基础上学习SpinalHDL。

文章目录

    • Scala 基础
      • Scala 数据类型(5种:Boolean、Int、Float、Double、String)
      • Scala Variables
      • Scala Functions
        • Return
        • Return类型推断
        • 花括号
        • 返回值为空的函数
        • 参数默认值
        • Apply 函数
      • Object(静态类)
      • 入口函数main
      • class
        • 继承(Inheritance):override
        • Case class
        • 模板 / 类型参数化(Templates / Type parameterization)
    • Scala 编码规范
      • 使用class还是case class
      • [case] class:所有类名应以大写字母开头
      • companion object(伴生对象):伴生对象应该以大写字母开头
      • function:函数名始终以小写字母开头
      • instances(实例):类的实例应该始终以小写字母开头
      • if / when
      • switch
      • Parameters
    • Scala 交互
      • 介绍
      • SpinalHDL在API背后的工作原理
      • 一切皆为引用
      • 硬件类型
        • RGB example
      • 生成的 RTL 中的信号名称
      • Scala 用于elaboration,SpinalHDL 用于硬件描述(如:可以使用Boolean,而无法使用Bool)
      • Scala elaboration能力(if,for,函数式编程)

变量和函数应该定义在object、class或function中,不能在 Scala 文件的根目录下定义。

Scala 基础

Scala 数据类型(5种:Boolean、Int、Float、Double、String)

在这里插入图片描述

Scala Variables

在Scala中,您可以使用var关键字定义变量:

var number : Int = 0
number = 6
number += 4
println(number) // 10

Scala 能够自动推断类型。如果变量在声明时被赋值,您无需指定其类型:

var number = 0 //The type of 'number' is inferred as an Int during compilation

然而,在Scala中使用var并不是很常见。相反,通常会使用由val定义的常量值

val two = 2
val three = 3
val six = two * three

Scala Functions

例如,如果您想定义一个函数,该函数返回true,如果其两个参数的和大于零,则可以按照以下方式进行:

def sumBiggerThanZero(a: Float, b: Float): Boolean = {
	return (a + b) > 0
}

然后,要调用这个函数,你可以写:

sumBiggerThanZero(2.3f, 5.4f)

你也可以通过名称指定参数,如果有很多参数的话这是非常有用的:

sumBiggerThanZero(
	a = 2.3f,
	b = 5.4f
)

Return

return关键字并不是必须的。如果没有它,Scala会将函数中最后一个语句作为返回值

def sumBiggerThanZero(a: Float, b: Float): Boolean = {
	(a + b) > 0
}

Return类型推断

Scala 能够自动推断返回类型,您不需要指定它:

def sumBiggerThanZero(a: Float, b: Float) = {
	(a + b) > 0
}

花括号

如果你的函数只包含一条语句,Scala 函数不需要花括号

def sumBiggerThanZero(a: Float, b: Float) = (a + b) > 0

返回值为空的函数

如果你想让一个函数返回空值,那么它的返回类型应该设置为 Unit。这相当于 C/C++ 中的 void 类型。

def printer(): Unit = {
	println("1234")
	println("5678")
}

参数默认值

您可以为函数的每个参数指定默认值:

def sumBiggerThanZero(a: Float, b: Float = 0.0f) = {
	(a + b) > 0
}

Apply 函数

名为apply的函数很特殊,因为你可以在不必输入它们的名称的情况下调用它们

class Array() {
	def apply(index: Int): Int = index + 3
}
val array = new Array()
val value = array(4) //array(4) 被解释为 array.apply(4),将返回 7。

Object(静态类)

在Scala中,没有静态关键字。取而代之的是object。在对象定义内部定义的所有内容都是静态的。

以下示例定义了一个名为pow2的静态函数,它以浮点值作为参数,并返回一个浮点值。

object MathUtils {
	def pow2(value: Float): Float = value * value
}

//可以使用如下方式调用它:
MathUtils.pow2(42.0f)

入口函数main

Scala程序的入口点(即主函数)应该定义在一个 object 内,作为名为main的函数

object MyTopLevelMain{
	def main(args: Array[String]) {
		println("Hello world")
	}
}

class

类语法与Java非常相似。假设您想定义一个颜色类,该类以三个浮点值(r、g、b)作为构造参数

class Color(r: Float, g: Float, b: Float) {
	def getGrayLevel(): Float = r * 0.3f + g * 0.4f + b * 0.4f
}

接下来,实例化前面示例中的类并使用它的getGrayLevel函数:

val blue = new Color(0, 0, 1)
val grayLevelOfBlue = blue.getGrayLevel()

注意,如果你想从外部访问类的构造参数,这个构造参数应该被定义为val:

class Color(val r: Float, val g: Float, val b: Float) { ... }
...
val blue = new Color(0, 0, 1)
val redLevelOfBlue = blue.r

继承(Inheritance):override

举个例子,假设你想定义两个类,矩形和正方形,它们都继承自Shape类:(注意使用其中的override

class Shape {
	def getArea(): Float
}
class Square(sideLength: Float) extends Shape {
	override def getArea() = sideLength * sideLength
}
class Rectangle(width: Float, height: Float) extends Shape {
	override def getArea() = width * height
}

Case class

case class是声明类的另一种方式。

case class Rectangle(width: Float, height: Float) extends Shape {
	override def getArea() = width * height
}

case class和普通calss之间有一些区别:

  • case class不需要使用 new 关键字进行实例化
  • 构造参数可以从外部访问;您无需将它们定义为 val

在 SpinalHDL 中,这解释了编码规范的原因:通常建议使用case class而不是普通class,以减少输入量并增加连贯性

模板 / 类型参数化(Templates / Type parameterization)

假设你想设计一个给定数据类型的队列类,那么你需要为该类提供一个类型参数:

class Queue[T](){
	def push(that: T) : Unit = ...
	def pop(): T = ...
}

如果您想将 T 类型限制为给定类型(例如 Shape)的子类,可以使用 <: Shape 语法:

class Shape() {
	def getArea(): Float
}

class Rectangle() extends Shape { ... }

class Queue[T <: Shape]() {
	def push(that: T): Unit = ...
	def pop(): T = ...
}

函数也可以这样实现:

def doSomething[T <: Shape](shape: T): Something = { shape.getArea() }

Scala 编码规范

使用class还是case class

当您定义Bundle或Component时,最好将其声明为case class。原因如下:

  • 避免了使用new关键字
  • case类提供了一个clone函数。这在SpinalHDL中非常有用,例如,在定义新的Reg或某种新的Stream时需要克隆Bundle。
  • 构造参数可以直接从外部看到

[case] class:所有类名应以大写字母开头

class Fifo extends Component {

}
class Counter extends Area {

}
case class Color extends Bundle {

}

companion object(伴生对象):伴生对象应该以大写字母开头

object Fifo {
	def apply(that: Stream[Bits]): Stream[Bits] = {...}
}
object MajorityVote {
	def apply(that: Bits): UInt = {...}
}

这个规则的一个例外是当伴生对象被用作函数(仅适用于内部),并且这些apply函数不会生成硬件时

object log2 {
	def apply(value: Int): Int = {...}
}

function:函数名始终以小写字母开头

def sinTable = (0 until sampleCount).map(sampleIndex => {
	val sinValue = Math.sin(2 * Math.PI * sampleIndex / sampleCount)
	S((sinValue * ((1 << resolutionWidth) / 2 - 1)).toInt, resolutionWidth bits)
})
val rom = Mem(SInt(resolutionWidth bits), initialContent = sinTable)

instances(实例):类的实例应该始终以小写字母开头

val fifo = new Fifo()
val buffer = Reg(Bits(8 bits))

if / when

Scala中的if和SpinalHDL中的when通常应该按照以下方式编写:

if(cond) {
	...
} else if(cond) {
	...
} else {
	...
}


when(cond) {
	...
}.elseWhen(cond) {
	...
}.otherwise {
	...
}

例外情况包括:

  • 在 otherwise 前省略点是可以的。
  • 如果将 if/when 语句压缩到一行上可以使代码更易读,则可以这样做。

switch

SpinalHDL switch通常应按以下方式编写:

switch(value) {
	is(key) {
	
	}
	is(key) {
	
	}
	default {
	
	}
}

如果将is/default语句压缩成一行可以使代码更易读,则可以这样做。

Parameters

将 Component/Bundle 中的分组参数放在一个case class中通常是受欢迎的,因为:

  • 更容易携带/操作(carry/manipulate)以配置设计
  • 更好的可维护性
case class RgbConfig(rWidth: Int, gWidth: Int, bWidth: Int) {
	def getWidth = rWidth + gWidth + bWidth
}
case class Rgb(c: RgbConfig) extends Bundle {
	val r = UInt(c.rWidth bits)
	val g = UInt(c.gWidth bits)
	val b = UInt(c.bWidth bits)
}

但这并不适用于所有情况。例如:在FIFO中,将dataType参数与fifo的depth参数分组是没有意义的,因为通常dataType是与设计相关的内容,而depth则是与设计配置相关的内容。

class Fifo[T <: Data](dataType: T, depth: Int) extends Component {

}

Scala 交互

介绍

SpinalHDL实际上不是一种语言:它是一个普通的Scala库。这乍一看可能很奇怪,但它是一个非常强大的组合。
您可以使用整个Scala世界来帮助您通过SpinalHDL库描述硬件,但为了做到这一点,重要的是要理解SpinalHDL如何与Scala交互。

SpinalHDL在API背后的工作原理

当您执行SpinalHDL硬件描述时,每次使用SpinalHDL functions、operators或classes时,它都会构建一个表示设计电路的内存图形(in-memory graph)。

然后,在**完成实例化顶层组件类的工作(即elaboration)**之后,SpinalHDL将对构建的图进行一些处理,并且如果一切正常,则将该图刷新到VHDL或Verilog文件中。

一切皆为引用

例如,如果您定义了一个以Bits类型参数为输入的Scala函数,在调用它时,该参数将作为引用传递。因此,如果在函数内部分配该参数,则对底层Bits对象产生的影响与在函数外部进行分配相同。

硬件类型

SpinalHDL中的硬件数据类型是两个部分的组合:

  • 给定Scala类型的实例
  • 该实例的配置

例如,Bits(8位)是Scala类型Bits和其8位配置(作为构造参数)的组合。

RGB example

让我们以 Rgb bundle 类为例:

case class Rgb(rWidth: Int, gWidth: Int, bWidth: Int) extends Bundle {
	val r = UInt(rWidth bits)
	val g = UInt(gWidth bits)
	val b = UInt(bWidth bits)
}

硬件数据类型在这里是Scala Rgb类及其rWidth、gWidth和bWidth参数化的组合。

这是一个用法示例:【cloneOf 有点意思!!!】

// 定义一个 RGB 信号
val myRgbSignal = Rgb(5, 6, 5)

// 定义与前面相同数据类型的另一个 RGB 信号
val myRgbCloned = cloneOf(myRgbSignal)

你还可以使用函数来定义各种类型工厂(typedef):

// 定义一个类型工厂函数
def myRgbTypeDef = Rgb(5, 6, 5)

// 使用该类型工厂创建一个 RGB 信号。
val myRgbFromTypeDef = myRgbTypeDef

生成的 RTL 中的信号名称

为了在生成的 RTL 中命名信号,SpinalHDL 使用 Java 反射来遍历整个组件层次结构,收集存储在class属性中的所有引用,并使用它们的属性名称进行命名。

这就是为什么在function内定义的每个信号的名称都会丢失:

def myFunction(arg: UInt) {
	val temp = arg + 1 // 在生成的 RTL 中,您将无法检索到“temp”信号
	return temp
}

val value = myFunction(U"000001") + 42

如果您想保留生成的 RTL 中内部变量的名称,一种解决方案是使用 Area

def myFunction(arg: UInt) new Area {
	val temp = arg + 1 // 您将无法在生成的 RTL 中检索到临时信号。
}

val myFunctionCall = myFunction(U"000001") // 将生成名为`myFunctionCall_temp`的`temp`
val value = myFunctionCall.temp + 42

Scala 用于elaboration,SpinalHDL 用于硬件描述(如:可以使用Boolean,而无法使用Bool)

例如,如果您编写一个Scala for循环来生成一些硬件,它将在VHDL / Verilog中生成展开的结果。

另外,如果您想要一个常量,您不应该使用SpinalHDL硬件字面值而是Scala的。例如:

// 这是错误的,因为您不能将硬件Bool用作构造参数(它会导致层次结构违规)。
class SubComponent(activeHigh: Bool) extends Component {
	// ...
}

// 没错,你可以使用Scala世界中的所有内容来参数化你的硬件。
class SubComponent(activeHigh: Boolean) extends Component {
	// ...
}

Scala elaboration能力(if,for,函数式编程)

Scala的所有语法都可以用于详细说明硬件设计,例如,Scala if语句可用于启用或禁用硬件生成:

val counter = Reg(UInt(8 bits))
counter := counter + 1
if(generateAClearWhenHit42) { // elaboration test,就像在VHDL中生成if语句一样
	when(counter === 42) { // Hardware test
		counter := 0
	}
}

Scala的for循环也是如此:

val value = Reg(Bits(8 bits))
when(something) {
	// 使用Scala for循环设置值的所有位(在硬件elaboration期间评估)
	for(idx <- 0 to 7) {
		value(idx) := True
	}
}

此外,函数式编程技术可以与许多SpinalHDL类型一起使用:

val values = Vec(Bits(8 bits), 4)
val valuesAre42 = values.map(_ === 42)
val valuesAreAll42 = valuesAre42.reduce(_ && _)
val valuesAreEqualToTheirIndex = values.zipWithIndex.map{ case (value, i) => value === i }

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

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

相关文章

Python自动化测试框架:unittest介绍

Unittest是Python中最常用的测试框架之一&#xff0c;它提供了丰富和强大的测试工具和方法&#xff0c;可以帮助开发者更好地保证代码质量和稳定性&#xff0c;本文就来介绍下Unittest单元测试框架。 1. 介绍 unittest是Python的单元测试框架&#xff0c;它提供了一套丰富的测…

2023软件测试卷出天际!!!性能测试为啥一枝独秀?

近十年是中国互联网发展最快的10年&#xff0c;互联网用户从4亿增长至10亿。面对用户量的暴增&#xff0c;用户体验就成为互联网产品最大的考验。而 影响用户体验的最重要因素就是性能。 流量为王的时代&#xff0c;性能测试是所有产品上线前必须通过的重要环节。 企业招聘性…

12米与30米TanDEM-X数字高程模型DEM数据的下载申请方法

本文介绍全球12米与30米高空间分辨率的数字高程模型&#xff08;DEM&#xff09;数据——TanDEM-X数据的下载申请方法。 Tandem-X卫星项目于2010年6月启动&#xff0c;并于2010年6月21日和2010年12月21日分别发射两颗卫星&#xff0c;即TerraSAR-X和TanDEM-X。Tandem-X卫星之间…

裸辞3个月,面试了25家公司,这难度真不一般····

上半年裁员&#xff0c;下半年裸辞&#xff0c;有不少人高呼裸辞后躺平真的好快乐&#xff01;但也有很多人&#xff0c;裸辞后的生活五味杂陈。 面试25次终于找到心仪工作 因为工作压力大、领导PUA等各种原因&#xff0c;今年2月下旬我从一家互联网小厂裸辞&#xff0c;没想到…

【Android】WMS(五)输入事件原理

输入事件原理 安卓输入事件整体流程 Android 系统是由事件驱动的&#xff0c;而 input 是最常见的事件之一&#xff0c;用户的点击、滑动、长按等操作&#xff0c;都属于 input 事件驱动&#xff0c;其中的核心就是 InputReader 和 InputDispatcher。 InputReader 和 InputD…

申请国家标准项目管理专业人员能力评级(CSPM)报名条件有哪些?

2021年10月&#xff0c;中共中央、国务院发布的《国家标准化发展纲要》明确提出构建多层次从业人员培养培训体系&#xff0c;开展专业人才培养培训和国家质量基础设施综合教育。建立健全人才的职业能力评价和激励机制。由中国标准化协会&#xff08;CAS&#xff09;组织开展的项…

3.JavaScript常用对象数组对象

3.1、数组对象 3.1.1、概述 目录 3.1、数组对象 3.1.1、概述 3.1.2、创建数组 3.1.2.1、使用对象创建 3.1.2.2、使用字面量创建 3.1.3、遍历数组 3.1.4、数组属性 3.1.5、数组方法 3.2、函数对象 3.2.1、call()和apply() 3.2.2、this指向 3.2.3、arguments参数 3…

JavaSE-06 [面向对象+封装]

JavaSE-06 [面向对象封装] 第一章 面向对象思想 1.1 面向过程和面向对象 面向过程&#xff1a; 面向过程就是分析出解决问题所需要的步骤&#xff0c;然后用函数把这些步骤一步一步实现&#xff0c;使用的时候一个一个依次调用就可以了面向对象&#xff1a; 面向对象是把构成…

PYTHON元素定位方式总结

一&#xff0c;常用的8种定位方式 id定位 driver.find_element_by_id("id 值")   driver.find_element(by "id", value "ID值" ) name定位 单个元素&#xff1a;     driver.find_element_by_name("name值")     drive…

总结:记一次docker调试镜像的问题

一、背景 同事让帮忙部署一个应用到QKE&#xff0c;给了我镜像地址与配置文件。 由于要将配置文件映射到容器中&#xff0c;我创建了configmap&#xff0c;然后应用中将configmap中key对应的内容映射到了容器中的配置文件中。 但是我遇到了一个问题&#xff1a; 容器频繁快…

100多的ipad触控笔好用吗?ipad可以用的手写笔推荐

随着IPAD的普及&#xff0c;一些学习党已经从传统的纸质教学走向了无纸化教学。所以&#xff0c;本来就是苹果品牌专利的电容笔&#xff0c;现在更是成为了炙手可热的产品&#xff0c;很多人都对这款售价近千元的电容笔充满了好奇。我认为&#xff0c;对于职业画师来说&#xf…

你“心累”吗?教你方法

解决“心累”的方法来了 前言一、“心累”的原因二、认识“心累”三、走出“心累”四、发现自己的“优势” 前言 不管是脑力劳动还是体力劳动&#xff0c;工作生活本身并没有多么累&#xff0c;但总感觉累。这就是我今天想说的话题&#xff1a;心累。 如果你也被这个状态折磨&a…

用好 mysql 分区表

为了保证mysql的性能&#xff0c;我们都建议mysql单表不要太大&#xff0c;也经常有人问我这样的问题&#xff0c;整体来说呢&#xff0c;建议是&#xff1a;单表小于2G&#xff0c;记录数小于1千万&#xff0c;十库百表。如果但行记录数非常小&#xff0c;那么记录数可以再偏大…

粮油企业MES系统源码 粮油质量管控防伪溯源系统

粮油企业MES系统源码 粮油企业质量管控防伪溯源系统源码 粮油企业ERP系统源码 农产品MES系统源码 农产品溯源系统源码 利用物联网、云计算 、区块链、人工智能、5G等先进技术&#xff0c;结合特有的码码关联和RSA加密验证技术&#xff0c;开发的一套粮油质量管控防伪溯源系统&…

C++ 设计模式 包装类型(Wrapper Type)的运用:运算符重载的包装类型策略

目录标题 1. 运算符重载与包装类型&#xff08;Wrapper Type&#xff09;1.1 运算符重载的基本概念1.2 包装类型的定义与应用1.3 运算符重载与包装类型的结合 2. 包装类型的设计与实现2.1 包装类型的基本设计2.2 运算符重载的实现2.3 包装类型与原始类型的转换 3. 包装类型的性…

基于jsp+mysql+Spring+mybatis+Springboot的SpringBoot停车场停车位管理系统

运行环境: 最好是java jdk 1.8&#xff0c;我在这个平台上运行的。其他版本理论上也可以。 IDE环境&#xff1a; Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以&#xff0c;如果编译器的版本太低&#xff0c;需要升级下编译器&#xff0c;不要弄太低的版本 tomcat服务器环…

阿里巴巴内部10w字Java面试小抄火了,完整版开放下载

Java 面试 “金九银十”这个字眼对于程序员应该是再熟悉不过的了&#xff0c;每年的金九银十都会有很多程序员找工作、跳槽等一系列的安排。说实话&#xff0c;面试中 7 分靠能力&#xff0c;3 分靠技能&#xff1b;在刚开始的时候介绍项目都是技能中的重中之重&#xff0c;它…

【3步教程】如何使用商城小程序源码打造自己的商城?

作为电商行业的领头人&#xff0c;在移动端上拥有一款独立小程序绝对是不能缺少的&#xff0c;而使用商城小程序源码打造自己的商城则是最佳的选择之一。本文将教您如何在3步之内&#xff0c;快速高效地使用商城小程序源码&#xff0c;打造属于自己的小程序商城。 步骤一&…

深度解析Java程序员从入行到被裁全过程

很多年以前&#xff0c;我拿着 2000 的月薪入职了一家电商创业公司&#xff0c;整个公司只有一个会画饼的老板和啥也不会的我。 一切都是从零开始。 入职第一天&#xff0c;老板说我们首先需要一个网页。 于是我现学现卖了 HTML、CSS、JavaScript&#xff0c;做出来的界面大…

从古至今数据安全的守护者:哈希算法和加密方法的数据安全进化之旅

1、哈希算法进化史 在当今的数字化世界中&#xff0c;数据的安全性和完整性是至关重要的。哈希算法作为一种核心的密码学工具&#xff0c;用于生成数据的唯一标识和验证数据的完整性。然而&#xff0c;随着技术的进步和安全威胁的不断演化&#xff0c;早期的哈希算法逐渐暴露出…