敏捷硬件开发语言Chisel与数字系统设计 课件 第4章 Chisel常用的硬件原语_第1页
敏捷硬件开发语言Chisel与数字系统设计 课件 第4章 Chisel常用的硬件原语_第2页
敏捷硬件开发语言Chisel与数字系统设计 课件 第4章 Chisel常用的硬件原语_第3页
敏捷硬件开发语言Chisel与数字系统设计 课件 第4章 Chisel常用的硬件原语_第4页
敏捷硬件开发语言Chisel与数字系统设计 课件 第4章 Chisel常用的硬件原语_第5页
已阅读5页,还剩66页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

4.Chisel常用的硬件原语一、多路选择器二、优先编码器三、仲裁器四、队列五、ROM六、RAM七、计数器八、线性反馈移位寄存器九、状态机2目

录一、多路选择器3多路选择器二输入多路选择器”Mux(sel,in1,in2)”sel是Bool类型;in1和in2的类型相同,都是Data的任意子类型。当sel为true.B时,返回in1,否则返回in2可内嵌Mux,构成n输入,例:

“Mux(c1,a,Mux(c2,b,Mux(...,default)))”1.1Mux4多路选择器1.2MuxCasen输入多路选择器”MuxCase(default,Array(c1->a,c2->b,...))”第一个参数是默认情况下返回的结果;第二个参数是一个数组,数组的元素是对偶“(成立条件,被选择的输入)”。5多路选择器MuxCase的变体,类似查找表“MuxLookup(idx,default,Array(0.U->a,1.U->b,...))”相当于把MuxCase的成立条件依次换成从0开始的索引值它的展开相当于:“MuxCase(default,Array((idx===0.U)->a,(idx===1.U)->b,...))”1.3MuxLookup6多路选择器1.4Mux1H独热码多路选择器如果零个或多个选择信号有效,则行为变为多个被选择数字之和Mux1H三种常用的定义形式(等价):valhotValue=Mux1H(io.selector,Seq(2.U,4.U,8.U,11.U))

valhotValue=Mux1H(Seq(io.selector(0),io.selector(1),

io.selector(2),io.selector(3)),Seq(2.U,4.U,8.U,11.U))

valhotValue=Mux1H(Seq(

io.selector(0)->2.U,

io.selector(1)->4.U,

io.selector(2)->8.U,

io.selector(3)->11.U

))io.selectorSeq(2.U,4.U,8.U,11.U)00012.U00104.U01008.U100011.U7多路选择器1.5PriorityMux当多个选择信号有效时,按照定义时的顺序,返回更靠前的被选数据。有以下三种定义形式(等价):valpriorityValue=PriorityMux(io.selector,Seq(2.U,4.U,8.U,11.U))

valpriorityValue=PriorityMux(Seq(io.selector(0),io.selector(1),

io.selector(2),io.selector(3)),

Seq(2.U,4.U,8.U,11.U))

valpriorityValue=PriorityMux(Seq(

io.selector(0)->2.U,

io.selector(1)->4.U,

io.selector(2)->8.U,

io.selector(3)->11.U,

))io.selectorSeq(2.U,4.U,8.U,11.U)xxx12.Uxx104.Ux1008.U100011.U8二、优先编码器9优先编码器2.1PriorityEncoderPriorityEncoder("b1010".U)

PriorityEncoder(Seq(false.B,true.B,false.B,true.B))作用是对多个输入信号中优先级最高的一个信号进行编码。有以下两种定义形式:以上两种形式是等价的,返回值类型都是UInt,值为1.U。入参:Bits返回值:UIntxxx10.Uxx101.Ux1002.U10003.U10优先编码器2.2PriorityEncoderOH有以下两种定义形式:PriorityEncoderOH("b1010".U)

PriorityEncoderOH(Seq(false.B,true.B,true.B,false.B))它和第一种编码器的区别在于该编码器会把编码结果转换成独热码。第一种形式返回一个UInt的数据2.U,第二种形式返回一个Seq:Seq(false.B,true.B,false.B,false.B)。入参:Bits返回值:Uintxxx10001xx100010x10001001000100011三、仲裁器12仲裁器3.1ready-valid接口Arbiter使用的是标准的ready-valid接口,该类型的端口在单一数据信号的基础上又添加了ready和valid信号以使用ready-valid握手协议。它包含3个信号:ready:高有效时表示数据接收者consumer已经准备好接收信号,由consumer驱动。valid:高有效时表示数据生产者producer已经准备好待发送的数据了,由producer驱动。bits:是要在producer与consumer之间传输的数据。13仲裁器使用单例对象Decoupled可创建ready-valid接口,有以下两种形式:

