草庐IT

6.3 Verilog RTL 级低功耗设计(上)

runoob 2023-03-28 原文

下表显示了在数字设计的各个层次上可减少功耗的百分比。RTL 级之后,功耗的减少量已经非常有限。

设计层次改善程度
系统级50% ~ 90%
RTL 级20% ~ 50%
门级10% ~ 15%
晶体管级5% ~ 10%
版图级< 5%

作为一个编写 Verilog 的伪码农,系统级减少功耗的工作也可参与一些,但重点应该放在 RTL 级来减少功耗。

下面就分 2 节来介绍从 RTL 级来减少功耗的常用方法。


并行与流水

对于一个功能模块,可以通过并行的方式实现,也可以通过流水线的方式实现,这两种方法都是用资源换速度。在一定的场合下灵活的使用这两种方法,可以降低功耗。

并行处理

并行处理,可以同时处理多条执行语句,使执行效率变高。所以在满足工作需求的条件下,采用并行处理,可降低系统工作频率,减少功耗。

例如,采用 1 个乘法器和 2 个乘法器(并行)来实现 4 个数据乘加运算的代码描述分别如下:

实例


//===========================================
//1 multiplier, high speed
module  mul1_hs
    (
        input           clk ,           //200MHz
        input           rstn ,
        input           en  ,
        input [3:0]     mul1 ,          //data in
        input [3:0]     mul2 ,          //data in
        output          dout_en ,
        output [8:0]    dout
     );

    reg                  flag ;
    reg                  en_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            flag   <= 1'b0 ;
            en_r   <= 1'b0 ;
        end
        else if (en) begin
            flag   <= ~flag ;
            en_r   <= 1'b1 ;
        end
        else begin
            flag   <= 1'b0 ;
            en_r   <= 1'b0 ;
        end
    end

    wire [7:0]           result = mul1 * mul2 ;

    // data output en
    reg [7:0]            res1_r, res2_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            res1_r         <= 'b0 ;
            res2_r         <= 'b0 ;
        end
        else if (en & !flag) begin
            res1_r         <= result ;
        end
        else if (en & flag) begin
            res2_r         <= result ;
        end
    end

    assign dout_en = en_r & !flag ;
    assign dout = res1_r + res2_r ;

endmodule

//===========================================
// 2 multiplier2, low speed
module  mul2_ls
    (
        input           clk ,           //100MHz
        input           rstn ,
        input           en  ,
        input [3:0]     mul1 ,          //data in
        input [3:0]     mul2 ,          //data in
        input [3:0]     mul3 ,          //data in
        input [3:0]     mul4 ,          //data in
        output          dout_en,
        output [8:0]    dout
     );

    wire [7:0]           result1 = mul1 * mul2 ;
    wire [7:0]           result2 = mul3 * mul4 ;

    //en delay
    reg                  en_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            en_r           <= 1'b0 ;
        end
        else begin
          en_r           <= en ;
        end
    end

    // data output en
    reg [7:0]            res1_r, res2_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            res1_r         <= 'b0 ;
            res2_r         <= 'b0 ;
        end
        else if (en) begin
            res1_r         <= result1 ;
            res2_r         <= result2 ;
        end
    end
    assign dout          = res1_r + res2_r ;
    assign dout_en       = en_r ;

endmodule

testbench 描述如下。

实例


