Verilog设计与验证 学习笔记一

Verilog设计与验证 学习笔记一

第四章 RTL概念与RTL建模 安排常用的设计实例,对RTL建模有感性的认识
第五章 重点讨论RTL级的编码风格和各种设计原则
第六章 讨论RTL级设计的FSM(有限状态机)的描述技巧

第四章 RTL概念与RTL建模

主要内容如下:
RTL与综合的概念
RTL级设计的基本要素和步骤
常用的RTL级建模
实例

RTL和综合的概念

1.HDL语言的层次:

系统级(system level)
功能模块级(function model level)
行为级(behavior level)
寄存器传输级(RTL,Register Transfer Level)
门级(Gate Level)

2.RTL级概念:

不关注寄存器和组合逻辑的细节,
通过描述寄存器到寄存器之间的逻辑功能描述电路的HDL层次。

3.RTL级综合:

将RTL级源代码翻译并优化为门级网表
在可编程器件(PLD,主要是指FPGA和CPLD)设计领域,最重要的设计层次就是RTL级

RTL级设计的基本要素和步骤

1.典型的RTL级设计包含三部分:

时钟域描述

时序逻辑描述(寄存器描述):

根据时钟沿的变换,描述寄存器之间的数据传输方式

组合逻辑描述:

描述电平敏感信号的逻辑组合方式和逻辑功能

2.全局时钟资源的特点是几乎没有Clock Skew(时钟偏斜),有一定的Clock Delay(时钟延时);
第二全局时钟资源的特点是具有娇羞奥的Clock Skew和Clock Delay,时钟驱动能力较强。

3.RTL级代码的设计顺序

  1. 功能定义与模块划分
  2. 定义所有模块的接口
  3. 设计时钟域
  4. 考虑设计的关键路径
  5. 顶层设计
  6. FSM设计
  7. 时序逻辑设计
  8. 组合逻辑设计

4.设置异步复位信号

设置异步复位信号“reset_”为低有效信号(下降沿开始复位):

reg [3:0] cnt_reg;

always @ (posedge clock or negedge reset_)

if ( !reset_ )
	cnt_reg<= 4'b0000;
else
	begin
		......
	end

5.设置同步复位信号

always的敏感列表中仅有时钟沿信号,仅当时钟沿猜到同步复位的有效电平时,才会在时钟沿到达时刻进行复位/置位的操作。

reg [3:0] cnt_reg;

always @ (posedge clock)

if(!reset_)
	cnt_reg <= 4'b0000;
else
	begin
	......
	end

tips:

在时钟的上升沿和下降沿到达时,对寄存器电路都进行相应的操作。这个双沿电路相当于使用了原时钟的倍频时钟的单沿操作电路。在PLD设计中,不推荐使用该方式。

5.常见的两种组合逻辑模

  1. always模块的敏感表为电平敏感信号的电路

     使用阻塞赋值“=”,因为最后实现的不是寄存器使用阻塞赋值“=”,因为最后实现的不是寄存器
    

2.assign等关键字描述的组合逻辑电路

	用于表示相对简单的电路,信号一般被定义为wire型
	除了直接赋值外,还可以使用? 语句

6.MUX建模

  1. 简单的Mux,采用assign和? 表达式

举例:利用?表达式描述一个2选1的Mux

	wire mux_out;
	assign mux_out=(en)?a:b;
  1. 复杂的Mux,需要使用always和if…else…,case等条件判断语句

举例:利用case描述一个4选1的Mux

	reg mux_out;
		always @(en or a or bor c or d)
			case(en)
				2'b00:	mux_out = a;
				2'b01:	mux_out = b;
				2'b10:	mux_out = c;
				2'b11:	mux_out = d;
			endcase

7.存储器建模

定义一个数据位宽为 8 bit,地址为 63 位的RAM8x64:

	reg	 [7:0] RAM8x64 [0:63]

使用存储单元时,不能直接引用存储器某地址的某比特位值
正确的操作为,现将存储单元赋值给某个寄存器,然后对寄存器的某位进行操作

举例:将 8 bit 位宽,64 位地址RAM的读写电路,在读的时候,先将RAM某地址的数据读到“mem_data”寄存器中,然后即可对寄存器的任意bit位进行相关操作:

	reg	[7:0]	RAM8x64	[0:63];
	reg	[7:0]	mem_data;
		always @(posedge clk)
			if(WR&&CS)	//	WRite
				RAM8x64[addr]	<=	data_in	[7:0];
			else if(~WR&&CS)	//	read
				mem_data	<=	RAM8x64	[addr];

