RTL Coding Tech

本文详细探讨了在RTL设计中如何考虑功耗、优化逻辑资源消耗,如通过简化if嵌套、使用Grey码、参数化多层if/case等。还介绍了如何处理时序违规、inout信号,并讨论了Verilog中的generate for循环和递归模块在动态规划中的应用。此外,还分析了Vivado FIFO的位宽变化和触发器的添加位置对性能的影响。

本文是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(...) 2 1 1 0
m位无符号数加法器(m>=2) a[m-1:0] + b[m-1:0] 8m-5 4m-3 6m-4 0 注意最终的结果一个(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'dx 4m-4 2m-zero(m'dx)-1 4m-(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-1 0 0 0 等价m个叶子节点且度1为0的二叉树,节点总数为2m-1,再去掉叶子节点就为m-1
|a[m-1:0] 0 m-1 0 0 等价m个叶子节点且度1为0的二叉树,节点总数为2m-1,再去掉叶子节点就为m-1
~a[m-1:0] 0 0 m 0 注意最终的结果还是一个m位数据,每一位取反
a[m-1:0] & m'dx 0 0 0 0 注意最终的结果还是一个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 0 0 0 0 注意最终的结果还是一个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-1 m 2m 0 先是a和b每一位相同或,需要2m个与门、m个或门、2m个非门。之后的与门判等结构等价m个叶子节点且度1为0的二叉树,n2节点总数为m-1。因此一共有与门3m-1个与门、m个或门、2m个非门
a[m-1:0] == m'dx m-1 0 zero(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]+1 0.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+y m-y x 0 此处只讨论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] << 1 0 0 0 m

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

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Starry丶

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

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

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

打赏作者

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

抵扣说明:

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

余额充值