chisel快速入门(三)

前一篇见此:

chisel快速入门(二)_沧海一升的博客-CSDN博客简单介绍了chisel,使硬件开发者能快速上手chisel。https://blog.csdn.net/qq_21842097/article/details/121418806

十四、模块的功能创建

        制造用于模块构造的功能接口也是有用的。例如,我们可以构建一个构造函数,它将多路复用器输入作为参数,并返回多路复用器输出:

object Mux2 {
	def apply (sel: UInt, in0: UInt, in1: UInt) = {
		val m = new Mux2() 
		m.io.in0 := in0 
		m.io.in1 := in1 
		m.io.sel := sel 
		m.io.out
	}
}

        其中对象Mux2在Mux2模块类中创建一个Scala单例对象,并且apply定义了创建Mux2实例的方法。有了这个Mux2创建功能,Mux4的规格现在明显更简单。

class Mux4 extends Module { 
	val io = new Bundle {
		val in0 = UInt(INPUT, 1)
		val in1 = UInt(INPUT, 1) 
		val in2 = UInt(INPUT, 1) 
		val in3 = UInt(INPUT, 1) 
		val sel = UInt(INPUT, 2) 
		val out = UInt(OUTPUT, 1)
	}
	io.out := Mux2(io.sel(1), Mux2(io.sel(0), io.in0, io.in1), Mux2(io.sel(0), io.in2, io.in3))
}

        选择输入非常有用,以至于 Chisel 将其内置并称之为 Mux。 然而,与上面定义的 Mux2 不同,内置版本允许 in0 和 in1 上的任何数据类型,只要它们有一个共同的超类。

        Chisel提供MuxCase,其本质上是一个n-way Mux。

MuxCase(default, Array(c1 -> a, c2 -> b, ...))

十五、多态性和参数化

        Scala是一种强类型语言,使用参数化类型来指定通用函数和类。 在本节中,我们展示了Chisel用户如何使用参数化类来定义自己的可重用函数和类。

1、参数化函数

        前面我们在Bool上定义了Mux2,但现在我们展示如何定义一个通用的多路复用器功能。我们使用一个布尔条件和con和alt参数(对应于then和else表达式)来定义一个T类型的函数:

def Mux[T <: Bits](c: Bool, con: T, alt: T): T { ... }

        其中T需要是Bits的子类。Scala确保在Mux的每个使用中,它可以找到实际的con和alt参数类型的公共超类,否则会导致Scala编译类型错误。

2、参数化类

        与参数化函数一样,我们也可以参数化类,使它们可重用程度更高。例如,我们可以将Filter类概括为可以使用任何类型的链接。

        我们可以通过参数化FilterIO类和定义构造函数采取零参数类型构造函数来做到这点,如下所示:

class FilterIO[T <: Data](type: T) extends Bundle { 
	val x = type.asInput.flip
	val y = type.asOutput
}

        我们现在可以通过定义一个模块类来定义Filter,该模块类也接收一个链接类型构造函数参数,并将其传递给FilterIO接口构造器:

class Filter[T <: Data](type: T) extends Module { 
	val io = new FilterIO(type)
	...
}

        另一个例子,通用FIFO可以这样定义,并使用如下:

class DataBundle extends Bundle { 
	val A = UInt(width = 32)
	val B = UInt(width = 32)
}
object FifoDemo {
	def apply () = new Fifo(new DataBundle, 32)
}

class Fifo[T <: Data] (type: T, n: Int) extends Module {
	val io = new Bundle {
		val enq_val = Bool(INPUT) 
		val enq_rdy = Bool(OUTPUT) 
		val deq_val = Bool(OUTPUT) 
		val deq_rdy = Bool(INPUT) 
		val enq_dat = type.asInput 
		val deq_dat = type.asOutput
	}
	val enq_ptr = Reg(init = UInt(0, sizeof(n)))
	val deq_ptr = Reg(init = UInt(0, sizeof(n)))
	val is_full = Reg(init = Bool(false))
	val do_enq  = io.enq_rdy && io.enq_val
	val do_deq  = io.enq_rdy && io.deq_val 
	val is_empty = !is_full && (enq_ptr === deq_ptr)
	val deq_ptr_inc = deq_ptr + UInt(1)
	val enq_ptr_inc = enq_ptr + UInt(1)
	val is_full_next = Mux(do_enq && ~do_deq && (enq_ptr_inc === deq_ptr), Bool(true), Mux(do_deq && is_full, Bool(false), is_full)) 
	enq_ptr := Mux(do_enq, enq_ptr_inc, enq_ptr) 
	deq_ptr := Mux(do_deq, deq_ptr_inc, deq_ptr) 
	is_full := is_full_next
	val ram = Mem(n) 
	when (do_enq) {
		ram(enq_ptr) := io.enq_dat 
	}
	io.enq_rdy := !is_full 
	io.deq_val := !is_empty 
	ram(deq_ptr) <> io.deq_dat
}

        对FIFO定义通用解耦接口,可以简化IO:

class DecoupledIO[T <: Data](data: T) extends Bundle {
	val ready = Bool(INPUT)
	val valid = Bool(OUTPUT)
	val bits = data.clone.asOutput 
}

class DecoupledDemo
extends DecoupledIO()( new DataBundle )

class Fifo[T <: Data] (data: T, n: Int) extends Module {
	val io = new Bundle {
		val enq = new DecoupledIO( data ).flip() 
		val deq = new DecoupledIO( data )
	}
	... 
}

十六、多时钟域

1、创建时钟域

        为了使用多个时钟域,用户必须创建多个时钟。 在Chisel中,时钟是使用复位信号参数创建的第一级节点,定义如下:

class Clock (reset: Bool) extends Node { 
	def reset: Bool // returns reset pin
}

        在Chisel中有一个内置的隐式时钟,状态元素默认使用:

var implicitClock = new Clock( implicitReset )

        状态元素和模块的时钟可以使用名为clock的附加命名参数来定义:

Reg(... clock: Clock = implicitClock) 
Mem(... clock: Clock = implicitClock) 
Module(... clock: Clock = implicitClock)

2、跨时钟域

        有两种方式可以定义电路在时钟域之间发送数据。第一种也是最原始的方式就是使用由两个寄存器组成的同步器电路,如下所示: 

// signalA is in clock domain clockA,
// want a version in clockB as signalB
val s1 = Reg(init = UInt(0), clock = clockB) 
val s2 = Reg(init = UInt(0), clock = clockB)
s1 := signalA
s2 := s1;
signalB := s2

        由于亚稳性问题,该技术只限于在域之间传递一位数据。

        在域之间发送数据的第二种和更一般的方式是通过使用异步fifo:

class AsyncFifo[T<:Data](gen: T, entries: Int, enq_clk: Clock, deq_clock:Clock) extends Module

        当通过指定标准fifo参数和两个时钟,然后使用标准解耦ready/valid信号从时钟域clockA到clockB获取一个版本的signalA时:

val fifo =
new AsyncFifo(Uint(width = 32), 2, clockA, clockB)
fifo.io.enq.bits := signalA
signalB := fifo.io.deq.bits
fifo.io.enq.valid := condA
fifo.io.deq.ready := condB

3、后端多时钟处理

        每个 Chisel 后端都需要用户以后端特定的方式设置和控制多个时钟。 为了展示如何驱动多时钟设计,我们以具有两个模块的硬件为例进行说明,该例子使用 AsyncFifo 与不同时钟的每个模块进行通信:fastClock 和 slowClock。

        在Verilog中:

  • Chisel为每个时钟/复位创建一个新端口,
  • Chisel将所有的时钟连到顶层模块
  • 用户必须要为每个时钟i创建一个always块时钟驱动
module emulator;
	reg fastClock = 0, slowClock = 0, resetFast = 1, resetSlow = 1; 
	wire [31:0] add, mul, test;
	always #2 fastClock = ~fastClock;
	always #4 slowClock = ~slowClock; 
	initial begin
		# 8
		resetFast = 0; 
		resetSlow = 0; 
		#400
	$finish;
end
ClkDomainTest dut (
	.fastClock(fastClock), 
	.slowClock(slowClock), 
	.io_resetFast(resetFast),
	.io_resetSlow(resetSlow),
	.io_add(add), 
	.io_mul(mul), 
	.io_test(test));
endmodule
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沧海一升

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值