TIPs

对PLD设计而言,大多数FPGA都有内嵌的RAM资源,所以不推荐使用Verilog直接建模RAM。
FPGA内嵌的RAM资源大致分为两类:

  1. 块RAM(Block RAM)资源
  2. 分布式RAM资源(Distributed RAM,是一种特殊的底层逻辑单元,通过查找表和触发器实现的RAM结构)

8.两种在PLD中使用存储结构的方式

  1. 通过开发平台中内嵌的IP生成器,在图形化界面直接选择存储器类型,配置相关参数,生产相应的IP,在用户逻辑中直接调用该IP;
  2. 建模存储器

9.一般的时钟分频电路的建模方法

没有内嵌PLL/DLL的时钟电路中,或内嵌的资源不能满足时钟关系的处理方式:(PLD的主要时钟处理为分频和移相)

偶数分频

例:对200kHZ的时钟做2分频,4分频,8分频,要求分频之后的时钟同相。

TIPs:

一般PLD内嵌的PLL的输入频率下限都在MHz级,因此无法使用PLL完成分频和相位调整要求。
对于低速时钟的分频,一般使用计数器。

reg	[2:0]	cnt;
always	@	(posedge clk_200K or negedge rst)
	if(!rst)
		cnt	<=	3'b000;
	else
		cnt	<=	cnt	+	1;
	assign	clk_100K	=	~cnt	[0];//直接偶数分频后的相位与原相位相反
	assign	clk_50K	=	~cnt	[1];
	assign	clk_25K	=	~cnt	[2];

奇数分频*

例:对源时钟做3分频,要求3分频时钟占空比为50%。

	reg	[1:0]	state;
	reg	clk1;
		always	@(posedge	clk	or	negedge	reset)
			if(!reset)
				state	<=	2'b00;
			else
				case(state)
					2'b00	:	state	<=	2'b01;
					2'b01	:	state	<=	2'b11;
					2'b11	:	state	<=	2'b00;
					default	:	state	<=	2'b00;
				endcase
			always	@(negedge	clk	or	negedge	restt)
				if(!reset)
					clk1	<=	1'b0;
				else
					clk1	<=	state[0];
				assign	clk_out	=	state[0]	&	clk1;

串并转换建模

  1. 对于数据量较小的设计,可利用移位寄存器完成串并转换
  2. 对于排列顺序有规律的串并转换,利用case语句实现
  3. 对于复杂的串并转换,利用状态机实现

设计实例: CPU读写PLD寄存器接口

信号说明:
  1. CS:片选信号,低电平有效,对PLD为输入信号
  2. OE:输出使能信号,低电平有效,用于指示数据总线上的数据是否有效,对PLD为输入信号
  3. WR:读,写指示信号,低电平为读,高电平为写数据;对PLD而言为输入信号
  4. Adress:地址总线,假设地址总线为8bit,对PLD为输入信号
  5. Data:数据总线,假设数据总线为8bit,对PLD为双向总线
CPU读写寄存器设计由4部分组成
  1. 地址译码电路:根据CPU的CS,OE,Address,WR等信号译码出对那个寄存器进行何种操作
  2. 读寄存器操作:读某个寄存器的值,并将读到的值送到CPU的数据总线上
  3. 写寄存器操作:根据信号将数据总线上的数据写入某个寄存器
  4. 顶层:连接上述各个模块,并实例化CPU的数据总线为双向总线
CPU读写寄存器的方式有3种:
  1. 使用CPU的读写时钟同步方式读写PLD寄存器
  2. 使用OE或WR的沿读写寄存器
  3. 使用OE或WR的电平异步方式读写寄存器

第一种方法:使用CPU的读写时钟同步方式读写寄存器

