跳转至

AUTOSAR 内存映射设计 (MemMap)

本文档详述 AUTOSAR MemMap.h 的工作原理,以及如何将 Safety-Critical (ASIL D) 代码段放置在特定 Flash Sector 中实现空间隔离。

MemMap 概述

设计目的

目的 说明
内存分区 将代码/数据放置到特定内存区域
安全隔离 不同 ASIL 等级的代码物理隔离
性能优化 关键代码放入快速内存
编译器抽象 统一不同编译器的内存属性语法

MemMap 在 AUTOSAR 中的位置

flowchart TB subgraph SWC_Layer["SWC 层"] SWC1["SWC_Safety<br/>(ASIL D)"] SWC2["SWC_Diag<br/>(QM)"] end subgraph RTE["RTE 层"] RTE_IF["RTE 接口"] end subgraph BSW["BSW 层"] COM["Com"] DCM["Dcm"] OS["Os"] end subgraph Platform["平台层"] MEMMAP["MemMap.h<br/>(内存映射抽象)"] COMPILER["Compiler.h<br/>(编译器抽象)"] end subgraph Linker["链接器"] LD["Linker Script<br/>(.ld / .lcf)"] end subgraph Memory["物理内存"] FLASH["Flash"] RAM["RAM"] end SWC1 & SWC2 --> RTE RTE --> BSW BSW --> Platform Platform --> Linker Linker --> Memory style MEMMAP fill:#e3f2fd,stroke:#1976d2,stroke-width:2px style LD fill:#fff3e0,stroke:#f57c00,stroke-width:2px

MemMap.h 工作原理

宏定义机制

flowchart LR subgraph Source["源代码"] S1["#define XXX_START_SEC_CODE"] S2["#include <MemMap.h>"] S3["... 函数代码 ..."] S4["#define XXX_STOP_SEC_CODE"] S5["#include <MemMap.h>"] end subgraph MemMap["MemMap.h 处理"] M1["检测 START/STOP 宏"] M2["展开为编译器特定 pragma"] M3["清除宏定义"] end subgraph Output["编译器输出"] O1["#pragma section .text_ASIL_D"] O2["函数放入指定段"] end Source --> MemMap --> Output

MemMap.h 实现示例

/* ============================================================
 * File: MemMap.h
 * Description: AUTOSAR Memory Mapping Abstraction
 * ============================================================ */

/* 防止重复包含检测 - MemMap 需要多次包含 */
/* #ifndef MEMMAP_H 不能使用 */

/*---------------------------------------------------------------------------
 * ECU_SAFETY (ASIL D) 模块内存段
 *---------------------------------------------------------------------------*/

/* ===== CODE SECTIONS ===== */

#if defined(ECU_SAFETY_START_SEC_CODE)
    #undef ECU_SAFETY_START_SEC_CODE
    #undef MEMMAP_ERROR

    /* GCC 编译器 */
    #if defined(__GNUC__)
        #pragma GCC push_options
        #pragma GCC optimize("O2")
        __attribute__((section(".text_ASIL_D")))
    /* Green Hills 编译器 */
    #elif defined(__ghs__)
        #pragma ghs section text=".text_ASIL_D"
    /* Tasking 编译器 */
    #elif defined(__TASKING__)
        #pragma section code ".text_ASIL_D"
    /* ARM Compiler */
    #elif defined(__ARMCC_VERSION)
        #pragma arm section code=".text_ASIL_D"
    /* IAR 编译器 */
    #elif defined(__ICCARM__)
        #pragma location=".text_ASIL_D"
    #endif

#elif defined(ECU_SAFETY_STOP_SEC_CODE)
    #undef ECU_SAFETY_STOP_SEC_CODE
    #undef MEMMAP_ERROR

    #if defined(__GNUC__)
        #pragma GCC pop_options
    #elif defined(__ghs__)
        #pragma ghs section text=default
    #elif defined(__TASKING__)
        #pragma section code restore
    #elif defined(__ARMCC_VERSION)
        #pragma arm section code
    #elif defined(__ICCARM__)
        /* 自动结束 */
    #endif
#endif

/* ===== DATA SECTIONS ===== */