Decoupled(...):可以传入任意的数据类型,然后返回一个ready-valid接口,此时ready是input信号,valid和bits都是output信号。因此它是属于数据生产者producer的端口。

Flipped(Decoupled(...)):Flipped()会将ready-valid接口的信号方向进行取反,因此此时ready是output信号,valid和bits都是input信号。因此它是属于数据接收者consumer的端口。14仲裁器producerDecoupled(...)readyvalidbitsconsumerFlipped(Decoupled(...))readyvalidbits15仲裁器3.2优先仲裁器优先仲裁器的输入通道的优先级是固定的,每次都是选择多个有效通道中优先级最高的。创建Arbiter对象的方式为newArbiter(gen:T,n:Int)gen是传输的数据的类型,n是待仲裁对象的个数,也即数据发送者producer的个数。数据接收者consumer的个数为默认为1。16仲裁器Arbiter内部使用ArbiterIO定义端口,而ArbiterIO内部又使用Decoupled()创建最终所需的ready-valid接口,定义如下:classArbiterIO[T<:Data](privatevalgen:T,valn:Int)extendsBundle{

valin=Flipped(Vec(n,Decoupled(gen)))

valout=Decoupled(gen)

valchosen=Output(UInt(log2Ceil(n).W))

}”log2Ceil(n)”是实现的是以2为底的对数函数的计算,把结果向上取整返回17仲裁器producer0readyvalidbitsconsumerreadyvalidbitsArbiterin(0).validin(0).bitsin(0).readyin(1).validin(1).bitsin(1).readyout.bitsout.readyout.validchosenproducer1readyvalidbitschosen18仲裁器例:

classMyArbiterextendsModule{

valio=IO(newBundle{

valin=Flipped(Vec(2,Decoupled(UInt(8.W))))

valout=Decoupled(UInt(8.W))

valchosen=Output(UInt())

})

valarbiter=Module(newArbiter(UInt(8.W),2))

//2to1PriorityArbiter

arbiter.io.in<>io.in

io.out<>arbiter.io.out

io.chosen:=arbiter.io.chosen

}Verilog代码中生成了两个module,第一个moduleArbiter对应的是例化的优先仲裁器Arbiter,第二个moduleMyArbiter对应的是顶层模块MyArbiter。19仲裁器每次都从不同的起点开始仲裁,采用轮询方式查看各个通道是否有请求,优先选择先查到的有效通道。由于起点是依次变化的,所以每个通道总体来说具有相同的优先级。它的创建与调用方式和Arbiter是一样的,只是内部实现的仲裁逻辑不同。3.3循环仲裁器RRArbiter20四、队列21队列objectEnqIO{

defapply[T<:Data](gen:T):DecoupledIO[T]=Decoupled(gen)

}

objectDeqIO{

defapply[T<:Data](gen:T):DecoupledIO[T]=Flipped(Decoupled(gen))

}Chisel内建了队列Queue,它会创建一个使用ready-valid接口的FIFOQueue内部使用QueueIO定义端口,QueueIO最终仍然是使用Decoupled()创建所需的ready-valid接口,定义如下:4.1队列的接口22队列classQueueIO[T<:Data](privatevalgen:T,valentries:Int)extendsBundle

