在RTL书写中如何考虑延迟,面积等

20 篇文章 9 订阅
19 篇文章 35 订阅

在RTL书写中如何考虑延迟,面积等

reference

来自中国大学MOOC上西南交通大学的慕课《芯动力-硬件加速设计方法》,这里放的是笔者的一些学习笔记,示例代码修正等,以下是他们的课程大纲,有兴趣的朋友也可以看看

image-20221007214614267

笔者会集中在第二到第五章,也就是具体的设计部分,后续也会陆续更新别的关于IC设计相关的东西。

在RTL编码中考虑时延

分if-else,case 的各种情况分开讨论,主要目的是将分支支路中 晚到的信号放到离输出最近的一级中.

对多if语句:

代码

此处课件应该讲错了,为下面代码为多if语句

module mult_if (a,b,c,d,sel,z)
	input a,b,c,d;
    input[3:0]sel;
	output Z;
	reg Z;
    always@(*) begin
	    z=1'b0;
        if (sel[o])z=a;
        if (sel[1])z=b;
        if (sel[2])z =c;
        if (sel[3])z =d;
	end
endmodule

对应的硬件结构为有优先级的级联多路选择器

如果有个别信号到来的比较晚。应该要尽可能把这个延迟较大的分支单独拿出来,放到离出口最近的选择器中。假设b信号的延迟较大,可以在不影响逻辑功能的前提下,将b往后拉,如:

    always@(*) begin
	    z=1'b0;
        if (sel[o])z=a;
        if (sel[2])z =c;
        if (sel[3])z =d;
        // if (sel[1])z=b;
        if(sel[1]& ~(sel[2]|sel[3]))
            z = b_is_late;
	end

此时如果仅将if (sel[1])z=b;直接放下会使原来的逻辑功能改变。修改后的电路:

对单if语句

代码:

module single_if_late (A,C,CTRL_is_late_arriving,Z):
input [6:1]A:
input [5:1]C;
input CTRL_is_late_arriving:
output Z;
reg Z;
always@(A or C or CTRL_is_late_arriving)begin
	if(C[1]==1'b1)
        Z=A[1];
    else if (C[2]==1'b0)
        Z=A[2];
	else if (C[3]==1'b1)
        Z=A[3];
    else if (C[4]==1'b1&CTRL_is_late_arriving==1'b0)
        Z=A[4];
    else if (C[5]==1'b0)
        Z=A[5];
	else
        Z=A[6];
	end
endmodule

其中 CTRL_is_late_arriving 到来延迟较大,此时就会把这个延迟较大的分支单独拿出来,放到离出口最近的选择器中:

具体的修改代码是这样的,先将原来的if打散,再在后面出口做一个选择,单独处理A[4]:

module single_if_improved (A,C,CTRL_is_late_arriving,Z);
input [6:1]A;
input [5:1]C;
input CTRL_is_late_arriving;
output Z;
reg Z,Z1;
wire Z2,prev_cond;
always@(A or C)begin
	if(C[1]==1'b1)
        Z1=A[1];
    else if (C[2]==1'b0)
        Z1=A[2];
    else if (C[3]==1'b1)
        Z1=A[3];
    else if (C[5]==1'b0)
        Z1=A[5];
    // removed the branch with the late_arriving control signal
    // else if (C[4]==1'b1&CTRL_is_late_arriving==1'b0)
	else
        Z1=A[6];
end
    
    assign Z2=A[4];
    assign prev_cond=(C[1]=1'b1)l(C[2]==1'b0)||(C[3]==1'b1);
    
always @ (C or prev_cond or CTRL_is_late_arriving or Z1 or Z2) begin
    if((C[4]==1'b1)&&(CTRL_is_late_arriving==1'b0))
		if(prev_cond) Z=Z1:
		else Z=Z2;
	else Z=Z1 ;
end
endmodule

case和if嵌套

module case_in_if_01(A,DATA_is_late_arriving,C,sel,Z);
input[8:1] A;
input DATA_is_late_arriving;
input [2:0] sel;
input [5:!] C;
output Z;
reg Z;
    
always @ (sel or C or A or DATA_is_late_arriving) begin
	if (C[1])
		Z=A[5];
    else if (C[2]==1'b0)
		Z=A[4];
    else if (C[3])
		Z=A[1]:
	else if (C[4])
		case (sel)
			3'b010:Z=A[8];
			3'b011:Z=DATA_is_late_arriving;
			3'b101:Z=A[7]:
			3'b11O:Z=A[6];
		default:Z=A[2];
		endcse
	else if (C[5]==1'b0)
		Z=A[2];
	else Z=A[3];
end
endmodule

假设case中有一个支路到达比较晚,其电路结构为:

此时与单if相似,将这条通路往外提:

代码与单if-else结构的相似,去掉了case分支,单独判断这个分支:

module case_in_if_01(A,DATA_is_late_arriving,C,sel,Z);
input[8:1] A;
input DATA_is_late_arriving;
input [2:0] sel;
input [5:!] C;
output Z;
reg Z,Z1;
reg FIRST_IF;
always @ (sel or C or A or DATA_is_late_arriving) begin
	if (C[1])
		Z1=A[5];
    else if (C[2]==1'b0)
		Z1=A[4];
    else if (C[3])
		1Z=A[1]:
	else if (C[4])
		case (sel)
			3'b010:Z1=A[8];
			// 3'b011:Z=DATA_is_late_arriving;
			3'b101:Z1=A[7]:
			3'b11O:Z1=A[6];
		default:Z1=A[2];
		endcse
	else if (C[5]==1'b0)
		Z1=A[2];
	else Z=A[3];
	FIRST_IF=(C[1]==1'b1) || (C[2]==1'b0) ||(C[0]==1'b1);
	if(!FIRST_IF && C[4] &&(sel==3'b011))
        Z=DATA_is_late_arriving;
    else Z=Z1;
end
endmodule

“先加后选”和“先选后加”

注意“先加后选”和“先选后加”两种方法对数据通道延迟的影响,对以下代码:

module BEFORE(ADRESS, PTR1,PTR2, B,CONTROL,COUNT);
input [7:O] PTR1, PTR2;
input CONTROL: //CONTROL is late arriving
output [15:0] COUNT:
parameter [7:0] BASE=8'b1000000;
wire [7:O] PRT,OFFSET;
wire [15:0] ADDR;
    
assign PTR = (CONTROL ==1'b1)?PTR1:PTR2;
assign OFFSET=BASE-PTR:
assign ADDR = ADRESS-{8'h00-OFFSET};
assign COUNT=ADDR+B;
endmodule

这里是先选后加,假设控制信号到达比较晚,则需要改为先加后选:

代码修改如下

assign OFFSET1 = BASE-PTR1;//could be T(BASE,PTR)
assign OFFSET2 = BASE-PTR2; //could be f(BASE,PTR)
assign ADDR1 = ADRESS-{8'h00-OFFSET1);
assign ADDR2 = ADRESS-{8'h00-OFFSET2};
assign COUNT1=ADDR1+B;
assign COUNT2=ADDR2+B;
assign COUNT=(CONTROL==1'b1)?COUNT1:COUNT2;

复制数据路径将CONTROL信号放到最后

调整计算顺序

在下例中,包含一个加法器和比较器:

module cond_oper(A,B,C,D,Z):
parameter N=8;
input [N-1:O] A,B,C,D; //A is late arriving
output [N-1:0] Z;
reg [N-1:O] Z;
always@(A,B,C,D) begin
	if(A+B<24)Z<=C;
	else Z<=D;
end
endmodule

假设A信号到来较晚,此时调整A的计算路径,原来的计算顺序为:

通过调整代码:

always@(A,B,C,D) begin
	if(A<24-B)Z<=C;
	else Z<=D;
end

A将少过一个减法器,此时电路结构变为:

在RTL编码中考虑面积

  • 随着芯片工艺的进步和生产成本的降低,面积显得没有时序问题重要。

  • 减少设计面积可以

    • 成本降低、功耗降低,
    • 特別是对于FPGA的设计,直接决定FPGA的选型。
  • 一般综合过程中可以对面积进行优化,但在RTL编码中如果注意节约设计面积,往往可以达到事半功倍的效果。

  • 减少设计的面积

    • 估计设计使用资源的数量
    • 知道设计中那些部分占用了较大的面积(触发器,加法器,乘法器)
  • 触发器的资源估计

    • 触发器的数量:触发器的数量由功能决定,很难减少

    • 触发器的面积:比较好估计

操作符优化

所以优化目标大头放在 组合逻辑 中,其对应着RTL代码中的各种操作符 RTL代码屮的一个“+“可能对应着一个64位的加法器。以下这些操作符都可能产生较大的组合逻辑,使用时应加以重视,如“+”、“-”、“×”、“/”以及条件语句的比较运算。对于这些操作,首先应该判断其必要性,是否能用更简单的运算代替。

比如考虑

if(A<32)

可以将其修改为

if(A[5]==1'b1)

通过这种方式即可节省一个6bit的比较器

此处例子中,需要考虑A的自身位宽,未必适合于所有设计,对所有定值比较都可以这样考虑

资源共享

如果,必须使用复杂的运算符,则应考虑是否可以资源共享。尽管电路逻辑综合工具也会在综合的过程中采用资源共享的方法进行优化,但是,综合器的策略是有限的,因此在编写RTL的时候,应该尽量考虑共享,而不是把这项工作完全留给综合工具。

比如对以下代码:

If(y1>a+b+q)
	statement1;
If(y2>a+b+r)
	statement2;
If(y3>a+b+s)

可以修改为:

sum<=a+b;
If(yl>sum+q)
	statement1;
If(y2>sum+r)
	statement2;
If(y3>sum+s)

则可以减少两个不必要的加法器

多比特

多比特的信号也往往会占用较大的资源,因为使用这些信号的操作都是对所有的比特进行的,相当于成倍使用资源,因此,对这类信号的操作也应重视。

  • 应该看一看这个信号的所有比特是否都需要参与操作,如果不是,则可以只对需要的部分比特进行操作。

假设:

访问—RAM的地址有8比特,而写入操作时从0开始,每隔32个地址写入一个值,地址的产生可以有两种写法。

addr <= addr + 32;

addr[7:5] <= addr[7:5] + 1;
addr[4:0] <= addr[4:0] + 0;

逻辑优化

针对不同的设计,还有可能有各种各样的优化和改进的方法,但是,归结到一点,就是编写代码时,应对操作符有足够的重视,对有可能简化的地方尽量简化。逻辑简化往往在减少面积的同时也减少了延迟,因此,是值得花费一些时间的。

在RTL编码中考虑功耗

考虑电路割点的功耗:
P d = ∑ a f C V 2 P_d = \sum a f C V^2 Pd=afCV2
其中:

  • P d P_d Pd : 电路割点的功耗总和
  • a a a: 电路翻转次数
  • f f f:电路工作频率
  • C C C:负载电容
  • V V V: 电压值

在RTL设计中无法改变负载电容工作电压,所以在RTL级主要考虑尽量降低电路的翻转频率

主要措施包括如下:

  • 门控时钟,门控时钟是电路设计最常用也是最有效的方法,在逻辑综合阶段可以让综合工具自行插入。

  • 增加使能信号,使得部分电路只有在需要工作时才工作;

  • 对芯片各个模块进行控制,在需要工作时才工作;

  • 除了有用信号和时钟的翻转会消耗功耗,组合逻辑产生的毛刺也会大量消耗功耗。但是,毛刺在设计中无法避免,因此,只有尽呈减少毛刺在电路中的传播,才可以减少功耗。即在设计中,尽量把产生毛刺的电路放在传播路径的最后。另外,可以使用一些减少毛刺的技术。

  • 对于有限状态机,可以通过低功耗编码来减少电路的翻转。比如对4比特状态编码:

更改状态编码后:

总的来说,使用这些技术时,应首先

  • 考虑全局的功耗控制
  • 在RTL编码中,注意消耗功率较多的电路,如状态机、译码器、多路选择器等。
  • 在综合中,使用门控时钟和其他减少功耗的优化技术。
  • 这里要注意下门控时钟和增加使能控制的区别。增加使能仅仅是使得电路的信号不在翻转,但是时钟每个周期还会继续翻转。而门控时钟则是直接关掉时钟,这种方法效果更好。

在RTL编码中考虑布线问题

布线(routing)是芯片设计中最后的流程,其功能是根据门级网表的描述实现各个单元的连接

布局(placement)是芯片设计中最关键的因素,但即使使用最好的布局工具,还是可能出现无法布通的情况

-> 如果可以在RTL编码阶段考虑代码可能对布线产生的影响,就可能避免最后出现无法布通的情况。

布线阶段,通常热点是一个影响布线质量的问题。

热点是指设计的功能需要在一个面积内占用大量的布线资源:

热点产生原因:RTL编码时使用了特定的结构,如很大的MUX:

  • 这种结构产生的热点,在综合的时候,导致的延迟是看不出来 只有到了布线阶段才能给看到它的负面影响。因此,我们在RTL阶段应该重视这种电路,及早发现可能在布线阶段产生的问题。

  • 如果设计的功能中确实需要采用大的mux,可以通过其他方式改变他的结构。其基本的思路是将一个大的mux分解为多级较小的mux。

小结

心中有电路,真的好重要

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小何的芯像石头

谢谢你嘞,建议用用我的链接

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

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

打赏作者

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

抵扣说明:

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

余额充值