《低功耗方法学》翻译——第八章:低功耗IP设计

《低功耗方法学》翻译——第八章:低功耗IP设计_第1张图片



第八章:低功耗IP设计

前几章从系统架构师和芯片设计者的角度讨论了低功耗设计。本章从复杂IP设计工程师的角度来描述低功耗设计,如处理器、DSP、USB、PCI Express和总线基础架构。到目前为止,我们已经假设IP是相对固定的,我们必须增加低功耗能力。现在,我们将讨论如何设计复杂的IP,以满足我们的低功耗目标。

今天,绝大多数复杂芯片都是使用IP设计的——第三方或内部开发。设计好的IP的关键是以一种允许它在多个应用程序中使用的方式来设计它。

为了确保一个IP可以在多个需要低功率的应用程序中有效地使用,我们必须设计使其能够与不同的功率策略一起使用。在一个应用程序中,时钟门控和多电压阈值库可能提供足够低的功率。对于其他应用,可能需要针对性的片上功率门控。在其他应用中,动态电压缩放可能是实现芯片功率目标的关键。

为了满足这些各种需求,我们需要做以下工作:

  • 分区设计,以支持各种低功率策略,特别是功率门控
  • 包括对电源门控的明确支持
  • 开发参考功耗目标文件
  • 设计具有低功率功能的时钟和复位策略
  • 封装IP以支持低功耗
  • 使用各种低功耗策略来验证IP

在我们经历IP的架构、设计和包装过程时,我们需要记住,在芯片中的IP的实际实现中,可以使用以下任何一种或所有技术:

  • Multi VT
  • 时钟门控
  • 电源门控(内部和/或外部)
  • 电压缩放

对于某些类型的IP,可能需要对这些功能提供不同类型的支持。

内存和其他硬IP块对低功耗有特殊的要求。低功率存储器通常有各种模式:正常工作模式、保留模式和关机模式。在保留模式下,电压会降低到保留数据所需的最小值,但低于读写所需的电压。

USB或PCI Express等IO标准的物理层接口通常有较多的电源模式。除了正常工作模式之外,还可以是驱动功率接近于零的完全关闭模式。可能有一个额外的操作模式,其中足够的电路通电,以便它可以唤醒以响应其接口上的活动。

可配置的软IP,因为它可以由用户配置,所以提供了一个复杂的设计挑战,在某种意义上是硬IP的超级挑战集。对于softIP,必须以用户可配置的方式支持多种强大、易使用模式和多种节能技术。如何设计这样的IP是本章其余部分的重点。

我们以SALT芯片上包含的USB On-the-Go(OTG)IP为例。OTG核心是synopsys数字软IP核心,设计之初规划具有功率门控,但实际上从来没有添加到电源门控能力。我们修改了RTL,增加了一个电源门控控制器、保留寄存器和隔离单元。当然,今天,我们将使用UPF来描述大多数这些修改,如本章后面所述。

8.1 功率门控的架构和分区

体系结构的一个良好的工作定义(至少在IP设计的上下游中)是IP的分区和接口设计。在支持各种低功耗策略时,功率门控提出了IP架构中最大的新架构挑战。

为支持功率门控,我们需要:

  • 决定何时/如何将IP断电上电;
  • 决定哪些模块需要功率门控,哪些模块需要一直上电;
  • 设计功率控制器控制断电上电的顺序;
  • 决定哪些信号在断电期间需要隔离;
  • 研究一种时钟、复位以及电源控制信号的启动策略;

8.1.1 如何/何时关闭

在SALT芯片上,我们包括了一个电源门控版本的CPU和USB OTG数字核心。CPU的策略是由软件控制断电顺序。当软件确定要关闭CPU时,它会向CPU电源控制器发出信号。然后控制器产生断电顺序。1个处理器被激活来响应一个中断足矣。当适当的中断发生时,例如中断源自定时器外围设备或从外部源,CPU的电源控制器然后执行所有的通电序列。

USB OTG的策略是在空闲时间内关机,但只有在CPU允许的情况下才能关机。CPU在USB OTG的寄存器中写入一个使能位来关闭电源——本质上是说它已经完成了事务。然后,USB OTG等待USB总线空闲3ms(表明USB OTG可以进入暂停( SUSPEND )模式)。当进入SUSPEND时,USB信号指示USB电源控制器开始断电顺序。足够的USB OTG,它可以响应CPU的读写或USB总线上的活动。如果CPU清除了断电使能位,或者如果在USB总线上有活动,那么控制器将执行通电顺序。

8.1.2 什么关闭什么保持

