RTL Coding Tech

本文是RTL算法设计流程中常用技巧总结


1. 前端设计时是否要考虑功耗? 需要

扪心自问,作为IC前端设计人员,有没有必要在设计RTL时考虑资源

因为综合过程有专门的后端人员去实现。

例如严格按照下面的代码去综合的话,应该是需要一个MUX和一个触发器,即2与1或1非1触发器

always@(posedge clk) begin
	if(cond1) begin
		if(cond2)
			a <= 1'b1;
	end
end

在这里插入图片描述

但优化代码之后,只需1与1或1触发器就OK了

always@(posedge clk) begin
	a <= (cond1 && cond2) || a;
end

在这里插入图片描述

优化代码可以降低直接综合的资源消耗,但是会降低代码可读性。

例如状态机设计出的RTL经常会出现多个if嵌套,是否需要优化?
应该是有必要的,可以直接用Vivado -> Flow Navigator -> RTL ANALYSIS -> Schemetic看到真正综合出来的电路。上述两个相同逻辑的代码综合的电路就不相同

2. 常用逻辑资源消耗

RTL代码中常见的逻辑需要消耗什么基本资源呢?基本资源,即与或非门和触发器

注意此处的门电路个数是指最少的门电路个数,例如 Y = A ⊕ B ⊕ C = A ′ B C ′ + A ′ B ′ C + A B ′ C ′ + A B C Y=A⊕B⊕C=A'BC'+A'B'C+AB'C'+ABC Y=ABC=ABC+ABC+ABC+ABC,前者需要4与2或4非、后者需要8与3或6非,选择前者。

下表中a[m-1:0]和b[m-1:0]为m位寄存器变量,m’dx为m位常量,zero(m’dx)为常量m’dx中1’d0的个数。