#if defined(ECU_SAFETY_START_SEC_VAR_INIT_32)
    #undef ECU_SAFETY_START_SEC_VAR_INIT_32
    #undef MEMMAP_ERROR

    #if defined(__GNUC__)
        __attribute__((section(".data_ASIL_D")))
    #elif defined(__ghs__)
        #pragma ghs section data=".data_ASIL_D"
    #endif

#elif defined(ECU_SAFETY_STOP_SEC_VAR_INIT_32)
    #undef ECU_SAFETY_STOP_SEC_VAR_INIT_32
    #undef MEMMAP_ERROR

    #if defined(__GNUC__)
        /* 自动结束 */
    #elif defined(__ghs__)
        #pragma ghs section data=default
    #endif
#endif

/* ===== CONST SECTIONS ===== */

#if defined(ECU_SAFETY_START_SEC_CONST_32)
    #undef ECU_SAFETY_START_SEC_CONST_32
    #undef MEMMAP_ERROR

    #if defined(__GNUC__)
        __attribute__((section(".rodata_ASIL_D")))
    #endif

#elif defined(ECU_SAFETY_STOP_SEC_CONST_32)
    #undef ECU_SAFETY_STOP_SEC_CONST_32
    #undef MEMMAP_ERROR
#endif

/*---------------------------------------------------------------------------
 * ECU_DIAG (QM) 模块内存段
 *---------------------------------------------------------------------------*/

#if defined(ECU_DIAG_START_SEC_CODE)
    #undef ECU_DIAG_START_SEC_CODE
    #undef MEMMAP_ERROR

    #if defined(__GNUC__)
        __attribute__((section(".text_QM")))
    #endif

#elif defined(ECU_DIAG_STOP_SEC_CODE)
    #undef ECU_DIAG_STOP_SEC_CODE
    #undef MEMMAP_ERROR
#endif

/*---------------------------------------------------------------------------
 * 错误检测
 *---------------------------------------------------------------------------*/

#if defined(MEMMAP_ERROR)
    #error "MemMap.h: Unknown memory section!"
#endif

源代码中的使用

SWC 代码示例

/* ============================================================
 * File: Ecu_Safety.c
 * Description: ASIL D Safety-Critical Component
 * ============================================================ */

#include "Ecu_Safety.h"

/*---------------------------------------------------------------------------
 * ASIL D Code Section
 *---------------------------------------------------------------------------*/
#define ECU_SAFETY_START_SEC_CODE
#include "MemMap.h"

/**
 * @brief Safety-critical monitoring function
 * @note This function is placed in ASIL D Flash sector
 */
void Ecu_Safety_Monitor(void) {
    /* ASIL D 安全监控逻辑 */
    if (checkSafetyConditions() == FALSE) {
        triggerSafeState();
    }
}

/**
 * @brief Watchdog refresh function
 */
void Ecu_Safety_RefreshWatchdog(void) {
    Wdg_Trigger();
}

#define ECU_SAFETY_STOP_SEC_CODE
#include "MemMap.h"

/*---------------------------------------------------------------------------
 * ASIL D Initialized Data Section
 *---------------------------------------------------------------------------*/
#define ECU_SAFETY_START_SEC_VAR_INIT_32
#include "MemMap.h"

static uint32 SafetyCounter = 0U;
static uint32 LastCheckTime = 0U;

#define ECU_SAFETY_STOP_SEC_VAR_INIT_32
#include "MemMap.h"

/*---------------------------------------------------------------------------
 * ASIL D Constant Data Section
 *---------------------------------------------------------------------------*/
#define ECU_SAFETY_START_SEC_CONST_32
#include "MemMap.h"

static const uint32 SafetyCheckPeriod = 10U;  /* 10 ms */
static const uint32 MaxFailureCount = 3U;

#define ECU_SAFETY_STOP_SEC_CONST_32
#include "MemMap.h"

链接器脚本设计

Flash 内存布局

