当前位置:网站首页>吃透Chisel语言.03.写给Verilog转Chisel的开发者(没有Verilog基础也可以看看)
吃透Chisel语言.03.写给Verilog转Chisel的开发者(没有Verilog基础也可以看看)
2022-07-04 12:50:00 【github-3rr0r】
写给Verilog转Chisel的开发者——以组合逻辑电路为例,对比Chisel和Verilog的基本语法(没有Verilog基础也可以看看)
一个CPU或者其他数字芯片,本质上都是一个大型的数字逻辑电路,如果设计一个硬件描述语言来设计CPU,必然需要支持所有数字逻辑电路的基本组件,Verilog如此,Chisel当然也不例外。这一篇文章我们就以组合逻辑电路为例,对比Chisel和Verilog的基本语法。
那么我们先回顾一下组合逻辑电路有哪些基本单元。组合逻辑电路是一种没有状态的数字逻辑电路,其输出仅和输入有关,基本单元除了输入信号、输出信号以外,就是由逻辑门构成的门电路了。
单比特输入/输出和模块的编写
输入/输出信号通常为二进制值,一位的输入/输出信号可以为0或1,多位的输入/输出信号就可以组合成为多位二进制小信号,根据特定的编码/解码约定可以表示一个特定的二进制数。一位信号就可以称为一个bit,多位二进制信号称为bits。
具有单bit输入、且输入直接硬连线到输出的模块在Verilog中表述方法如下:
module module_sample {
input wire in_sample_bit, // 一位的线网型数据输入
output wire out_sample_bit // 一位的线网型数据输出
};
assign out_sample_bit = in_sample_bit;
endmodule
其中,module和endmodule之间的部分定义了一个模块,module_sample为模块名,input、output表示该信号是输入还是输出,wire指定了信号类型,这里可以省略,因为Verilog中默认信号为wire类型,in_sample_bit和out_sample_bit都是信号名。
而在Chisel中的表述稍有不同,直接看例子:
import chisel3._
class ModuleSample extends Module {
val io = IO(new Bundle {
val in_sample_bit = Input(Bool())
val out_sample_bit = Output(Bool())
})
io.out_sample_bit := io.in_sample_bit
}
解释如下:
- 首先我们需要导入
chisel3._包,使得Scala支持Chisel; - Chisel中实现的模块是个类,继承于Chisel的内置类
Module; - Chisel模块中的输入输出是
IO类的实例,实例化的变量是一个Bundle的实例,这个Bundle后面会说,现在可以简单理解为一个结构体; Bundle实例化的参数为两个信号,分别是Input类和Output类的实例,实例化的参数均为Bool(),表示他们是Bool类型的输入/输出信号;- 使用输入/输出信号时,信号均为
IO实例io的成员,用io.xxxx的方式引用,使用:=运算符表示硬连线;
我们可以通过getVerilogString函数输出上述模块对应的Verilog代码,完整代码如下:
import chisel3._
class ModuleSample extends Module {
val io = IO(new Bundle {
val in_sample_bit = Input(Bool())
val out_sample_bit = Output(Bool())
})
io.out_sample_bit := io.in_sample_bit
}
object MyModule extends App {
println(getVerilogString(new ModuleSample()))
}
输出如下:
module ModuleSample(
input clock,
input reset,
input io_in_sample_bit,
output io_out_sample_bit
);
assign io_out_sample_bit = io_in_sample_bit; // @[temp.scala 10:23]
endmodule
可以看到,除了省略的类型wire和额外生成但未使用的时钟、复位信号,其他与Verilog代码基本一致。
关于高阻态和不定态(可跳过)
这里需要注意的是,Verilog中有四种逻辑状态,分别为0、1、z和x,分别对应低电平、高电平、高阻态、不定态,而在Chisel中并没有高阻态和不定态。
但是这对于我们来说没有什么影响,因为在绝大多数芯片内部的设计中是用不到这两种逻辑状态的,设计中使用这种状态可能会带来危害,而且只有0、1的设计简化了模型,便于分析设计的行为。
当然,Chisel 3.1+版本的Experimental包中还定义了模拟类型Analog(等价于Verilog中的inout),也叫做黑盒类型(BlackBox Type),用于覆盖诸如高阻态和不定态这类特性。其中定义了模拟线网、三态线网、双向线网和电源线网(对“地线”或“电源线”建模)。虽然是实验性质的,但也表示Chisel未来可能会更好地支持这些特性,即使我们基本上用不上这些。
Analog是一种没有方向的类型,所以多个Analog类型可以通过attach操作符连接到一起。也可以用<>操作符连接Analog类型一次,但是多次就不行了。比如:
val a = IO(Analog(1.W))
val b = IO(Analog(1.W))
val c = IO(Analog(1.W))
// 可以这么连
attach(a, b)
attach(a, c)
// 或者这么连
a <> b
// 这样就不行,因为'a'连接了多次
a <> b
a <> c
多比特信号
我们可能在复位信号、使能信号中使用单比特信号,但是绝大多数时候我们要处理的信号或者说数据是多比特的。比如输入信号是两个有符号整数,输出是这两个整数的和,因此硬件设计语言必须支持多比特信号。
Verilog中使用数组来表示多比特的信号,比如下面的模块是实现了4位无符号数截断加法:
module module_sample {
input wire [3:0] in_a, // 4位的无符号数in_a
input wire [3:0] in_b, // 4位的无符号数in_b
output wire [3:0] out_c // in_a和in_b的和,溢出则截断取后四位
};
assign out_c = in_a + in_b;
endmodule
可以看到,我们用[3:0]指定了信号的宽度。如果未指定是否有符号,则wire类型的信号默认为无符号数,如果是有符号数,则需要使用signed关键字。下面是4位有符号数加法:
module module_sample {
input wire signed [3:0] in_a, // 4位的无符号数in_a
input wire signed [3:0] in_b, // 4位的无符号数in_b
output wire signed [3:0] out_c // in_a和in_b的和,溢出则截断取后四位
};
assign out_c = in_a + in_b;
endmodule
而Chisel中则分别用UInt和SInt类型表示无符号数和有符号数,使用方法和Bool类似,区别是需要指定信号宽度。例如,Chisel中4位有符号数加法模块的实现如下:
class ModuleSample extends Module {
val io = IO(new Bundle {
val in_a = Input(SInt(4.W))
val in_b = Input(SInt(4.W))
val out_c = Output(SInt(4.W))
})
io.out_c := io.in_a + io.in_b
}
其中,SInt()内的4.W定义了信号宽度,格式为宽度.W,如果不给定的话,就很可能无法进行宽度推理,运行时会抛出Uninferred width for target below. (Did you forget to assign to it?)错误。输出的Verilog代码如下:
module ModuleSample(
input clock,
input reset,
input [3:0] io_in_a,
input [3:0] io_in_b,
output [3:0] io_out_c
);
assign io_out_c = $signed(io_in_a) + $signed(io_in_b); // @[temp.scala 11:25]
endmodule
可以看到,虽然在定义输入输出未指定是否为有符号数,但是在运算过程中将两个4位输入都作为有符号操作数了,因此与前面Verilog定义的4位有符号数加法模块也是等价的。
UInt用法类似,在此不做赘述。
常量
顾名思义,常量就是值不能被改变的恒定值,下面分别说说整数常量在Verilog和Scala里的表示。
Verilog中整数的格式为+/-<位宽>'<进制><数字>,其中+/-为符号位,正数可以省略,<位宽>表示二进制宽度,默认32位,然后用单引号分开,后面跟着的<进制>有四种:
- 二进制:
b或B; - 十进制:
d或D或默认; - 十六进制:
h或H; - 八进制:
o或O;
例如,以下数字在Verilog中是等价的:
10
32'd10
32'b1010
32'ha
32'o12
而在Chisel中,常量或者说字面值通过将Scala整数或字符串传递给类型的构造器来得到,所以说Chisel中的常量和Scala中的常量应该区分开来,示例如下:
// 无符号整数
10.U // 从十进制Scala Int构造,10,二进制表示为4位
"ha".U // 从十六进制表示的Scala字符串构造,10,二进制表示为4位
"o12".U // 从八进制表示的Scala字符串构造,10,二进制表示为4位
"b1010".U // 从二进制表示的Scala字符串,还是10,二进制表示为4位
// 有符号整数
5.S // 从十进制Scala Int构造,有符号整数5,二进制表示为4位(0101)
-8.S // 从十进制Scala Int构造,有符号整数-8,二进制表示为4位(1111)
5.U // 从十进制Scala Int构造,无符号整数5,二进制表示为3位(101)
// 也可以指定常量的宽度
8.U(4.W) // 4位无符号整数,值为8
-152.S(32.W)// 32位有符号整数,值为-152
// 最后是Bool类型,即单比特常量
true.B // 从Scala布尔类型构造,值为1,一位
false.B // 从Scala布尔类型构造,值为0,一位
对于比较长的串,我们在Verilog和Chisel中都可以使用下划线来分隔,以增加代码可读性,不过Verilog是用在数字里面的:
32'h_dead_beef // 在Verilog中等价于32'hdeadbeef
而Chisel是用在字符串里面的:
"h_dead_beef".U // 在Chisel中等价于"hdeadbeef".U
默认来说,Chisel编译器会把每个常量定长度为能放下常量的最小尺寸,包括有符号类型的符号位,我们也可以通过.W来指定,上面已经演示过了。
下面解释一下类似.U和.W这类用法(这一段到下一小节之前都可以跳过):
.W前面是一个Scala整数,.W就是将这个整数转换为一个Chisel宽度类型的对象;.U前面是一个Scala对象,.U就是对这个Scala对象调用了UInt的构造器asUInt,比如:
"ha".asUInt(8.W) // 等价于"ha".U(8.W)
"o12".asUInt(6.W) // 等价于"o12".U(6.W)
"b1010".asUInt(12.W) // 等价于"b1010".U(12.W)
// 同理
5.asSInt(7.W) // 等价于5.S(7.W)
5.asUInt(8.W) // 等价于
我们也可以利用asUInt这种构造器进行强制类型转换,但是注意不能显式指定宽度:
val sint = 3.S(4.W) // 4-bit SInt
val uint = sint.asUInt // 将SInt转换为UInt
uint.asSInt // 将UInt转换为SInt
因为没有宽度参数,Chisel会按需自己扩展或截断,这种转换也能用在时钟类型Clock上,但是得小心一点,这里就不详细说了。
总结
这一篇文章对Verilog和Chisel的模块编写、基本数据类型和整数常量进行了简单的对比,主要是给你一个简单的印象。
如果你以前是写Verilog的,看完应该感觉到Chisel其实也差不多,包括暂时还没提到的时钟、寄存器等。
就算你之前对Verilog有没有了解,看到这里应该都有了一个初步的印象了,甚至再简单了解下基本语法就可以直接上手开写Chisel模块了。
下一篇文章开始,我们就正式一点点开始学习Chisel的语法和特性了,这套系列跟下来必须拿捏Chisel!
边栏推荐
- Flet tutorial 03 basic introduction to filledbutton (tutorial includes source code) (tutorial includes source code)
- Redis - how to install redis and configuration (how to quickly install redis on ubuntu18.04 and centos7.6 Linux systems)
- 担心“断气” 德国正修改《能源安全法》
- 30: Chapter 3: develop Passport Service: 13: develop [change / improve user information, interface]; (use * * * Bo class to accept parameters, and use parameter verification)
- 高质量软件架构的唯一核心指标
- Node の MongoDB 安装
- OpenHarmony应用开发之如何创建DAYU200预览器
- Secretary of Homeland Security of the United States: domestic violent extremism is one of the biggest terrorist threats facing the United States at present
- Gorm 读写分离(转)
- js中的变量提升和函数提升
猜你喜欢