含义 逻辑 与门 或门 非门 触发器 Description
单个MUX if(...) or else if(...)2110
m位无符号数加法器(m>=2) a[m-1:0] + b[m-1:0]8m-54m-36m-40注意最终的结果一个(m+1)位数据s[m:0]。根据真值表写逻辑表达式,若和为s、进位为c。当i=0时,s[0] = a[0]⊕b[0];当(m-1)≤i≤1时,s[i]=a[i]⊕b[i]⊕c[i];当i=m时,s[m]=c[m]。则s需要(4m-2)个与门、(2m-1)个或门、(4m-2)个非门。当i=0时,c[0] = 0;当i=1时,c[1] = a[0]b[0];当m≤i≤2时,c[i]=a[i-1]b[i-1]+c[i-1](a[i-1]⊕b[i-1]),则c需要(4m-3)个与门、(2m-2)个或门、(2m-2)个非门。因此一共有(8m-5)个与门、(4m-3)个或门、(6m-4)个非门
a[m-1:0] +m'dx4m-42m-zero(m'dx)-14m-(2zero(m'dx)+2)0注意最终的结果一个(m+1)位数据s[m:0]。参考变量相加,若和为s、进位为c。当i=0时,s[0] = (b[0])?a'[0]:a[0];当(m-1)≤i≤1时,s[i]=(b[0])?(a[i]⊕c[i])'b[i]:(a[i]⊕c[i])b[i];当i=m时,s[m]=c[m]。则s需要(3m-3)个与门、(m-1)个或门、(3m-(zero(m'dx)+2))个非门。当i=0时,c[0] = 0;当i=1时,c[1] = (b[0])?a[0]:0;当m≤i≤2时,c[i]=(b[i-1])?(a[i-1]+c[i-1]a'[i-1]):(c[i-1]a[i-1]),则c需要(m-1)个与门、(m-zero(m'dx))个或门、(m-zero(m'dx))个非门。因此一共有(4m-4)个与门、(2m-zero(m'dx)-1)个或门、(4m-(2zero(m'dx)+2))个非门
m位信号按位与或非
&a[m-1:0]m-1000等价m个叶子节点且度1为0的二叉树,节点总数为2m-1,再去掉叶子节点就为m-1
|a[m-1:0]0m-100等价m个叶子节点且度1为0的二叉树,节点总数为2m-1,再去掉叶子节点就为m-1
~a[m-1:0]00m0注意最终的结果还是一个m位数据,每一位取反
a[m-1:0] & m'dx 0000注意最终的结果还是一个m位数据res[m-1:0]。任取(m-1)≤i≤0,若m'dx在位i为1,则res[i] = a[i];若m'dx在位i为0,则res[i] = 0。即无需任何门电路
a[m-1:0] | m'dx 0000注意最终的结果还是一个m位数据res[m-1:0]。任取(m-1)≤i≤0,若m'dx在位i为1,则res[i] = 1;若m'dx在位i为0,则res[i] = a[i]。即无需任何门电路
m位无符号数比较器
a[m-1:0] == b[m-1:0]3m-1m2m0先是a和b每一位相同或,需要2m个与门、m个或门、2m个非门。之后的与门判等结构等价m个叶子节点且度1为0的二叉树,n2节点总数为m-1。因此一共有与门3m-1个与门、m个或门、2m个非门
a[m-1:0] == m'dxm-10zero(m'dx)0对于所有满足(m-1)≤i≤0且m'dx在i位为0的i,a[i]先经过一个非门。之后等价m个叶子节点且度1为0的二叉树,节点总数为2m-1,再去掉叶子节点就为m-1
a[m-1:0] > b[m-1:0]1.5[(m^2)-m]+10.5[(m^2)-m](m^2)0思路是真值表:a[m-1]为1而b[m-1]为0,结果为1;a[m-1]和b[m-1]相等,且a[m-1]为1而b[m-1]为0,结果为1;a[m-1]和b[m-1]相等,且a[m-2]和b[m-2]相等,且a[m-3]为1而b[m-3]为0,结果为1,以此类推......逻辑表达式为Y=(a[m-1]· ~b[m-1])+(a[m-1]⊙b[m-1])(a[m-2]· ~b[m-2])+(a[m-1]⊙b[m-1])(a[m-2]⊙b[m-2])(a[m-3]· ~b[m-3])+...+(a[m-1]⊙b[m-1])(a[m-2]⊙b[m-2])...(a[1]⊙b[1])(a[0]· ~b[0])
a[m-1:0] > {x{1'd0},y{1'd1},(m-x-y){1'd0}}x+ym-yx0此处只讨论m'dx中所有的1'd1之间不含1'd0的情况,其他情况较复杂。逻辑表达式为Y=a[m-1]+a[m-2]+...+a[m-x+1]+(a'[m-1]a'[m-2]...a'[m-x+1]a[m-x+1]...a[m-(x+y)+1])(a[m-(x+y)]+a[m-(x+y)-1]+...+a[0])。
m位信号左移寄存器a[m-1:0] << 1000m

加法器概要
数电之比较器
数字电路基础(四) 数据分配器、数据选择器和数值比较器

2.1. 含有m个叶子节点且不含度为1节点 的二叉树节点数目为2m-1

对于多bit信号相乘或者作比较时,需要1个bit1个bit地比较,最终得到一个结果,需要计算这种电路耗费多少资源。

在这里插入图片描述

涉及到的结构本质就是二叉树

二叉树 - 百度百科

先明确几个概念。

● 叶子节点: 二叉树中每个圆圈代表1个节点,节点之间用连接。图中绿色的节点没有孩子,称之为叶子节点

● 度: 表示某个节点的孩子个数。图中绿色节点的度为0

那么耗费的门电路资源如何计算呢?将叶子节点看作是输入,其余的每个节点就可看作门电路资源,并且此处考虑门电路资源都是两个输入的,因此二叉树中不存在度1的节点

总而言之, 我们要解决的问题是:已知二叉树含有m个叶子节点,且不存在度1的节点,问度2的节点数目是多少?

用到一个性质:

● 性质:对任何一棵二叉树, 如果其叶结点数为n0,度为1的结点数为 n1,度为2的结点数为 n2,则n0=n2+1

所以若叶子节点有m个,即n0=m,而n1=0,整个二叉树含有的节点数目为n0+n1+n2=m+0+m-1=2m-1

证明:任意一颗二叉树的节点总数为(n0+n1+n2),由于除了根外其余节点均连有一个上边,该二叉树的边总数就为(n0+n1+n2-1)。又因为度0节点无孩子没有下边,度1节点有1个孩子有1个下边,度2节点有2个孩子有2个下边,因此二叉树的边总数又为(n1+2n2)。因此有(n0+n1+n2-1)=(n1+2n2)推得(n0=n2+1)。证闭
二叉树的五大性质及证明

3. 使用Grey码而非独热码

两个状态机,看看综合出来是个啥电路

always@(*) begin
	case(cur_state)
		2'b00:	nxt_state = 2'b01;
		2'b01:	nxt_state = 2'b11;
		2'b11:	nxt_state = 2'b10;
		2'b10:	nxt_state = 2'b00;
		default:	nxt_state = 2'b00;
	endcase
end

//综合:nxt_state[0] = !cur_state[1]
//综合:nxt_state[1] = cur_state[0]

//---------------------------------------------------------
always@(*) begin
	case(cur_state)
		4'b0001:	nxt_state = 4'b0010;
		4'b0010:	nxt_state = 4'b0100;
		4'b0100:	nxt_state = 4'b1000;
		4'b1000:	nxt_state = 4'b0001;
		default:	nxt_state = 4'b0001;
	endcase
end

//综合:nxt_state[0] = cur_state[3] && (!cur_state[2]) && (!cur_state[1]) && (!cur_state[0])
//综合:其余的也不同看了会非常复杂,原因在于default: nxt_state == 4'b0001这句话导致其余情况必然要输出4'b0001

4. 降低if层数——真值表法 + 画波形/仿真

经常遇到这样的情况,always块内嵌套多层if,是否可以优化呢?

always@(posedge clk) begin
	if(!rstn)
		a <= 1'b0;
	else if(cond1) begin
			if(cond2)
				a <= res1;
			else
				a <= res2;
	end
	else
		a <= res3;
end
//----------------------------------------------
always@(posedge clk) begin
	if(!rstn)
		a <= 1'b0;
	else if(cond1 && cond2) 
		a <= res1;
	else if(cond1 && !cond2)
		a <= res2;
	else
		a <= res3;
end

综合出来的电路分别是

在这里插入图片描述

在这里插入图片描述
由于一个MUX需要 2与门、1或门、1非门

若a为1bit,那么第一个电路需要4与门、2或门、2非门、1触发器。第二个电路需要6与门、2或门、3非门、1触发器。

同时可以得出一个结论一个if对应一个MUX

上述过程经过了Vivado -> Flow Navigator -> RTL ANALYSIS -> Schemetic的验证

4.1. 多层if等价——case(1'b1)

如果if层数过多,则可以是使用case(1'b1)等价,会优先执行第一个满足条件语句的代码

例如

				case(1'b1)
					res_frac_ext[23]:	begin res_frac <= res_frac_ext[22:0]; 		   res_exp <= res_exp; 	    end
					res_frac_ext[22]:	begin res_frac <= {res_frac_ext[21:0],1'b0};   res_exp <= res_exp - 1;  end								
					res_frac_ext[21]:	begin res_frac <= {res_frac_ext[20:0],2'b0};   res_exp <= res_exp - 2;  end
					res_frac_ext[20]:	begin res_frac <= {res_frac_ext[19:0],3'b0};   res_exp <= res_exp - 3;  end
					res_frac_ext[19]:	begin res_frac <= {res_frac_ext[18:0],4'b0};   res_exp <= res_exp - 4;  end
					res_frac_ext[18]:	begin res_frac <= {res_frac_ext[17:0],5'b0};   res_exp <= res_exp - 5;  end
					res_frac_ext[17]:	begin res_frac <= {res_frac_ext[16:0],6'b0};   res_exp <= res_exp - 6;  end
					res_frac_ext[16]:	begin res_frac <= {res_frac_ext[15:0],7'b0};   res_exp <= res_exp - 7;  end
					res_frac_ext[15]:	begin res_frac <= {res_frac_ext[14:0],8'b0};   res_exp <= res_exp - 8;  end
					res_frac_ext[14]:	begin res_frac <= {res_frac_ext[13:0],9'b0};   res_exp <= res_exp - 9;  end
					res_frac_ext[13]:	begin res_frac <= {res_frac_ext[12:0],10'b0};  res_exp <= res_exp - 10; end
					res_frac_ext[12]:	begin res_frac <= {res_frac_ext[11:0],11'b0};  res_exp <= res_exp - 11; end
					res_frac_ext[11]:	begin res_frac <= {res_frac_ext[10:0],12'b0};  res_exp <= res_exp - 12; end
					res_frac_ext[10]:	begin res_frac <= {res_frac_ext[9:0] ,13'b0};  res_exp <= res_exp - 13; end
					res_frac_ext[9]:	begin res_frac <= {res_frac_ext[8:0] ,14'b0};  res_exp <= res_exp - 14; end
					res_frac_ext[8]:	begin res_frac <= {res_frac_ext[7:0] ,15'b0};  res_exp <= res_exp - 15; end
					res_frac_ext[7]:	begin res_frac <= {res_frac_ext[6:0] ,16'b0};  res_exp <= res_exp - 16; end
					res_frac_ext[6]:	begin res_frac <= {res_frac_ext[5:0] ,17'b0};  res_exp <= res_exp - 17; end
					res_frac_ext[5]:	begin res_frac <= {res_frac_ext[4:0] ,18'b0};  res_exp <= res_exp - 18; end
					res_frac_ext[4]:	begin res_frac <= {res_frac_ext[3:0] ,19'b0};  res_exp <= res_exp - 19; end
					res_frac_ext[3]:	begin res_frac <= {res_frac_ext[2:0] ,20'b0};  res_exp <= res_exp - 20; end
					res_frac_ext[2]:	begin res_frac <= {res_frac_ext[1:0] ,21'b0};  res_exp <= res_exp - 21; end
					res_frac_ext[1]:	begin res_frac <= {res_frac_ext[0:0] ,22'b0};  res_exp <= res_exp - 22; end
					res_frac_ext[0]:	begin res_frac <= 23'd0; 					   res_exp <= res_exp - 23; end
					default:			begin res_frac <= 23'd0; 					   res_exp <= 8'd0;		    end
				endcase

4.2. 参数化 多层if/case等价——for循环

如果组合逻辑中有太多if,或者case语句中的分支太多,如何实现parameter组合逻辑呢?可以用for循环实现。

for循环本质是生成语句,循环变量只能为reg或integer类型,for语句只可存在于always块内部

无论是if还是case以下三种写法等价,也就是说将else语句写到最前面不会综合出LATCH,已经过vivado验证

	
	//----------------------------------------------------------------------------------------------------
	// 输出nums最后那个1的下标: if写法(可综合、无LATCH)
	//----------------------------------------------------------------------------------------------------
	
	always@(*) begin
		if(nums[0])
			idx = 0 + sel[0] + sel[1];
		else if(nums[1])
			idx = 1 + sel[1] + sel[2];
		else if(nums[2])
			idx = 2 + sel[3] + sel[3];
		else if(nums[3])
			idx = 3 + sel[4] + sel[4];
		else if(nums[4])
			idx = 4 + sel[5] + sel[5];
		else if(nums[5])
			idx = 5 + sel[6] + sel[6];
		else if(nums[6])
			idx = 6 + sel[7] + sel[7];
		else if(nums[7])
			idx = 7 + sel[8] + sel[8];
		else
			idx = 0;
	end
	
	//----------------------------------------------------------------------------------------------------
	// 输出nums最后那个1的下标: case写法(可综合、无LATCH)
	//----------------------------------------------------------------------------------------------------
	
	always@(*) begin
		case(1'b1)
			nums[0]:	idx = 0 + sel[0] + sel[1];
			nums[1]:	idx = 1 + sel[1] + sel[2];
			nums[2]:	idx = 2 + sel[2] + sel[3];
			nums[3]:	idx = 3 + sel[3] + sel[4];
			nums[4]:	idx = 4 + sel[4] + sel[5];
			nums[5]:	idx = 5 + sel[5] + sel[6];
			nums[6]:	idx = 6 + sel[6] + sel[7];
			nums[7]:	idx = 7 + sel[7] + sel[8];
			default:	idx = 0;
		endcase
	end
	
	//----------------------------------------------------------------------------------------------------
	// 输出nums最后那个1的下标: 参数化if写法(可综合、无LATCH)
	//----------------------------------------------------------------------------------------------------
	
	integer i;
	
	always@(*) begin
		idx = 0;
		for(i=0;i<=7;i=i+1) begin
			if(nums[i])
				idx = i + sel[i] + sel[i+1];
		end
	end

其实integer i;等价于reg signed [31:0] i;,所以可以使用reg型变量进行单bit片选,但多bit片选则报错。

但是移位符号不会报错,如下两个相同结果的程序,均可编译通过,但是综合结果差别很大。

● 第一个程序,使用for循环与移位实现

module vivado_test_top(
   input	[3:0]	nums,
   input	[1:0]	sel,
   
   output reg	[3:0]	nums_high,
   output reg	[3:0]	nums_low
	);
	
	integer i;
	
	always@(*) begin
		nums_high = 4'd0;
		nums_low = 4'd0;
		
		if(sel == 0) begin
			nums_high = nums[3:0];
			nums_low = 4'd0;
		end
		
		for(i=1;i<=3;i=i+1) begin
			if(sel == i) begin
				nums_high = nums >> i;					
				nums_low = (nums << (4-i)) >> (4-i);		
			end
		end
	
	end
endmodule

综合结果:

在这里插入图片描述
● 第二个程序,使用if-else实现

module vivado_test_top(
   input	[3:0]	nums,
   input	[1:0]	sel,
   
   output reg	[3:0]	nums_high,
   output reg	[3:0]	nums_low
	);

	integer i;
	
	always@(*) begin
		if(sel == 0) begin
			nums_high = nums[3:0];
			nums_low = 4'd0;
		end
		else if(sel == 1) begin
			nums_high = nums[3:1];
			nums_low = nums[0];
		end
		else if(sel == 2) begin
			nums_high = nums[3:2];
			nums_low = nums[1:0];
		end
		else if(sel == 3) begin
			nums_high = nums[3];
			nums_low = nums[2:0];
		end
		else begin
			nums_high = 4'd0;
			nums_low = 4'd0;
		end
	end
endmodule

综合结果:

在这里插入图片描述

综上所述得出结论:
不涉及位宽选通 推荐使用for循环
涉及位宽选通 推荐不使用for循环

How to parameterized a case statement with don’t cares? - stackoverflow
How to define parameterized multiplexer using Systemverilog - stackoverflow

5. 时序违规——加入触发器 + 画波形图/仿真

在合适的地方加入触发器,然后画波形图/仿真。如果出现未达预期的时序,再在其他地方修正时序

RTL协议包拼凑

6. inout信号处理——输入为高阻、输出赋值

对于inout信号,常使用assign修饰,当该信号作为输出时,赋予其应该输出的值、当该信号作为输入时,赋予其高阻态。

module spi(
	output 		sck,
	output		csn,
	inout [3:0]	sdio
	);
...

// spi_dir指示sdio方向
// spi_dir为高表示sdio为输出信号、赋予其spi_dout
// spi_dir为低表示sdio为输入信号、赋予其高阻
assign sdio = (spi_dir)? spi_dout : 4'dz;		

// spi_dir为低表示sdio为输入信号、将输入值赋给spi_din
assign spi_din = (spi_dir)? 4'd0 : sdio;

endmodule

7. generate for循环与 一维数组

generate forfor的功能是一样的,都是生成语句,但用法有区别

generate for本质是生成语句,循环变量只能为genvar类型,generate for语句只可存在于 always块/assign语句 外部

verilog-2001允许使用多维数组,用法核心思想是只可对数组内单个元素进行操作,例如

module vivado_test_top(
	input [31:0] nums_i[5:0]				//错误,端口禁止声明数组
	);

//----------------------------------------------------
// 使用generate for循环初始化
//----------------------------------------------------

wire [31:0] mem[15:0];

genvar i;
generate
	for(i=0;i<=15;i=i+1) begin							//正确
		assign mem[i] = i;
	end
endgenerate

//----------------------------------------------------
// 访问
//----------------------------------------------------

wire [63:0] a1;
wire [31:0] a2[15:0];
wire [31:0] a3[2:0];

assign a1 = mem[1:0];										//错误,不可part-select数组
assign a1 = {mem[1], mem[0]};								//正确
assign a3 = {mem[2], mem[1], mem[0]};						//错误,不可直接使用数组本身
assign {a3[2], a3[1], a3[0]} = {mem[2], mem[1], mem[0]};	//正确
assign a2 = mem;											//错误,不可直接使用数组本身
						
assign a2[0] = mem[0];										//正确,只可直接使用数组元素
assign a2[1] = mem[1];

//----------------------------------------------------
// 传递
//----------------------------------------------------

wire [31:0] res;

assignment	U(
	.a		(mem		),					//错误,不可直接使用数组本身	
	.b		(mem[3:0]	),					//错误,不可part-select数组
	.mem_0	(mem[0]		),					//正确,只可直接使用数组元素	
	.mem_1	(mem[1]		),	
	.mem_2	(mem[2]		),	
	.mem_3	(mem[3]		),	
	.c		(res		)
);

endmodule

Verilog多维数组
Verilog初级教程(5)Verilog中的多维数组和存储器
Verilog中的数组

8. module递归 - 参数化动态规划

Verilog允许module递归,vivado可以正确综合。但注意要用generate if...else...作修正,否则会无限递归下去

可以使用递归module实现参数化动态规划。同一拍算得 s 1 , . . . , s n s1,...,sn s1,...,sn,要计算 f ( s 1 , . . . , s n ) = g ( s 1 , f ( s 2 , . . . , s n ) ) f(s1,...,sn) = g(s1,f(s2,...,sn)) f(s1,...,sn)=g(s1,f(s2,...,sn)),如果 n n n为参数,就无法显式写明 f ( s 1 , . . . , s n ) f(s1,...,sn) f(s1,...,sn),可以使用module递归列写。

例如计算 f ( s 1 , . . . , s n ) = s 1 + s 2 + . . . + s n f(s1,...,sn) = s1+s2+...+sn f(s1,...,sn)=s1+s2+...+sn,其中n被参数化,多少个数相加由用户配置parameter确定。RTL中就可以写成 f ( s 1 , . . . , s n ) = s 1 + f ( s 2 , . . . , s n ) f(s1,...,sn) = s1+f(s2,...,sn) f(s1,...,sn)=s1+f(s2,...,sn)进行递归实现。例程如下

module acc#(
	parameter n 	  = 4,
	parameter a_width = 16,
	parameter s_width = 16,					
	)(
		input 	[n*a_width-1:0]	a,
		output 	[s_width-1:0]	sum
	);
	
	
	generate if(n>1) begin
		wire [s_width-1:0]	sum_sub;
		acc#(
			.n						(	n-1						),
			.a_width 				(	a_width 				),
			.s_width 				(	s_width 				)					
		)SUB_ACC(				
			.a						(	a[(n-1)*a_width-1:0]	),
			.sum					(   sum_sub					)
		);
				
		assign sum = a[n*a_width -: a_width] + sum_sub;
	end
	else begin
		assign sum = a;
	end
	endgenerate

endmodule

RTL基于二叉加法树的流水乘法器IP设计

9. parameter、localparam:默认integer、 一次赋值、算式不综合、只可使用压缩数组

不指定位宽则默认为Integer类型。只可在初始化时赋予值,之后不可再更改,并且综合时计算式不会成综合成实际电路

	localparam NUM_MUL_U = (adata_width > bdata_width)? adata_width:bdata_width;
	localparam node_width_u = adata_width + bdata_width;
	
	localparam NUM_MUL = (data_type == 1)? (NUM_MUL_U-1) : NUM_MUL_U;
	localparam node_width = (data_type == 1)? (node_width_u-2) : node_width_u;
	
	localparam CNT = NUM*10+92/3200;			// 不会被综合
	localparam NUMS[5:0] = {1,2,3,4,5,6};		//错误,不可声明数组
	localparam [8*CNT-1:0] NUMS = {8'd1,8'd2,8'd3,8'd4,8'd5};	// 正确,压缩数组

10. Vivado 变位宽FIFO:先高位 后低位 空为可读数据不够 & Vivado XPM_FIFO:先低位 后高位 空为可读数据不够 & Vivado 变位宽BRAM:先低位 后高位

● vivado FIFO IP:
1)写位宽<读位宽
先入高位,后入低位;空标志拉高表示FIFO数据中不够读位宽,不代表没数!!!
如依次写入十六进制数0x00、0x11、0x22、0x33;空标志在0x33写入成功后才拉低,读出数据为0x00112233

2)写位宽>读位宽:
先出高位,再出低位;
如写入一个十六进制数0xAABBCCDD;依次读出数据为0xAA, 0xBB, 0xCC, 0xDD。

● vivado XPM_FIFO:
1)写位宽<读位宽
先入低位,后入高位;空标志拉高表示FIFO数据中不够读位宽,不代表没数!!!
如依次写入十六进制数0x00、0x11、0x22、0x33;空标志在0x33写入成功后才拉低,读出数据为0x33221100

2)写位宽>读位宽:
先出低位,再出高位;
如写入一个十六进制数0xAABBCCDD;依次读出数据为0xDD, 0xCC, 0xBB, 0xAA。

● BRAM:
1)写位宽<读位宽:
先入存低位,后入存高位;
如依次写入十六进制数0x00、0x11、0x22、0x33;读出数据为0x33221100。

2)写位宽>读位宽:
先出低位,再出高位;
如写入一个十六进制数0xAABBCCDD;读4次,依次读出数据为0xDD、0xCC、0xBB、0xAA。

11. -:+:$clog2()

来自Verilog2001标准

reg [31:0] big_vect;
reg [0:31] little_vect;
localparam [7:0] CS;

big_vect[ 0 +: 8] // == big_vect[ 7 : 0]
big_vect[15 -: 8] // == big_vect[15 : 8]

little_vect[ 0 +: 8] // == little_vect[0 : 7]
little_vect[15 -: 8] // == little_vect[8 :15]

CS[7:-4]		// == CS[7:4]

系统函数 $clog2(x)是将x取以2为底的对数并且向上取整。即
$clog2(8)=3
$clog2(9)=4
$clog2(10)=4

12. 多级组合逻辑 加装触发器

有NUM_LEVEL级相同运算(组合逻辑),要加入触发器形成ppl_stgs级流水,这些触发器加在哪里会使流水的建立时间最大呢?例如有13个数相加,要构成4级流水,这4个触发器加在哪个加法器之后?

首先肯定不能把ppl_stgs个触发器全加在运算末尾,这样建立时间是最小的。正确思路是将触发器均匀的加在各级组合逻辑中

RTL如下:

# 每隔inr_ff_level个层加装一层FF
localparam integer inr_ff_level = (NUM_LEVEL > ppl_stgs)? $ceil(real'(NUM_LEVEL)/real'(ppl_stgs)) : 1;

# 第level_num层运算加FF的条件		
if(level_num%inr_ff_level == 0)	
				
# 具有num_level层的二叉树根节点还需额外加多少个FF
localparam integer NUM_ROOT_DELAY = ppl_stgs - (NUM_LEVEL/inr_ff_level);

还是13个数相加、4级流水,inr_ff_level 取4,即每4层加装一层FF。则第4层、第8层、第12层加入FF,最后检测到FF加的不够就加在第13层。即:1 2 3 4| 5 6 7 8| 9 10 11 12| 13|

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Starry丶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值