`timescale 1ns/1ps
module test ;
    reg          rstn ;
    //mul1_hs
    reg          hs_clk;
    reg          hs_en ;
    reg [3:0]    hs_mul1 ;
    reg [3:0]    hs_mul2 ;
    wire         hs_dout_en ;
    wire [8:0]   hs_dout ;
    //mul1_ls
    reg          ls_clk = 0;
    reg          ls_en ;
    reg [3:0]    ls_mul1 ;
    reg [3:0]    ls_mul2 ;
    reg [3:0]    ls_mul3 ;
    reg [3:0]    ls_mul4 ;
    wire         ls_dout_en ;
    wire [8:0]   ls_dout ;

    //clock generating
    real         CYCLE_200MHz = 5 ; //
    always begin
        hs_clk = 0 ; #(CYCLE_200MHz/2) ;
        hs_clk = 1 ; #(CYCLE_200MHz/2) ;
    end
    always begin
        @(posedge hs_clk) ls_clk = ~ls_clk ;
    end

    //reset generating
    initial begin
        rstn      = 1'b0 ;
        #8 rstn      = 1'b1 ;
    end

    //motivation
    initial begin
        hs_mul1   = 0 ;
        hs_mul2   = 16 ;
        hs_en     = 0 ;
        #103 ;
        repeat(12) begin
            @(negedge hs_clk) ;
            hs_en          = 1 ;
            hs_mul1        = hs_mul1 + 1;
            hs_mul2        = hs_mul2 - 1;
        end
        hs_en = 0 ;
    end

    initial begin
        ls_mul1   = 1 ;
        ls_mul2   = 15 ;
        ls_mul3   = 2 ;
        ls_mul4   = 14 ;
        ls_en     = 0 ;
        #103 ;
        @(negedge ls_clk) ls_en = 1;
        repeat(5) begin
            @(negedge ls_clk) ;
           ls_mul1        = ls_mul1 + 2;
           ls_mul2        = ls_mul2 - 2;
           ls_mul3        = ls_mul3 + 2;
           ls_mul4        = ls_mul4 - 2;
        end
        ls_en = 0 ;
    end

    //module instantiation
    mul1_hs    u_mul1_hs
    (
      .clk              (hs_clk),
      .rstn             (rstn),
      .en               (hs_en),
      .mul1             (hs_mul1),
      .mul2             (hs_mul2),
      .dout             (hs_dout),
      .dout_en          (hs_dout_en)
    );

    mul2_ls    u_mul2_ls
    (
      .clk              (ls_clk),
      .rstn             (rstn),
      .en               (ls_en),
      .mul1             (ls_mul1),
      .mul2             (ls_mul2),
      .mul3             (ls_mul3),
      .mul4             (ls_mul4),
      .dout             (ls_dout),
      .dout_en          (ls_dout_en)
    );

    //simulation finish
    always begin
        #100;
        if ($time >= 1000)  begin
            #1 ;
            $finish ;
        end
    end
   
endmodule

仿真结果如下。

由图可知,两种实现方法输出结果一致,但并行处理方法的工作频率降低了一半,功耗会有所降低,此时设计面积也会有所增加。

流水线处理

《Verilog 教程》中讲述过,一个连续工作的 N 级流水线设计,效率提升倍数约为 N。同并行设计一样,采用流水线设计时,也可以适当降低工作频率来减少功耗。

从另一个角度讲,流水线设计可以将一个较长的组合路径分成 N 级流水线。路径长度缩短为原始路径长度的 1/N。此时如果时钟频率不变,则在一个周期内,只需要对电容 C/N 进行充放电,而不是对原来的电容 C 进行充放电。因此在相同的频率要求下,可以采用较低的电源电压来驱动系统,使功耗降低。

假设在一个设计中,关键路径是一个 32bit X 32bit 的乘法器。该乘法器的整体电容为 C,工作电压为 V。

不加流水线时,要达到此工作频率,工作电压应该为 V。

采用两级流水线方式时,该路径被分成两部分。对于每一部分,整体电容变为 C/2。如果要达到原来的工作频率,工作电压可以降为 βV(β<1)。整个系统功耗降低为原来的 β^2。

流水线具体设计方法,可参考 《Verilog 教程》章节中 《6.7 Verilog 流水线》一节。


资源共享与状态编码

资源共享

当设计中一些相同的运算逻辑在多处使用时,就可以使用资源共享的方法避免多个运算逻辑的重复出现,减少资源的消耗。

例如一个比较逻辑,没有使用资源共享的代码描述如下:

实例


    always @(*) begin
        case (mode) :
            3'b000:         result  = 1'b1 ;
            3'b001:         result  = 1'b0 ;
            3'b010:         result  = value1 == value2 ;
            3'b011:         result  = value1 != value2 ;
            3'b100:         result  = value1 > value2 ;
            3'b101:         result  = value1 < value2 ;
            3'b110:         result  = value1 >= value2 ;
            3'b111:         result  = value1 <= value2 ;
        endcase
    end
对上述代码进行优化,描述如下:
    wire equal_con       = value1 == value2 ;
    wire great_con       = value1 > value2 ;
    always @(*) begin
        case (mode) :
            3'b000:         result  = 1'b1 ;
            3'b001:         result  = 1'b0 ;
            3'b010:         result  = equal_con ;
            3'b011:         result  = equal_con ;
            3'b100:         result  = great_con ;
            3'b101:         result  = !great_con && !equal_con ;
            3'b110:         result  = great_con && equal_con ;
            3'b111:         result  = !great_con ;
        endcase
    end

第一种方法综合实现时,如果编译器优化做的不好,可能需要 6 个比较器。第二种资源共享的方法只需要 2 个比较器即可完成相同的逻辑功能,因此在一定程度会减少功耗。

状态编码

对于一些变化频繁的信号,翻转率相对较高,功耗相对较大。可以利用状态编码的方式来降低开关活动,减少功耗。

例如高速计数器工作时,使用格雷码代替二进制编码时,每一时刻只有 1bit 的数据翻转,翻转率降低,功耗随之降低。

例如进行状态机设计时,状态机切换前后的状态编码如果只有 1bit 的差异,也会减少翻转率。

操作数隔离

操作数隔离原理:如果在某一段时间内,数据通路的输出是无用的,将输入置成固定值,数据通路部分没有翻转,功耗就会降低。 

一个乘法器电路图如下所示。

当 sel0 = 0 或 sel1 = 1 时,乘法器 Multiplier 的输出结果并不能通过两个 Mux 到达寄存器的输入端。即寄存器并不能保存当前乘法器的结果,此次乘法运算是没有必要的。在此种条件下,采用操作数隔离,使乘法器不工作保持静态,也可以节省功耗。

对上述电路进行一个优化,如下图所示。

操作数隔离之后,当 sel0 = 0 或 sel1 = 1 时,乘法器输入端始终为 0,没有信号翻转,乘法器没有进行额外的无效工作,所以功耗会降低。

一般来说,操作数隔离的操作发生在代码综合的时候。这个过程往往是人为可设置、编译器可自动识别的。当然,良好的代码风格,在编写 RTL 电路时就考虑周全,更加有助于实现操作数隔离,从而降低功耗。

乘法器没有使用操作数隔离时,Verilog 代码描述如下:

实例


//no isolation
module  oper_isolation1
    (
     input                clk ,           //100MHz
     input [1:0]          sel ,
     input [3:0]          din1 ,          //data in
     input [3:0]          din2 ,          //data in
     output reg [7:0]     dout
     );

    reg [7:0]       res ;
    always @(*) begin
        res       = din1 * din2 ;
    end

    always @(posedge clk) begin
        if (sel == 2'b01) begin
            dout   <= res ;
        end
    end
endmodule

乘法器使用操作数隔离时,Verilog 代码描述如下:

实例


//using isolation
module  oper_isolation2
    (
    input                clk ,           //100MHz
    input [1:0]          sel ,
    input [3:0]          din1 ,          //data in
    input [3:0]          din2 ,          //data in
    output reg [7:0]     dout
    );

    wire [3:0]           mul1 = sel == 2'b01 ? din1 : 0 ;
    wire [3:0]           mul2 = sel == 2'b01 ? din2 : 0 ;
    reg [7:0]            res ;
    always @(*) begin
        res       = mul1 * mul2 ;
    end

    always @(posedge clk) begin
        if (sel == 2'b01) begin
            dout   <= res ;
        end
    end
endmodule

本章节源码下载

Download

有关6.3 Verilog RTL 级低功耗设计(上)的更多相关文章

  1. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  2. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  3. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  4. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  5. ruby-on-rails - 设计注册确认 - 2

    我在我的项目中有一个用户和一个管理员角色。我使用Devise创建了身份验证。在我的管理员角色中,我没有任何确认。在我的用户模型中,我有以下内容:devise:database_authenticatable,:confirmable,:recoverable,:rememberable,:trackable,:validatable,:timeoutable,:registerable#Setupaccessible(orprotected)attributesforyourmodelattr_accessible:email,:username,:prename,:surname,:

  6. ruby-on-rails - 设计通过 reset_password_token 获取用户 - 2

    我正在尝试创建密码规则来设计可恢复的密码更改。我通过passwords_controller.rb做了一个父类(superclass),但我需要在应用规则之前检查用户角色,但我所拥有的只是reset_password_token。 最佳答案 假设您的模型是用户:User.with_reset_password_token(your_token_here)Source 关于ruby-on-rails-设计通过reset_password_token获取用户,我们在StackOverflow

  7. ruby-on-rails - Rails 5,公寓和设计 : sign in with subdomains are not working - 2

    我已经使用Apartment设置了一个Rails5应用程序(1.2.0)和Devise(4.2.0)。由于某些DDNS问题,应用只能在app.myapp.com下访问(请注意子域app)。myapp.com重定向到app.myapp.com。我的用例是每个注册该应用的用户(租户)都应该通过他们的子域(例如tenant.myapp.com)访问他们的特定数据。用户不应限定在其子域内。基本上应该可以从任何子域登录。重定向到租户的正确子域由ApplicationController处理。根据Devise标准,登录页面位于app.myapp.com/users/sign_in。这就是问题开始的

  8. ruby-on-rails - 设计中的 ArgumentError::RegistrationsController#new 错误的参数数量(2 代表 0..1) - 2

    我在关注RyanbatesRailsCast的devise和omniauth(第235集-devise-and-omniauth-revised)。当我尝试使用Twitter登录时,标题中不断出现错误。defself.new_with_session(params,session)ifsession["devise.user_attributes"]new(session["devise.user_attributes"],without_protection:true)do|user|user.attributes=paramsuser.valid?end完整跟踪:C:/Ruby20

  9. ruby-on-rails - 使用用户或管理员模型和 Basecamp 样式子域设计登录 - 2

    我为Devise用户和管理员提供了不同的模型。我也在使用Basecamp风格的子域。除了我需要能够以用户或管理员身份进行身份验证的一些Controller和操作外,一切都运行良好。目前我有authenticate_user!在我的application_controller.rb中设置,对于那些只有管理员才能访问的Controller和操作,我使用skip_before_filter跳过它。不幸的是,我不能简单地指定每个Controller的身份验证要求,因为我仍然需要一些Controller和操作才能被用户或管理员访问。我尝试了一些方法都无济于事。看来,如果我移动authentica

  10. ruby-on-rails - 自定义设计 Cookie - 2

    我在我的Rails应用程序中使用设计。我在租户庄园中配置了它,其中帐户/session的范围限定为子域。例如:http://subdomain1.example.com/http://subdomain2.example.com/...这很好用,但我想为“super管理员”添加一个子域,允许这些用户导航到所有其他子域而无需重新验证。这将是这样的:http://admin.example.com/是否可以自定义仅在管理子域上生成的cookie,以便它在所有其他子域上都有效? 最佳答案 Cookie域的定义越不具体,它们的包容性就越大,

随机推荐