flowchart TB subgraph Flash["Flash Memory (2 MB)"] direction TB subgraph Sector0["Sector 0: 0x0000_0000 - 0x0000_FFFF (64KB)"] BOOT["Bootloader<br/>(不可擦除)"] end subgraph Sector1["Sector 1-2: 0x0001_0000 - 0x0002_FFFF (128KB)"] ASILD_CODE["ASIL D Code<br/>(.text_ASIL_D)"] end subgraph Sector3["Sector 3: 0x0003_0000 - 0x0003_FFFF (64KB)"] ASILD_CONST["ASIL D Const<br/>(.rodata_ASIL_D)"] end subgraph Sector4["Sector 4-5: 0x0004_0000 - 0x0005_FFFF (128KB)"] ASILB_CODE["ASIL B Code<br/>(.text_ASIL_B)"] end subgraph Sector6["Sector 6-10: 0x0006_0000 - 0x000A_FFFF (320KB)"] QM_CODE["QM Code<br/>(.text_QM)"] end subgraph Sector11["Sector 11-15: 0x000B_0000 - 0x000F_FFFF (320KB)"] QM_CONST["QM Const + Calibration<br/>(.rodata_QM)"] end end style Sector1 fill:#ffcdd2,stroke:#c62828,stroke-width:2px style Sector3 fill:#ffcdd2,stroke:#c62828 style Sector4 fill:#fff9c4,stroke:#f9a825 style Sector6 fill:#c8e6c9,stroke:#388e3c style Sector11 fill:#c8e6c9,stroke:#388e3c

RAM 内存布局

flowchart TB subgraph RAM["RAM Memory (256 KB)"] direction TB subgraph RAM_ASILD["ASIL D RAM: 0x2000_0000 - 0x2000_FFFF (64KB)"] ASILD_DATA[".data_ASIL_D"] ASILD_BSS[".bss_ASIL_D"] ASILD_STACK["ASIL D Stack"] end subgraph RAM_ASILB["ASIL B RAM: 0x2001_0000 - 0x2001_7FFF (32KB)"] ASILB_DATA[".data_ASIL_B"] ASILB_BSS[".bss_ASIL_B"] end subgraph RAM_QM["QM RAM: 0x2001_8000 - 0x2002_FFFF (96KB)"] QM_DATA[".data_QM"] QM_BSS[".bss_QM"] QM_STACK["QM Stack"] end subgraph RAM_SHARED["Shared RAM: 0x2003_0000 - 0x2003_3FFF (16KB)"] EXCHANGE["数据交换区<br/>(MPU 受控)"] end subgraph RAM_NOINIT["NoInit RAM: 0x2003_4000 - 0x2003_FFFF (48KB)"] NOINIT["复位保持数据<br/>(.noinit)"] end end style RAM_ASILD fill:#ffcdd2,stroke:#c62828,stroke-width:2px style RAM_ASILB fill:#fff9c4,stroke:#f9a825 style RAM_QM fill:#c8e6c9,stroke:#388e3c style RAM_SHARED fill:#e0e0e0,stroke:#757575

GNU LD 链接器脚本

/* ============================================================
 * File: linker_script.ld
 * Description: Memory Layout for ASIL/QM Partitioning
 * Target: ARM Cortex-R5 / Cortex-M7
 * ============================================================ */