图8-1显示了USB OTG数字核的简化图。在断电期间,总线接口单元保持通电,以便它可以响应一个CPU请求来通电。类似地,PHY接口块保持通电,以便如果检测到USB活动,它可以向电源控制器发出信号并唤醒该核心。时钟和复位块也保持通电,为总线接口单元和PHY接口提供时钟。

USB OTG核心的所有其余逻辑都是电源门控的。状态和控制寄存器在电源门控序列使用由单个引脚NRETAIN控制的保留寄存器进行保存和恢复。协议引擎,因为它从头开始每个交易,只是在通电时复位。

只是为了方便起见,电源控制器包含在总线接口单元的AHB从模块中——它在AHB时钟下运行,需要在电源门控期间保持通电,并且电源门控使能寄存器也位于此处。但控制器也可以很容易地成为一个单独的块。

在USB OTG数字核心的电源门控区域中,有两个时钟域——AHB时钟域和PHY时钟域。控制信号在跨越两个时钟域时使用了同步器,包括功率门控制信号。因此,从断言一个功率门控控制信号(在ahb域中)到它可以有效作用于PHY域的时间延迟并不是确定性的。在事实中可以有很大的不同,因为这两个领域之间的时序关系在不同的应用中可以非常不同。

因此,大量的电源门控控制信号采用请求-确认握手机制。

《低功耗方法学》翻译——第八章:低功耗IP设计_第2张图片

8.2 USB OTG的功率控制器设计