{

valenq=Flipped(EnqIO(gen))

valdeq=Flipped(DeqIO(gen))

valcount=Output(UInt(log2Ceil(entries+1).W))

}enq是用来写数据的端口,因此它和数据生产者producer连接;deq是用来读数据的端口,因此它和数据接收者consumer连接;count表示此时Queue中的数据个数23队列producerreadyvalidbitsconsumerreadyvalidbitsQueueenq.validenq.bitsenq.readyout.bitsout.readyout.validcountenqdeqcountcount24队列4.2使用队列Queue(enq:ReadyValidIO[T],entries:Int=2):返回的是DecoupledIO[T]类型的读数据端口,也即上述的deq,因此我们不能在代码中访问enq和count。第一种形式的使用案例:classMyQueueextendsModule{

valio=IO(newBundle{

valin=Flipped(Decoupled(UInt(8.W)))

valout=Decoupled(UInt(8.W))

})

valq=Queue(io.in,2)

io.out<>q

}25队列第二种形式的使用案例:classMyQueueextendsModule{

valio=IO(newBundle{

valin=Flipped(Decoupled(UInt(8.W)))

valout=Decoupled(UInt(8.W))

valcnt=Output(UInt(4.W))

})

valq=Module(newQueue(UInt(8.W),entries=16))

q.io.enq<>io.in

io.out<>q.io.deq

io.cnt:=q.io.count

}newQueue(gen:T,entries:Int):第一个参数是存储的数据的类型,第二个参数是存储的数据的深度。返回一个Queue对象,该对象包含QueueIO属性,因此我们可以在代码中访问QueueIO的enqdeqcount这三种端口信号。26队列各自生成的Verilog中,子模块Queue的端口定义分别如下:moduleQueue(

inputclock,

inputreset,

outputio_enq_ready,

inputio_enq_valid,

input[7:0]io_enq_bits,

inputio_deq_ready,

outputio_deq_valid,

output[7:0]io_deq_bits,

output[4:0]io_count

);moduleQueue(

inputclock,

inputreset,

outputio_enq_ready,

inputio_enq_valid,

input[7:0]io_enq_bits,

inputio_deq_ready,

outputio_deq_valid,

output[7:0]io_deq_bits

);都有所需的两对ready-valid握手信号,并且这两对信号方向相反在第种一形式中,是不会有io_count端口的,因为我们无法使用QueueIO中的count27队列io.deq.valid:=!empty

io.enq.ready:=!Full4.3empty和full属性由于在classQueue中有如下定义:我们就可以通过io.deq.valid和io.enq.ready间接地访问empty和full信号28五、ROM29ROMclassROMextendsModule{

valio=IO(newBundle{

valsel=Input(UInt(2.W))

valout=Output(UInt(8.W))

})

valrom=VecInit(1.U,2.U,3.U,4.U)

io.out:=rom(io.sel)

//Vec[T]类的apply方法可接收Int和UInt类型的索引值

}通过工厂方法“VecInit[T<:Data](elt0:T,elts:T*)”或“VecInit[T<:Data](elts:Seq[T])”来创建一个只读存储器参数就是ROM里的常量数值,对应的Verilog代码就是给读取ROM的线网或寄存器赋予常量值,例如:30六、RAM31RAM6.1异步读RAM第一种RAM是同步(时序)写,异步(组合逻辑)读的RAM,通过工厂方法“Mem[T<:Data](size:Int,t:T)”来构建。valasyncMem=Mem(16,UInt(32.W))由于现代的FPGA和ASIC技术大多不再支持异步读RAM,所以这种RAM会被综合成寄存器阵列。32RAM6.2同步读写RAM同步(时序)读、写,通过工厂方法“SyncReadMem[T<:Data](size:Int,t:T)”来构建,这种RAM会被综合成实际的SRAM。例:valsyncMem=SyncReadMem(16,UInt(32.W))

写RAM的语法是:valmem=SyncReadMem(16,UInt(32.W))

when(wr_en){

mem.write(address,dataIn)

out:=DontCare

}

//“DontCare”告诉Chisel的未连接线网检测机制,写入RAM时读端口

//的行为无需关心33RAM读RAM的语法是:out:=mem.read(address,rd_en)

读、写使能信号都可以省略,使用赋值语句,也可以实现读写:valmem=SyncReadMem(1024,UInt(width.W))

io.dataOut:=DontCare

when(io.enable){

valrdwrPort=mem(io.addr)

when(io.write){rdwrPort:=io.dataIn}

.otherwise{io.dataOut:=rdwrPort}

}34RAM当读写操作有效的条件互斥时,RAM会被推断为单端口RAM。如:valmem=SyncReadMem(1024,UInt(width.W))

io.dataOut:=DontCare

when(io.enable){

when(io.write){mem.write(io.addr,io.dataIn)}

.otherwise{io.dataOut:=mem.read(io.addr)}

}反之,如果读写操作有效的条件不互斥,那么会被推断为双口RAM。如:valmem=SyncReadMem(1024,UInt(width.W))

//Createonewriteportandonereadport

mem.write(io.addr,io.dataIn)