动画与过渡效果

Go 语言入门很简单:Go 实现凯撒密码

Animation and transition effects

2022年起重机械指挥考试模拟100题模拟考试平台操作

1200. 最小绝对差

Redis - how to install redis and configuration (how to quickly install redis on ubuntu18.04 and centos7.6 Linux systems)

Getting started with the go language is simple: go implements the Caesar password

2022g3 boiler water treatment examination question simulation examination question bank and simulation examination

如何在 2022 年为 Web 应用程序选择技术堆栈

2022 Shandong Province safety officer C certificate examination question bank and online simulation examination
随机推荐
unity不识别rider的其中一种解决方法
C foundation in-depth study I
MongoDB常用28条查询语句(转)
JVM 内存布局详解,图文并茂,写得太好了!
近日小结(非技术文)
C language programming topic reference
.NET 使用 redis
Gorm 读写分离(转)
基于链表管理的单片机轮询程序框架
C语言集合运算
面试拆解:系统上线后Cpu使用率飙升如何排查?
C语言程序设计
2022kdd pre lecture | 11 first-class scholars take you to unlock excellent papers in advance
CommVault cooperates with Oracle to provide metallic data management as a service on Oracle cloud
2022 Shandong Province safety officer C certificate examination question bank and online simulation examination
易周金融 | Q1保险行业活跃人数8688.67万人 19家支付机构牌照被注销
C语言图书租赁管理系统
Flet教程之 03 FilledButton基础入门(教程含源码)(教程含源码)
C语言课程设计题
C array supplement