USB OTG内的功率控制器使用`ifdef进行条件编译;如果用户不想使用功率门控,设计中的控制器将不会编译。

功率控制器是一个简单的状态机用于控制如下信号:

  • pwr_reset_n //协议引擎的复位信号
  • gate_hclk //控制信号用于关闭AHB域的时钟
  • h2pd_stop_pclk //控制信号用于关闭PHY域时钟
  • bius_pwr_clamp //控制信号控制AHB域的钳夹输出
  • h2pl_pwr_clamp //控制信号控制PHY域的钳夹输出
  • pwr_dwn_req_n //控制信号请求断电(低电平有效)
  • retain_n //负边沿保存;正边沿恢复 

接收如下输入:

  • pwr_dwn_ack_n //对于pwr_dwn_req的确认信号
  • stop_pclk_ack //h2pd_stop_clk的确认信号
  • pwr_clamp_ack //h2pl_pwr_clamp的确认信号
  • suspend_detected //表明3ms内没有USB活动
  • fifo_flushed //表明所有USB事务完成
  • wkup_res_det //表明检测到USB总线的活动性
  • enable_power_gating //来自CPU,使能功率门控

《低功耗方法学》翻译——第八章:低功耗IP设计_第3张图片

功率控制器检测到suspend_detected信号断言(功率门控使能位在状态寄存器中置位),则开启断电序列。该顺序如图8-2所示,描述如下:

  • 它会等待,直到fifo_flushed被断言。这表示所有挂起的交易都已完成,并且存储挂起事务的fifo为空。
  • 然后,它断言bius_pwr_clamp信号(到AHB时钟域)和h2pl_pwr_clamp信号(到PHY时钟域)来钳夹USB OTG的功率门控部分的输出。
  • 然后它等待pwr_clamp_ack(来自PHY时钟域)。这告诉控制器,隔离单元都被钳夹住了(在USB OTG中,它们都被钳夹到“0”)。
  • 然后它断言gate_helk和h2pd_stop_pclk来停止AHB和PHY时钟。
  • 然后,它等待stop_pclk_ack(来自PHY时钟域),以表明时钟已在PHY域中停止。由于存在同步器,因此,PHY时钟总是在AHB时钟后关闭。
  • 然后它断言retain_n(一个异步信号,因此不需要握手)。这将导致保留寄存器保存其内容。
  • 然后它断言reset_n(一个异步信号,因此不需要握手)。
  • 然后它断言pwr_dwn_req_n,导致USB OTG中的电源门控部分被断电。
  • 然后,它等待pwr_dwn_ack_n被断言,这表明USB OTG已完全断电。一旦收到了确认,电源控制程序就会进入空闲状态,等待被告知唤醒USB OTG。

当电源控制器看到enable_power_gating已被清除(指示CPU想要启动USB OTG)或在USB总线上检测到活动时,则电源控制器状态机启动唤醒顺序:

  • 它取消pwr_dwn_req_n,导致USB OTG的电源门控部分通电。
  • 然后,它将等待pwr_dwn_ack_n,以表明USB OTG已完全通电。
  • 然后它取消断言reset_n,因此协议引擎中的所有触发器都恢复到复位状态。
  • 然后它取消断言retain_n,这样控制和状态注册器块中的所有保留触发器都被恢复。
  • 然后它取消断言gate_hclk和h2pd_stop_pclk来启动两个时钟。
  • 然后,它等待stop_pclk_ack来指示时钟正在运行。
  • 然后取消断言bius_pwr_clamp和h2pl_pwr_clamp,以释放USB OTG电源门控部分输出上的钳夹信号。
  • 然后它等待pwr_clamp_ack。这告诉控制器隔离单元已释放。

一旦接收到确认信号,功率控制器进入空闲状态,USB OTG恢复正常的操作。

8.3 可移植功率控制器设计问题

除了实现上述功能,IP的功率控制器必须设计为可移植式,也就是说可以在多个应用以及不同的库中使用。此处主要的两个挑战为:

  • 对于不同的库可能需要不同的控制信号;
  • 满足与系统级电源控制器接口的(潜在)需求;

基本的控制功能是打开和关闭电源,打开和关闭时钟和复位,打开和关闭隔离单元,并对保留触发器发出保存/恢复命令。这些是几乎所有的电源门控设计。但特定的库可能需要不同的细节:

  • 不同的库的信号极性可能会有所不同。
  • 可能不是每个单独的控制都需要请求/确认。
  • 保存和恢复既可以作为单个控件(在上面的示例中retain_n)实现,也可以作为两个单独的信号实现。

建议:

  • 参数化所有控制信号上的信号极性,以便它可以由用户进行配置
  • 在所有控件上实现请求/确认握手,但提供一个机制,以便用户可以配置IP连接确认为那些不需要握手的应用程序请求。
  • 参数化保存和控制功能,以便用户可以将其配置为单个控件或双(保存和恢复)控件。

随着电源门控变得越来越普遍,芯片很可能会使用一个中心功率控制器来协调设计中各种电源门控块的活动。特别是,如果多个块想要同时通电,它可能需要通过块排序,一次通电一个,以限制过高电压峰值的噪声。可能需要一个中央代理来仲裁所有想要通电或断电的区块。

在前一节中描述的那种功率控制器应该能够适应这样的系统架构。用户可以将断电请求路由到中央控制器,中央控制器可以在其决定服务该请求时发出确认。对IP设计的唯一要求是,断电请求和确认信号必须在IP的顶级端口可用

8.4 时钟和复位

由于各种原因,需要控制IP的时钟和复位:

  • 对于扫描,正常的时钟可能必须与扫描时钟通过一个选择器
  • 对于扫描,可能需要从芯片级别上的扫描控制引脚来控制复位
  • 对于电源门控,电源门控区域可能需要打开和关断时钟,而对于非电源门控区域则始终保持开启
  • 对于电源门控,复位可能需要有选择地断言到部分电源门控区域,而不在设计的其他区域断言

由于这些原因,在IP中有一个专用的时钟和重置模块越来越重要,它只处理时钟和复位,并提供了满足上述需求的灵活性。

为了为扫描测试提供最佳的可控性,我们建议功率控制器本身可以从IP外部进行控制。也就是说,一个中央的、芯片级的扫描控制器需要能够强制打开或关闭IP电源,以及强制时钟,复位、强制断电区域的保存和恢复。

在SALT芯片,我们也决定做所有扫描时钟混合到USB OTG IP外部。PHY时钟由USB PHY生成,并在向数字核心提供PHY时钟之前通过一个选择器。此mux在PHY时钟(在正常模式下使用)和扫描时钟(在扫描测试时使用)之间切换。

8.5 验证

任何可配置IP的验证都是一个很大的挑战。这个挑战由增加电源门控带来的。

在开发USB OTG的电源门控版本时,我们最初在没有电源选通电路的情况下进行了完整的RTL功能测试。当USB OTG通过所有诊断后,我们添加了电源选通功能并重新运行诊断程序。当这些都通过后,我们运行了一组诊断程序来测试掉电功能本身。

由于电源门控功能完全独立于其他USB功能,这种方法似乎是最小化验证工作并仍然提供强验证的合适方法。

在RTL级别上,我们通过在断电期间强制寄存器输出到“X”来仿真电源开关结构。这种方法允许我们在RTL级别上完全验证核心。但是由于电源门控与开关结构的物理实现紧密相连,我们也对断电功能进行了广泛的门级仿真。这允许我们为开关结构使用详细的模型,包括完全通电或断电所需的时间。

8.6 包含功率设计意图的可重用IP封装

SALT芯片是在UPF可用之前开发的,UPF可以提供指定IP块功率策略的方便方法。随着UPF的引入,在IP的最终打包中包含电源策略变得更加简单。

任何IP都需要以允许用户配置其应用程序的方式打包IP。这通常是使用配置工具来完成的。对于软IP,最终的包装包括:

  • 配置RTL(对于USB,这包括选择端点的数量和配置每个端点)
  • 能够生成一个测试台来验证已配置的核心,包括合成前和合成后
  • 针对已配置的核心的综合脚本,包括支持时钟门控和多电压阈值

对于支持电源门控的核心,我们需要添加:

  • 配置电源控制器的能力
  • 生成用于验证综合前和综合后上电运行的测试台的能力
  • 配置功率设计意图的能力,包括目标保留寄存器
  • 支持权力意图的合成脚本
  • 可配置的UPF代码,以支持可配置的电源策略

8.7 USB OTG核的UPF

图8-3显示了USB数字核心OTG更详细的框图。在最初的SALT芯片中,我们必须在RTL中直接添加保留、隔离和电源开关。通过引入UPF,我们可以使用UPF tel命令来描述这种功能兼容性。

请注意,电源控制块仍然必须在RTL中设计,并在设计中实例化。

添加电源门控、保留和隔离的UPF代码如下所示。块名称otg(核心的顶层)、biu、mac等是RTL中这些模块实例的名称。

为了使UPF代码可移植,我们使用一个变量($otg)来指示从设计的顶部到RTL中核心的顶层的实际路径。我们还使用变量描述了VDD和VSS电源网的起源。这些很可能是在芯片的顶层。

《低功耗方法学》翻译——第八章:低功耗IP设计_第4张图片

set_scope $otg
create_power_domain otg_power_domain
    -elements {aiu pfc mac sync csr}

create_supply_net switched_VDD
    -domain otg_power_domain

set_domain_supply_net otg_power_domain
    -primary_power_net switched_VDD
    -primary_ground_net /$top_VSS

create_power_switch power_switch
    -domain otg_power_domain
    -input_supply_port {sw_input_port /$top_VDD}
    -output_supply_port {sw_output_port switched_VDD}
    -control_port {sw_control_port biu/pwr_dwn_req_n}
    -ack_port {pwr_ack_port biu/pwr_dwn_ack_n}
    -on_state {pwr_on_state sw_input_port
              {sw_control_port ==1}}
    -off_state {pwr_off_state {sw_control_port ==0}}

set_isolation otg_isolation -domain otg_power_domain
    -isolation_power_net $top_VDD
    -clamp_value 0

set_isolation_control otg_isolation
    -domain otg_power_domain
    -isolation_signal biu/bius_pwr_clamp

set_retention otg_retention
    -domain otg_power_domain
    -retention_power_net $top_VDD

set_retention_control otg_retention
    -domain otg_power_domain
    -save_signal {biu/retain_n negedge}
    -restore_signal {biu/retain_n posedge}

8.8 USB OTG功率门控控制器的状态机

下面的代码作为一个简单的电源控制器状态机的示例。它被编码为分层状态机——我们发现这种格式非常有用,尤其是在更复杂的状态机中。

//====================================================
//
//
// (C) Copyright 2004-2005, Synopsys, Inc.
// ALL RIGHTS RESERVED
//
//
// Filename : optc_sm.v
// Author : Mike Keating
// Date : November 28, 2005
// Version : 1.0
// Description : This Module implements state machine
// part of power down control logic for
// OTG
//
//====================================================
//====================================================

reg [2:0] main_state;
parameter TOP_IDLE= 3'd0;
parameter SLEEP= 3'd1;
parameter WAKEUP= 3'd2;
parameter FLUSH_FIFO=3'd3;
reg [2:0] SLEEP_state;
parameter SLEEP_IDLE=3'd0;
parameter CLAMP= 3'd1;
parameter SAVE = 3'd2;
parameter PWR_DOWN= 3'd3;
parameter CLOCKS_OFF=3'd4;
parameter SLEEP_DONE=3'd5;
parameter RESET_PDN =3'd6;
reg [2:0] WAKEUP_state;
parameter WAKEUP_IDLE=3'd0;
parameter WAKEUP_DONE=3'd1;
parameter PWR_UP= 3'd2;
parameter CLAMPS_OFF=3'd3;
parameter RESTORE= 3'd4;
parameter CLOCKS_ON=3'd5;
parameter RESET_OFF=3'd6;

always @ ( posedge hclk or negedge hreset_n ) begin
    if (!hreset_n) begin
        bius_pwr_reset_n <= 1'b1;
        pwr_clamp_n_tmp <= 1'b1;
        bius_pwr_clamp_n_tmp <= 1'b1;
        h2pl_pwr_clamp_n_tmp <= 1'b1;
        bius_pwr_clamp_tmp <= 1'b0;
        bius_gate_hclk_tmp <= 1'b0;
        h2pd_stop_pclk <= 1'b0;
        retain_n <= 1'b1;
        pwr_dwn_req_n <= 1'b1;
        main_state <= TOP_IDLE;
        SLEEP_state <= SLEEP_IDLE;
        WAKEUP_state <= WAKEUP_IDLE;
        end else begin
            case (main_state)
                TOP_IDLE: begin
                    if (suspend_detected_interrupt &&
                                enable_power_gating)
                    begin
                        main_state <= FLUSH_FIFO;
                    end
                end
                SLEEP: begin
                    sleep;
                    if (SLEEP_state == SLEEP_DONE &&
                        (sp2ht_wkup_res_det_biu ||
                        !enable_power_gating ))
                    begin
                        main_state <= WAKEUP;
                        SLEEP_state <= SLEEP_IDLE;
                    end
                end
                WAKEUP: begin
                    wakeup;
                    if (WAKEUP_state == WAKEUP_DONE)
                    begin
                        main_state <= TOP_IDLE;
                        WAKEUP_state <= WAKEUP_IDLE;
                    end
                end
                FLUSH_FIFO: begin
                    if (fifo_flushed)
                    begin
                        main_state <= SLEEP;
                    end
                end
        endcase
    end
end

//-------------------------------------------------
// sleep_task
//-------------------------------------------------
task sleep;
    case (SLEEP_state)
        SLEEP_IDLE: SLEEP_state <= CLAMP;
        CLAMP: begin
            pwr_clamp_n_tmp <= 1'b0;
            bius_pwr_clamp_n_tmp <= 1'b0;
            h2pl_pwr_clamp_n_tmp <= 1'b0;
            bius_pwr_clamp_tmp <= 1'b1;
            if (pwr_clamp_ack_sync==1)
                SLEEP_state <= CLOCKS_OFF;
        end
        CLOCKS_OFF: begin
            bius_gate_hclk_tmp <= 1'b1;
            h2pd_stop_pclk <= 1'b1;
            if (stop_pclk_ack_sync==1)
            begin
                SLEEP_state <= SAVE;
                retain_n <= 1'b0;
            end
        end
        SAVE: SLEEP_state <= RESET_PDN;
        RESET_PDN: begin
            bius_pwr_reset_n <= 1'b0;
            SLEEP_state <= PWR_DOWN;
        end
        PWR_DOWN: begin
            pwr_dwn_req_n <= 1'b0;
            if (!pwr_dwn_ack_sync_n)
                SLEEP_state <= SLEEP_DONE;
        end
        SLEEP_DONE:
    endcase
endtask

//-------------------------------------------
// wakeup_task
//-------------------------------------------
task wakeup;
    case (WAKEUP_state)
        WAKEUP_IDLE: WAKEUP_state <= PWR_UP;
        PWR_UP: begin
            pwr_dwn_req_n <= 1'b1;
            if (pwr_dwn_ack_sync_n==1'b1)
                WAKEUP_state <= RESET_OFF;
            end
        RESET_OFF: begin
            bius_pwr_reset_n <= 1'b1;
            WAKEUP_state <= RESTORE;
        end
        RESTORE: begin
            retain_n <= 1'b1;
            WAKEUP_state <= CLOCKS_ON;
        end
        CLOCKS_ON: begin
            bius_gate_hclk_tmp <= 1'b0;
            h2pd_stop_pclk <= 1'b0;
            if (stop_pclk_ack_sync==0)
                WAKEUP_state <= CLAMPS_OFF;
        end
        CLAMPS_OFF: begin
            pwr_clamp_n_tmp <= 1'b1;
            bius_pwr_clamp_n_tmp <= 1'b1;
            h2pl_pwr_clamp_n_tmp <= 1'b1;
            bius_pwr_clamp_tmp <= 1'b0;
            if (pwr_clamp_ack_sync==0)
                WAKEUP_state <= WAKEUP_DONE;
        end
        WAKEUP_DONE:
    endcase
endtask

图8-4显示了顶层状态机。符号是UML2.0中使用的状态图符号。我们发现这种格式比传统的气泡图更有用。

《低功耗方法学》翻译——第八章:低功耗IP设计_第5张图片

下图所示为SLEEP和WAKEUP状态的细节:

《低功耗方法学》翻译——第八章:低功耗IP设计_第6张图片

 



第八章结束~ 

 

 

 

 

你可能感兴趣的:(低功耗方法学(Soc),芯片设计,低功耗,SOC,IC设计,Soc)