io.dataOut:=mem.read(io.addr2,io.enable)35RAM6.3带写掩码的RAMRAM通常都具备按字节写入的功能,当写掩码的比特位有效时,对应的字节才会写入。当构建RAM的数据类型为Vec[T]时,就会推断出该RAM具有写掩码。此时,需要定义一个Seq[Bool]类型的写掩码信号。构建一个带写掩码的单端口RAM,classMaskRAMextendsModule{

valio=IO(newBundle{

valaddr=Input(UInt(10.W))

valdataIn=Input(UInt(32.W))

valen=Input(Bool())

valwe=Input(UInt(1.W))

valmask=Input(Vec(4,Bool()))

valdataOut=Output(UInt(32.W))

})36RAM而write方法有一个重载版本,就是第三个参数是接收写掩码信号的。当下标为0的写掩码比特是true.B时,最低的那个字节会被写入,依次类推。syncRAM.write(io.addr,dataIn_temp,io.mask)valsyncRAM=SyncReadMem(1024,Vec(4,UInt(8.W)))编译器会把Vec[T]的元素逐个展开,而不是合并成压缩数组的形式。因此本代码对应的Verilog的RAM主体定义成4个reg,Vivado综合出来的电路是四小块BRAM,而不是一大块BRAM。37RAM6.4从文件读取数据到RAM单例对象loadMemoryFromFile的apply方法可以在Chisel层面上从txt文件读取数据到RAM里。第一个参数接收一个自定义的RAM对象。第二个参数是文件的名字及路径,用字符串表示。第三个参数表示读取的方式为十六进制或二进制。注意,没有十进制和八进制。defapply[T<:Data](

memory:MemBase[T],

fileName:String,

hexOrBinary:MemoryLoadFileType.FileType=MemoryLoadFileType.Hex

):Unit38RAM例://loadmem.scala

packagechapter04

importchisel3._

importchisel3.util.experimental.loadMemoryFromFile

classLoadMemextendsModule{

valio=IO(newBundle{

valaddress=Input(UInt(3.W))

valvalue=Output(UInt(8.W))

})

valmemory=Mem(8,UInt(8.W))

io.value:=memory.read(io.address)

loadMemoryFromFile(memory,"~/chisel-workspace/chisel-template/mem.txt")

}

39七、计数器40计数器第一个参数为true.B时计数器从0开始每个时钟上升沿加1自增,为false.B时则计数器保持不变;第二个参数n是一个Int类型的具体正数,当计数到n时归零。在Chisel.util包里定义了一个自增计数器原语Counter,其apply方法共有三个重载版本7.1apply方法一apply(cond:Bool,n:Int):(UInt,Bool)41计数器classMyCounterextendsModule{

valio=IO(newBundle{

valen=Input(Bool())

valout=Output(UInt(8.W))

valvalid=Output(Bool())

})

val(a,b)=Counter(io.en,233)

io.out:=a

io.valid:=b

}该方法返回一个二元组:第一个元素是计数器当前的计数值,类型为UInt。第二个元素是判断当前计数值是否等于n的结果,类型为Bool。apply(cond:Bool,n:Int):(UInt,Bool)42计数器defapply(r:Range,enable:Bool=true.B,reset:Bool=false.B):(UInt,Bool)7.2apply方法二r:是一个scala.collection.immutable.Range类型的参数,用来提供一个计数的范围。enable:有效时表示该clk使能计数器计数。reset:有效时表示该clk使计数器复位到初始值,这里的初始值是提供的计数范围的起始值。如果不提供,那么默认跟随隐式复位信号,如果提供,那么就会有多个复位信号。43计数器importchisel3._

importchisel3.util._

importscala.collection.immutable.Range

classMyCounterextendsModule{

valio=IO(newBundle{

valen=Input(Bool())

valout=Output(UInt(8.W))

valvalid=Output(Bool())

})

val(a,b)=Counter(Range(0,233),io.en)

io.out:=a

io.valid:=b

}defapply(r:Range,enable:Bool=true.B,reset:Bool=false.B):(UInt,Bool)44计数器apply(n:Int):Counter7.3apply方法三和上述两个方法不同,该方法返回的是一个Counter对象,有如下的属性和方法可以使用:definc():Bool随时钟使计数器+1,返回值表示计数器是否在下一个时钟周期结束defn:Int无参函数,返回计数器的计数最大值ndefrange:Range无参函数,返回计数器的计数范围,类型为Rangedefreset():Unit使计数器复位到初始值0valvalue:UInt表示计数器此时的计数值,因为是val类型,所以只能读取45计数器classMyCounterextendsModule{

valio=IO(newBundle{

valen=Input(Bool())

valout=Output(UInt(8.W))

valvalid=Output(Bool())

})

valcnt=Counter(233)

when(io.en){

cnt.inc()

}

vala=cnt.value

valb=cnt.value===cnt.n.U

io.out:=a

io.valid:=b

}apply(n:Int):Counter46八、线性反馈移位寄存器47线性反馈移位寄存器defapply(width:Int,increment:Bool=true.B,seed:Option[BigInt]=Some(1)):UInt要产生伪随机数,可使用线性反馈移位寄存器原语LFSR:第一个参数width是移位寄存器的位宽。第二个参数increment是一个Bool类型的使能信号,用于控制寄存器是否移位,缺省值为true.B。第三个参数seed是一个随机种子,是可选值类型。LFSR在chisel3.util.random包里。48线性反馈移位寄存器例如:importchisel3._

importchisel3.util.random.LFSR

classLFSR16extendsModule{

valio=IO(newBundle{

valen=Input(Bool())

valout=Output(UInt(16.W))

})

io.out:=LFSR(16,io.en,Some(1))

}49九、状态机50状态机defapply(n:Int):List[UInt]用Enum特质及其伴生对象。伴生对象里的apply方法定义如下:它会根据参数n返回对应元素数的List[UInt],每个元素都是不同的,所以可以作为枚举值来使用。最好把枚举状态的变量名也组成一个列表,然后用列表的模式匹配来进行赋值。检测持续时间超过两个时钟周期的高电平:classDetectTwoOnesextendsModule{

valio=IO(newBundle{

valin=Input(Bool())

valout=Output(Bool())

})

valsNone::sOne1::sTwo1s::Nil=Enum(3)

valstate=RegInit(sNone)

51状态机

io.out:=(state===sTwo1s)

switch(state){

is(sNone){

when(io.in){

state:=sOne1

}

}

is(sOne1){

when(io.in){

state:=sTwo1s

}.otherwise{

state:=sNone

}

}

is(sTwo1s){

when(!io.in){

state:=sNone

}

}

}

}有了枚举值后,可以通过“switch…is…is”语句来使用。其中,switch里是相应的状态寄存器,而每个is分支的后面则是枚举值及相应的定义。52十、常见电路的描述方式53常见电路的描述方式10.1如何定义向量54Vec[T]

T必须是Data的子类,而且每个元素的类型、位宽必须一样。

其伴生对象里的apply工厂方法,接收两个参数,第一个是Int类型,表示元素的个数,第二个是元素。VecInit[T]其接收一个Seq[T]作为参数来构造向量,常用于初始化寄存器组、ROM、RAM等,或者用来构造多个模块。valmyVec=Wire(Vec(3,UInt(32.W)))valVec1=VecInit(1.U,2.U,3.U,4.U)

valVec2=VecInit(Seq.fill(8)(0.U(8.W)))重复参数:序列:常见电路的描述方式10.2混合向量55混合向量MixedVec[T]:包含的元素可以不全都一样,比如位宽不一样。MixedVecInit[T]的单例对象,以重复参数或者序列作为参数来构造。对于可以传入序列的向量,可以通过Scala的函数来生成。valVec1=MixedVec(UInt(8.W),UInt(16.W),UInt(32.W))

valVec2=MixedVec(Array(UInt(8.W),UInt(16.W),UInt(32.W)))valVec1=MixedVecInit(1.U,2.U,3.U,4.U)

valVec2=MixedVecInit(Seq.fill(8)(0.U(8.W)))valmixVec=MixedVec((1to10)map{i=>UInt(i.W)})

valmixVecinit=MixedVecInit(Seq.fill(8)(0.U(8.W)))重复参数:序列:重复参数:序列:序列:序列:常见电路的描述方式10.3Vec和UInt的互相转换56使用asBools将UInt转换成Vec。使用asUInt将Vec转换成Uint。importchisel3._

classFooextendsRawModule{

valuint=0xc.U

valvec=VecInit(uint.asBools)

printf(p"$vec")//Vec(0,0,1,1)

//Test

assert(vec(0)===false.B)

assert(vec(1)===false.B)

assert(vec(2)===true.B)

assert(vec(3)===true.B)}importchisel3._

classFooextendsRawModule{

valvec=VecInit(true.B,false.B,true.B,true.B)

valuint=vec.asUInt

printf(p"$uint")//13

//Test

//(rememberleftmostBoolinVecisloworderbit)

assert(0xd.U===uint)}常见电路的描述方式10.4向量的维度与索引以及修改向量的某几位57赋值:索引:valmyVec=Wire(Vec(3,UInt(32.W)))

myVec(0)(5)

myVec(0)(3,0)valVec1=Wire(Vec(3,UInt(32.W)))

Vec1(0):=1.U(32.W)

Vec1(1):=1.U(32.W)

Vec1(2):=1.U(32.W)

valVec2=Wire(Vec(3,UInt(32.W)))

Vec2(0):=1.U(32.W)

Vec2(1):=1.U(32.W)

Vec2(2):=1.U(32.W)

Vec1(0):=Cat(Vec2(0)(31,4),1.U(4.W))valVec1=Wire(Vec(3,UInt(32.W)))

Vec1(0):=1.U(32.W)

Vec1(1):=1.U(32.W)

Vec1(2):=1.U(32.W)

valVec2=Wire(Vec(3,UInt(32.W)))

Vec2(0):=1.U(32.W)

Vec2(1):=1.U(32.W)

Vec2(2):=1.U(32.W)

valbools=VecInit(Vec1(0).asBools)

valseq=1.U(4.W).asBools

for(i<-0until4){

bools(i):=seq(i)}

Vec2(0):=bools.asUInt

Vec1(0):=Vec2(0)常见电路的描述方式10.5子字赋值58子字赋值:先调用Bits类型的asBools方法,再配合VecInit构成一个向量asBools方法:根据调用对象的0、1排列返回一个相应的Seq[Bool]对象。classTestModuleextendsModule{

valio=IO(newBundle{

valin=Input(UInt(10.W))

valbit=Input(Bool())

valout=Output(UInt(10.W))

})

valbools=VecInit(io.in.asBools)

bools(0):=io.bit

io.out:=bools.asUInt

}常见电路的描述方式10.6BitPat59在Chisel里有对应的BitPat类,可以指定无关位。在其伴生对象里,一个apply方法可以接收一个字符串来构造BitPat对象,字符串里用问号表示无关位。dontCare方法接收一个Int类型的参数,构造等值位宽的全部无关位。"b10101".U===BitPat("b101??")//等于true.B

"b10111".U===BitPat("b101??")//等于true.B

"b10001".U===BitPat("b101??")//等于false.BvalmyDontCare=BitPat.dontCare(4)//等于BitPat("b????")常见电路的描述方式10.7Lookup/ListLookup60Lookup:defapply[T<:Bits](addr:UInt,default:T,mapping:Seq[(BitPat,T)]):TListLookup:

它的apply方法与上面的类似,区别在于返回结果是一个T类型的列表。Lookup(2.U,//addressforcomparison

10.U,//default

Array(BitPat(2.U)->20.U,

BitPat(3.U)->30.U)

)//返回20.UListLookup(2.U,//addressforcomparison

List(10.U,11.U,12.U),//default

Array(BitPat(2.U)->List(20.U,21.U,22.U),

BitPat(3.U)->List(30.U,31.U,32.U))

)//返回List(20.U,21.U,22.U)常见电路的描述方式10.8PopCount61单例对象PopCount:两个apply方法,分别接收一个Bits类型的参数和Bool类型的序列计算参数里“1”或“true.B”的个数,返回对应的UInt值。PopCount(Seq(true.B,false.B,true.B,true.B))//等于3.U

