吃透Chisel语言.03.写给Verilog转Chisel的开发者(没有Verilog基础也可以看看)

本文对比了Chisel和Verilog在描述组合逻辑电路时的基本语法,介绍了Chisel如何通过Scala语法实现输入/输出、常量、多比特信号等,并通过实例展示了与Verilog的等价性。Chisel虽然语法略有不同,但依然支持数字逻辑电路的基本组件,适合无Verilog基础的学习者入门。
摘要由CSDN通过智能技术生成

写给Verilog转Chisel的开发者——以组合逻辑电路为例,对比Chisel和Verilog的基本语法(没有Verilog基础也可以看看)

一个CPU或者其他数字芯片,本质上都是一个大型的数字逻辑电路,如果设计一个硬件描述语言来设计CPU,必然需要支持所有数字逻辑电路的基本组件,Verilog如此,Chisel当然也不例外。这一篇文章我们就以组合逻辑电路为例,对比Chisel和Verilog的基本语法

那么我们先回顾一下组合逻辑电路有哪些基本单元。组合逻辑电路是一种没有状态的数字逻辑电路,其输出仅和输入有关,基本单元除了输入信号输出信号以外,就是由逻辑门构成的门电路了。

单比特输入/输出和模块的编写

输入/输出信号通常为二进制值,一位的输入/输出信号可以为01,多位的输入/输出信号就可以组合成为多位二进制小信号,根据特定的编码/解码约定可以表示一个特定的二进制数。一位信号就可以称为一个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

其中,moduleendmodule之间的部分定义了一个模块,module_sample为模块名,inputoutput表示该信号是输入还是输出,wire指定了信号类型,这里可以省略,因为Verilog中默认信号为wire类型,in_sample_bitout_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
}

解释如下:

  1. 首先我们需要导入chisel3._包,使得Scala支持Chisel;
  2. Chisel中实现的模块是个类,继承于Chisel的内置类Module
  3. Chisel模块中的输入输出是IO类的实例,实例化的变量是一个Bundle的实例,这个Bundle后面会说,现在可以简单理解为一个结构体;
  4. Bundle实例化的参数为两个信号,分别是Input类和Output类的实例,实例化的参数均为Bool(),表示他们是Bool类型的输入/输出信号;
  5. 使用输入/输出信号时,信号均为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中有四种逻辑状态,分别为01zx,分别对应低电平、高电平、高阻态、不定态,而在Chisel中并没有高阻态和不定态。

但是这对于我们来说没有什么影响,因为在绝大多数芯片内部的设计中是用不到这两种逻辑状态的,设计中使用这种状态可能会带来危害,而且只有01的设计简化了模型,便于分析设计的行为。

当然,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中则分别用UIntSInt类型表示无符号数和有符号数,使用方法和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位,然后用单引号分开,后面跟着的<进制>有四种:

  1. 二进制:bB
  2. 十进制:dD或默认;
  3. 十六进制:hH
  4. 八进制:oO

例如,以下数字在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!

  • 18
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值