嵌入式技术
用Verilog HDL描述寄存器
刚接触数字集成电路设计,特别是Verilog HDL语言的同学,往往不理解什么时候变量需要设置为wire型,什么时候需要设置成reg型。这主要的原因就是,在学习硬件描述语言之前,没有电路设计的概念。
一般来说reg类型变量,定义的是时序逻辑元件[1](Sequential Logical Device),在实际设计过程中主要涉及到的是寄存器、锁存器以及状态机[2])。
比如之前提到的带异步复位(低电平有效)的上升沿触发寄存器为例,其元件符号可以表现如下:
使用Verilog HDL描述时可以按照以下方式描述:
寄存器描述时,采用always块描述;
Always触发条件中,posedge CLK,表示CLK上升沿触发。如果是下降沿触发的寄存器,则需要换成negedge CLK;
Always触发条件中,negedge rstn,表示复位信号为低电平有效;如果是高电平有效,则需要修改成posedge rst;这里将rstn信号的命名换成了rst,这是因为行业内,在信号命名时,末尾如果是n,N,b,B[3]字母等,表示低电平有效,这是一种约定俗成的代码规范;
If(!rstn),与negedge rstn配合,表示rstn为低电平时有效(低电平时,电路复位或置位);如果希望该寄存器的rst在高电平时有效,则需要确保always触发条件中使用posedge rst,且这里改成if(rst);
Q<=1’b0;表示在复位信号有效时,输出端直接输出低电平,而无需其他信号有效,也就是异步复位的功能。如果是异步置位,则改为Q<=1’b1; 这里面1’b,表示寄存器位数为1位,后面数字是二进制数,如果多个寄存器同时设置,只需要修改位数和表示方法即可,如8’b0101_1010,8’h5a;
Q<=D;则表示,如果rstn/rst无效,且CLK上升沿触发时,将D端数据送给Q的输出,这样就是标准的D触发器,即寄存器行为了。
用Chisel描述寄存器
Chisel语言,与Verilog HDL这种传统的硬件描述语言相比,等级更高一些,无法灵活描述不同类型的寄存器类型。
以上变量申明分别描述了单位宽寄存器Q1以及多位宽寄存器Q2。其中Q1是一个在复位后为低电平的寄存器,Q2是一个8位的寄存器组,复位后Q2的输出是0x5a。
由于在Chisel语言中,不同类型是不能互相赋值的,建议对于控制信号尽量使用Bool类型,而数据信号尽量使用UInt类型。
与Verilog HDL不同,Chisel更接近于结构化描述,虽然申明了寄存器,且寄存器也有复位值,但实际上电路使用什么寄存器,需要依靠Chisel函数内部实现。
在Chisel2以及早期的Chisel3中,寄存器采用的是同步复位寄存器结构:
这种寄存器结构,没有使用异步复位,reset信号只有在时钟上升沿有效后,才会初始化寄存器的输出。这使得整个SoC系统需要在初始化上电时,消耗更多的时钟周期,确保系统完全进入稳定状态,才可以释放复位信号。增加了时钟系统以及复位系统的设计难度。
比如说最早使用Chisel开发的开源处理器内核,RocketChip,就是因为使用同步复位寄存器(实际情况更糟的是,内部还有大量寄存器连复位功能都没有使用),导致系统上电后,必须确保内核获得超过几百个时钟周期,才能释放复位信号,否则系统就会跑飞,甚至崩溃。
因此Chisel的开发团队也进行了优化,在新版本Chisel函数中使用了异步复位,当然因为当前我们团队还没有试用这个功能,因此不确定是否可以稳定工作。
结构化描述寄存器,有2大好处,一是描述方式简单高效,用一行代码就可以申明同时进行描述;二是,只要整个团队都是用同一个版本的Chisel开发环境进行描述,则都会使用同一种寄存器类型,这对于后续系统集成、验证、物理实现都会提供统一的输入接口,减少因为不同工程师,不同设计风格导致电路验证及实现的不可控隐患。
[1] 时序逻辑电路其任一时刻的输出不仅取决于该时刻的输入,而且还与过去各时刻的输入有关。常见的时序逻辑元件有触发器、锁存器、寄存器等。
[2] 状态机本身并不是时序元件,但考虑到由于状态机状态描述不完全时,会使用锁存器实现电路,因此在用Verilog HDL硬件描述时,需要申明为reg类型。
[3] 一般来说后缀可以用n,N,_n或_N,表示negative;b,B,_b或_B,表示Bar。都是反向的意思,比如说rstn,RSTN,reset_n,RESET_N,ceb,CEB,we_b,WE_B等;
全部0条评论
快来发表一下你的评论吧 !