见4-21的syn_wr包:

  1. top.v

     module top (clk_cpu, rst, CS_, OE_, WR_, Addr, data_bus);
    
     input       clk_cpu, rst;
     input        CS_, OE_, WR_;
     input [7:0] Addr;
     inout [7:0] data_bus;
    
     wire [7:0] data_in;
     wire [7:0] data_out;
     wire       my_wr, my_rd;
     wire       CS_reg1, CS_reg2, CS_reg3; // the register selection
     wire [7:0] reg1, reg2, reg3;          // the register to be read and written
    
     assign data_in = data_bus;
    
     decode decode_u1  (.CS_(CS_), 
                .OE_(OE_), 
                .WR_(WR_), 
                .Addr(Addr), 
                .my_wr(my_wr), 
                .my_rd(my_rd), 
                .CS_reg1(CS_reg1), 
                .CS_reg2(CS_reg2), 
                .CS_reg3(CS_reg3)
               					 );
    
     write_reg write_reg_u1 ( .clk(clk_cpu), 
                      .rst(rst),
                      .data_in(data_in),  
                      .my_wr(my_wr), 
                      .CS_reg1(CS_reg1), 
                      .CS_reg2(CS_reg2), 
                      .CS_reg3(CS_reg3), 
                      .reg1(reg1), 
                      .reg2(reg2), 
                      .reg3(reg3)
                      );
    
     read_reg read_reg_u1  ( .clk(clk_cpu), 
                      .rst(rst),
                      .data_out(data_out),  
                      .my_rd(my_rd), 
                      .CS_reg1(CS_reg1), 
                      .CS_reg2(CS_reg2), 
                      .CS_reg3(CS_reg3), 
                      .reg1(reg1), 
                      .reg2(reg2), 
                      .reg3(reg3)
                      );
    
    
                
     assign data_bus = ((!CS_) && (!OE_))? data_out : 8'bZZZZZZZZ;                    
                 
                 
                 
     endmodule
    
  2. decode:译码电路

     module decode (CS_, OE_, WR_, Addr, my_wr, my_rd, CS_reg1, CS_reg2, CS_reg3);
    
     input        CS_, OE_, WR_;
     input  [7:0] Addr;
    
     output       my_wr, my_rd;
     output       CS_reg1, CS_reg2, CS_reg3;
    
     reg          CS_reg1, CS_reg2, CS_reg3;
    
     assign my_wr = (!WR_) && (!CS_) && (!OE_);
     assign my_rd = (WR_)  && (!CS_) && (!OE_);
    
     always @ (Addr or CS_)
      if (!CS_)
     	 begin
     	  case (Addr)
      		 8'b 11110000: CS_reg1 <= 1'b1;
    	     8'b 00001111: CS_reg2 <= 1'b1;
             8'b 10100010: CS_reg3 <= 1'b1;
      	  default:     begin     
                        CS_reg1 <= 1'b0;   
                        CS_reg2 <= 1'b0;  
                        CS_reg3 <= 1'b0;                   
                   	   end
    	   endcase
      end
    
            
     endmodule
    
  3. write_reg: 写寄存器

     module read_reg (clk, rst, data_out,  my_rd, CS_reg1, CS_reg2, CS_reg3, reg1, reg2, reg3);
    
     input        clk, rst, my_rd, CS_reg1, CS_reg2, CS_reg3;
     input  [7:0] reg1, reg2, reg3;
     output [7:0] data_out;
     reg    [7:0] data_out;
    
      always @ (posedge clk or negedge rst)
     	 if (!rst)
      		data_out <= 8'b0;
     	 else
     		 begin
        		 if (my_rd)
            		 begin
                		  if (CS_reg1)
                     		 data_out <= reg1;
                		  else if (CS_reg2)
                      		 data_out <= reg2;
                  	  else if (CS_reg3)
                      		 data_out <= reg3;
            		 end
         	else
            		 data_out <= 8'b0;              
      
     		  end
      
    
    
     endmodule
    
  4. write_reg: 写寄存器

     module write_reg (clk, rst, data_in,  my_wr, CS_reg1, CS_reg2, CS_reg3, reg1, reg2, reg3);
    
     input        clk, rst, my_wr, CS_reg1, CS_reg2, CS_reg3;
     input  [7:0] data_in;
     output [7:0] reg1, reg2, reg3;
     reg    [7:0] reg1, reg2, reg3;
    
     always @ (posedge clk or negedge rst)
     	if (!rst)
     		 begin
          		 reg1 <= 8'b0;
          		 reg2 <= 8'b0;
         	     reg3 <= 8'b0;         
      		 end
     	else
     		 begin
        			 if (my_wr)
             		begin
                  		if (CS_reg1)
                     		 reg1 <= data_in;
                  		else if (CS_reg2)
                      		 reg2 <= data_in;
                  		else if (CS_reg3)
                    		     reg3 <= data_in;
             	    end
         else
            	  begin
                  reg1 <= reg1;
                  reg2 <= reg2;
                  reg3 <= reg3;
               end                
      
     		 end
      
    
     endmodule
    

你可能感兴趣的:(verilog学习)