PopCount(Seq(false.B,false.B,true.B,false.B))//等于1.U

PopCount("b1011".U)//等于3.U

PopCount("b0010".U)//等于1.U

PopCount(myUIntWire)//动态计数常见电路的描述方式10.9OHToUInt62OHToUInt的apply方法可以接收一个Bits类型或Bool序列类型的独热码参数,计算独热码里的“1”在第几位(从0开始),返回对应的UInt值。如果不是独热码,则行为不确定。单例对象UIntToOH,它的apply方法是根据输入的UInt类型参数,返回对应位置的独热码,独热码也是UInt类型。UIntToOH(3.U)//等于"b1000".U

UIntToOH(7.U)//等于"b1000_0000".UOHToUInt("b1000".U)//等于3.U

OHToUInt("b1000_0000".U)//等于7.U常见电路的描述方式10.10Scala高级语法应用63Tuple:包含不同类型的对象

以一个二元组为例,由于函数counter只有一个返回语句,但如果想返回多个表达式或对象,如cntReg和nextVal,就可以把它们包在一个元组里返回。classMemFifo[T<:Data](gen:T,depth:Int)extendsFifo(gen:T,depth:Int){

defcounter(depth:Int,incr:Bool):(UInt,UInt)={

valcntReg=RegInit(0.U(log2Ceil(depth).W))

valnextVal=Mux(cntReg===(depth-1).U,0.U,cntReg+1.U)

when(incr){

cntReg:=nextVal

}

(cntReg,nextVal)

}

}常见电路的描述方式10.10Scala高

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论