/* 内存区域定义 */
MEMORY
{
    /* Flash 区域 */
    FLASH_BOOT      (rx)  : ORIGIN = 0x00000000, LENGTH = 64K
    FLASH_ASIL_D    (rx)  : ORIGIN = 0x00010000, LENGTH = 128K
    FLASH_ASIL_D_RO (r)   : ORIGIN = 0x00030000, LENGTH = 64K
    FLASH_ASIL_B    (rx)  : ORIGIN = 0x00040000, LENGTH = 128K
    FLASH_QM        (rx)  : ORIGIN = 0x00060000, LENGTH = 320K
    FLASH_QM_RO     (r)   : ORIGIN = 0x000B0000, LENGTH = 320K

    /* RAM 区域 */
    RAM_ASIL_D      (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
    RAM_ASIL_B      (rwx) : ORIGIN = 0x20010000, LENGTH = 32K
    RAM_QM          (rwx) : ORIGIN = 0x20018000, LENGTH = 96K
    RAM_SHARED      (rw)  : ORIGIN = 0x20030000, LENGTH = 16K
    RAM_NOINIT      (rw)  : ORIGIN = 0x20034000, LENGTH = 48K
}

/* 段定义 */
SECTIONS
{
    /* ===============================================
     * ASIL D 区域
     * =============================================== */

    .text_ASIL_D :
    {
        . = ALIGN(4);
        __text_asild_start = .;

        /* ASIL D 代码 */
        *(.text_ASIL_D)
        *(.text_ASIL_D.*)

        /* 安全关键函数 */
        *Ecu_Safety*.o(.text .text.*)
        *SafetyMonitor*.o(.text .text.*)
        *Wdg*.o(.text .text.*)

        . = ALIGN(4);
        __text_asild_end = .;
    } > FLASH_ASIL_D

    .rodata_ASIL_D :
    {
        . = ALIGN(4);
        __rodata_asild_start = .;

        *(.rodata_ASIL_D)
        *(.rodata_ASIL_D.*)
        *Ecu_Safety*.o(.rodata .rodata.*)

        . = ALIGN(4);
        __rodata_asild_end = .;
    } > FLASH_ASIL_D_RO

    .data_ASIL_D :
    {
        . = ALIGN(4);
        __data_asild_start = .;

        *(.data_ASIL_D)
        *(.data_ASIL_D.*)
        *Ecu_Safety*.o(.data .data.*)

        . = ALIGN(4);
        __data_asild_end = .;
    } > RAM_ASIL_D AT> FLASH_ASIL_D_RO

    .bss_ASIL_D (NOLOAD) :
    {
        . = ALIGN(4);
        __bss_asild_start = .;

        *(.bss_ASIL_D)
        *(.bss_ASIL_D.*)
        *Ecu_Safety*.o(.bss .bss.* COMMON)

        . = ALIGN(4);
        __bss_asild_end = .;
    } > RAM_ASIL_D

    /* ASIL D 栈 */
    .stack_ASIL_D (NOLOAD) :
    {
        . = ALIGN(8);
        __stack_asild_bottom = .;
        . = . + 4K;  /* 4KB 栈 */
        __stack_asild_top = .;
    } > RAM_ASIL_D

    /* ===============================================
     * ASIL B 区域
     * =============================================== */

    .text_ASIL_B :
    {
        . = ALIGN(4);
        *(.text_ASIL_B)
        *(.text_ASIL_B.*)
        . = ALIGN(4);
    } > FLASH_ASIL_B

    .data_ASIL_B :
    {
        . = ALIGN(4);
        *(.data_ASIL_B)
        *(.data_ASIL_B.*)
        . = ALIGN(4);
    } > RAM_ASIL_B AT> FLASH_ASIL_B

    .bss_ASIL_B (NOLOAD) :
    {
        . = ALIGN(4);
        *(.bss_ASIL_B)
        *(.bss_ASIL_B.*)
        . = ALIGN(4);
    } > RAM_ASIL_B

    /* ===============================================
     * QM 区域
     * =============================================== */

    .text_QM :
    {
        . = ALIGN(4);
        *(.text_QM)
        *(.text_QM.*)

        /* 默认代码放入 QM */
        *(.text)
        *(.text.*)

        . = ALIGN(4);
    } > FLASH_QM

    .rodata_QM :
    {
        . = ALIGN(4);
        *(.rodata_QM)
        *(.rodata_QM.*)
        *(.rodata)
        *(.rodata.*)
        . = ALIGN(4);
    } > FLASH_QM_RO

    .data_QM :
    {
        . = ALIGN(4);
        *(.data_QM)
        *(.data_QM.*)
        *(.data)
        *(.data.*)
        . = ALIGN(4);
    } > RAM_QM AT> FLASH_QM_RO

    .bss_QM (NOLOAD) :
    {
        . = ALIGN(4);
        *(.bss_QM)
        *(.bss_QM.*)
        *(.bss)
        *(.bss.*)
        *(COMMON)
        . = ALIGN(4);
    } > RAM_QM

    /* ===============================================
     * 共享区域
     * =============================================== */

    .shared_exchange (NOLOAD) :
    {
        . = ALIGN(4);
        __shared_start = .;
        *(.shared_exchange)
        . = ALIGN(4);
        __shared_end = .;
    } > RAM_SHARED

    /* ===============================================
     * NoInit 区域 (复位保持)
     * =============================================== */

    .noinit (NOLOAD) :
    {
        . = ALIGN(4);
        *(.noinit)
        *(.noinit.*)
        . = ALIGN(4);
    } > RAM_NOINIT

    /* ===============================================
     * 调试信息
     * =============================================== */

    /* 输出段边界供 MPU 配置使用 */
    __flash_asild_start = ORIGIN(FLASH_ASIL_D);
    __flash_asild_end   = ORIGIN(FLASH_ASIL_D) + LENGTH(FLASH_ASIL_D);
    __ram_asild_start   = ORIGIN(RAM_ASIL_D);
    __ram_asild_end     = ORIGIN(RAM_ASIL_D) + LENGTH(RAM_ASIL_D);
}

/* 断言检查 - 确保段没有溢出 */
ASSERT(__text_asild_end <= ORIGIN(FLASH_ASIL_D) + LENGTH(FLASH_ASIL_D), 
       "ASIL D code overflow!")
ASSERT(__bss_asild_end + 4K <= ORIGIN(RAM_ASIL_D) + LENGTH(RAM_ASIL_D), 
       "ASIL D RAM overflow!")

MPU 配置联动

链接器符号与 MPU 配置

/* ============================================================
 * File: Mpu_Config.c
 * Description: MPU Configuration using Linker Symbols
 * ============================================================ */

#include "Mpu.h"

/* 从链接器脚本导入边界符号 */
extern uint32_t __flash_asild_start;
extern uint32_t __flash_asild_end;
extern uint32_t __ram_asild_start;
extern uint32_t __ram_asild_end;
extern uint32_t __shared_start;
extern uint32_t __shared_end;

/* MPU 配置表 */
static const Mpu_RegionConfigType Mpu_Config[] = {
    /* Region 0: ASIL D Flash - 只读执行 */
    {
        .regionNumber = 0U,
        .baseAddress  = (uint32_t)&__flash_asild_start,
        .size         = MPU_REGION_SIZE_128KB,
        .accessPermission = MPU_AP_RO_RO,     /* 特权只读, 用户只读 */
        .executeNever = FALSE,                 /* 可执行 */
        .shareable    = FALSE,
        .cacheable    = TRUE,
        .bufferable   = FALSE,
        .enable       = TRUE
    },

    /* Region 1: ASIL D RAM - 读写不可执行 */
    {
        .regionNumber = 1U,
        .baseAddress  = (uint32_t)&__ram_asild_start,
        .size         = MPU_REGION_SIZE_64KB,
        .accessPermission = MPU_AP_RW_RW,     /* 特权读写, 用户读写 */
        .executeNever = TRUE,                  /* 不可执行 */
        .shareable    = FALSE,
        .cacheable    = TRUE,
        .bufferable   = TRUE,
        .enable       = TRUE
    },

    /* Region 2: QM Flash - QM 任务可访问 */
    {
        .regionNumber = 2U,
        .baseAddress  = 0x00060000U,
        .size         = MPU_REGION_SIZE_512KB,
        .accessPermission = MPU_AP_RO_RO,
        .executeNever = FALSE,
        .enable       = TRUE
    },

    /* Region 3: QM RAM - QM 任务可访问 */
    {
        .regionNumber = 3U,
        .baseAddress  = 0x20018000U,
        .size         = MPU_REGION_SIZE_128KB,
        .accessPermission = MPU_AP_RW_RW,
        .executeNever = TRUE,
        .enable       = TRUE
    },

    /* Region 4: 共享交换区 - 所有任务可访问 */
    {
        .regionNumber = 4U,
        .baseAddress  = (uint32_t)&__shared_start,
        .size         = MPU_REGION_SIZE_16KB,
        .accessPermission = MPU_AP_RW_RW,
        .executeNever = TRUE,
        .enable       = TRUE
    },

    /* Region 5: 外设区域 */
    {
        .regionNumber = 5U,
        .baseAddress  = 0x40000000U,
        .size         = MPU_REGION_SIZE_512MB,
        .accessPermission = MPU_AP_RW_RW,
        .executeNever = TRUE,
        .shareable    = TRUE,
        .cacheable    = FALSE,
        .bufferable   = TRUE,
        .enable       = TRUE
    }
};

void Mpu_Init(void) {
    uint32_t i;

    /* 禁用 MPU */
    MPU->CTRL = 0U;

    /* 配置各区域 */
    for (i = 0U; i < (sizeof(Mpu_Config) / sizeof(Mpu_Config[0])); i++) {
        Mpu_ConfigureRegion(&Mpu_Config[i]);
    }

    /* 启用 MPU */
    MPU->CTRL = MPU_CTRL_ENABLE_Msk | 
                MPU_CTRL_PRIVDEFENA_Msk;  /* 启用默认背景区域 */

    /* 内存屏障 */
    __DSB();
    __ISB();
}

内存段映射总图

flowchart TB subgraph SourceCode["📝 源代码"] direction LR CODE1["Ecu_Safety.c"] CODE2["Dcm.c"] end subgraph MemMap["🔧 MemMap.h"] direction TB MM1["#define ECU_SAFETY_START_SEC_CODE"] MM2["展开为 section 属性"] end subgraph Compiler["⚙️ 编译器"] direction TB OBJ1["Ecu_Safety.o<br/>含 .text_ASIL_D 段"] OBJ2["Dcm.o<br/>含 .text_QM 段"] end subgraph Linker["🔗 链接器"] direction TB LD1["链接器脚本定义内存区域"] LD2["将段分配到物理地址"] end subgraph Binary["📦 输出文件"] ELF["app.elf"] HEX["app.hex"] MAP["app.map"] end subgraph Memory["💾 物理内存"] direction TB subgraph Flash_Layout["Flash"] F1["0x00010000: .text_ASIL_D"] F2["0x00060000: .text_QM"] end subgraph RAM_Layout["RAM"] R1["0x20000000: .data_ASIL_D"] R2["0x20018000: .data_QM"] end end SourceCode --> MemMap --> Compiler --> Linker --> Binary --> Memory style MemMap fill:#e3f2fd,stroke:#1976d2,stroke-width:2px style Linker fill:#fff3e0,stroke:#f57c00,stroke-width:2px style Memory fill:#e8f5e9,stroke:#388e3c,stroke-width:2px

验证与调试

Map 文件分析

/* 示例 app.map 摘录 */

Memory Configuration

Name             Origin             Length             Attributes
FLASH_ASIL_D     0x00010000         0x00020000         xr
RAM_ASIL_D       0x20000000         0x00010000         xrw
...

Linker script and memory map

.text_ASIL_D    0x00010000     0x00008a40
                0x00010000        __text_asild_start = .
 *(.text_ASIL_D)
 .text_ASIL_D   0x00010000     0x00001234  Ecu_Safety.o
 .text_ASIL_D   0x00011234     0x00000abc  SafetyMonitor.o
 .text_ASIL_D   0x000122f0     0x00000150  Wdg.o
                0x00018a40        __text_asild_end = .

.text_QM        0x00060000     0x00024680
 *(.text_QM)
 *(.text)
 .text          0x00060000     0x00012340  Dcm.o
 .text          0x00072340     0x00008900  Com.o

运行时验证

/* 验证代码段位置 */
void VerifyMemoryLayout(void) {
    uint32_t funcAddr;

    /* 获取函数地址 */
    funcAddr = (uint32_t)&Ecu_Safety_Monitor;

    /* 验证在 ASIL D 区域 */
    if ((funcAddr >= 0x00010000U) && (funcAddr < 0x00030000U)) {
        /* OK: 函数在正确的 Flash 区域 */
    } else {
        /* Error: 内存映射配置错误 */
        ReportError(ERR_MEMMAP_VIOLATION);
    }

    /* 验证变量位置 */
    uint32_t varAddr = (uint32_t)&SafetyCounter;
    if ((varAddr >= 0x20000000U) && (varAddr < 0x20010000U)) {
        /* OK: 变量在 ASIL D RAM */
    } else {
        ReportError(ERR_MEMMAP_VIOLATION);
    }
}

最佳实践

实践项 说明
模块化 MemMap 每个 BSW/SWC 模块有独立的段宏
段命名规范 .text_<MODULE>_<ASIL> 格式
链接器断言 添加 ASSERT 检测溢出
Map 文件审查 构建后检查段分配
MPU 联动 使用链接器符号配置 MPU
对齐要求 满足 MPU 最小区域大小 (32B/256B)

最后更新: 2026-01-25

ML
Verified

Michael Lin

MAGNA Quality Manager
Architect of Compliance-Wächter
AI for ISO 21434 & UN R155

作为 MAGNA 质量经理,我在德国汽车电子行业深耕 10+ 年。每天我都见证着一个新的瓶颈: 网络安全合规 (UN R155 / ISO 21434)

我正在构建 Compliance-Wächter — 一个 AI 驱动的 Copilot, 自动化 TARA 分析,将合规文书工作减少 70%