Skip to main content
欢迎来到PAWPAW技术文档网站了解更多信息

使用 LPDRR

本节介绍如何在采用 xcore.ai(XU316)设备的目标系统中,使用 LPDDR1 存储设备。

LPDDR1 存储器可以与部分 xcore.ai 设备相连。这些存储器的容量可能为 256Mbit、512Mbit 或 1024Mbit。详细的连接信息和电气规格,请参见相关设备的数据手册。

在 xcore.ai 设备的 Tile 0 或 Tile 1 上运行的应用软件能够访问 LPDDR 存储内容。但是,两个 Tile 不能同时从同一个应用中访问 LPDDR 存储。

开发者需提供带有注释的 C 语言源代码,以标明哪些应用组件需要放置于 LPDDR 中。引导程序将负责完成硬件设置,以确保应用能够正确执行、读取及写入这部分存储空间。

注意:在配置有两个 xcore.ai 设备的系统中,LPDDR 存储器和用于系统启动的闪存设备必须连接到同一 xcore.ai 设备上。

应用程序中访问 LPDDR

需要驻留在 LPDDR 中的可执行代码或数据实体,必须添加如下注释:

__attribute__ ((section(".ExtMem<qualifier>")))

<qualifier> 应为字符串 .bss,用于指示数据在引导时需初始化为 0(传统称为 BSS),且不占用闪存镜像空间。

对于需要初始化的数据或代码,<qualifier> 可为任意其他字符串。

以下实例展示了如何使用 __attribute__ 注释:

// 放置于 BSS 区 - 引导时初始化为零 - 不占用闪存镜像空间
__attribute__ ((section(".ExtMem.bss")))
char working_area[10 * 1024 * 1024];

// 放置于 BSS 区 - 引导时初始化为零 - 不占用闪存镜像空间
__attribute__ ((section(".ExtMem.bss")))
extern char working_area[10 * 1024 * 1024];

// 可执行代码的注释 - 占用闪存镜像空间
__attribute__ ((section(".ExtMem.code")))
void procedure(int * p) {
...
}

// 已初始化数据的注释 - 占用闪存镜像空间
__attribute__ ((section(".ExtMem.data")))
unsigned ddr_data_word = 0xdeadbeef;

// 已初始化数据的注释 - 占用闪存镜像空间
__attribute__ ((section(".ExtMem_data")))
unsigned ddr_stuff[32768] = { 0x12345678, 0x234567ab, 0x4567abcd };

注释需放置在实体的定义及其声明之前。声明时使用 extern,以避免相同对象的重复定义。

为外部内存(LPDDR)和软件定义内存编译

内存模型所述,通过名称直接访问外部或软件定义内存中的数据对象需要使用 largehybrid 内存模型。但是,通过指针访问可以在任何内存模型下完成。XMOS 推荐以下方案之一:

方案 1:通用情况:所有对象均可直接访问

将整个应用编译为相同的模型,largehybrid

仅当任一 Tile 的连续数据区域(通常位于内部 RAM 中)超过 256KB,或代码中的跳转超出 small 模型的最大范围时,才需要 large 模型。

方案 2:通过指针访问 LPDDR 或软件定义内存中的对象

当满足以下所有条件时,可用于访问外部/软件内存:

  1. 仅将数据而非代码(函数)放置于外部内存中。
  2. 外部/软件内存数据位于少数几个大型对象中,可在专用源文件中定义。若外部/软件内存对象分散在应用程序中,则此方案不适用。
  3. 每个 Tile 的内部 RAM 数据适合于 256KB 内。

优势:相比使用 large 模型,生成的代码更小、访问内部 RAM 数据更快。

在特定源文件中定义外部/软件内存对象,并提供访问函数,返回数据指针。定义外部/软件内存数据和访问函数的源文件需在 hybrid 模型下编译。(虽然 large 模型也可行,但如果满足上述条件,则没有必要。)所有其他源文件可在默认的 (small) 模型下编译。

此方案旨在特定情况下使用,即在除内部 RAM 以外的内存中存储少量非常大型对象。为许多较小对象编写地址获取函数并非推荐的做法。

示例:

external.c

// xcc -mcmodel=hybrid external.c ...

__attribute__((section(“.ExtMem.data”))) int readings[MAX];

int * get_readings(void) { return readings; }

external.h

int * get_readings(void);

main.c

// xcc -mcmodel=small main.c ...

#include “external.h”

...

int * r = get_readings();

r[3] = k;

int a = r[7];

硬件配置

xcore.ai 设备配备了 LPDDR 控制器,需由引导程序进行配置。配置所需的参数将在目标 XN 文件中给出。需要以下信息:

  1. LPDDR 接口的时钟频率
  2. LPDDR 设备的大小(256Mbit、512Mbit 或 1024Mbit)
  3. xcore.ai 输出 pad 驱动强度(到 LPDDR 设备的输入)
  4. LPDDR 输出驱动强度(到 xcore.ai 设备的输入)

LPDDR 时钟频率规定

LPDDR 时钟可以由系统 PLL 或辅助 PLL 提供。

使用主 PLL(系统)

LPDDR 时钟可以设定为从系统 PLL 派生的频率。系统 PLL 的工作频率是 100MHz 的倍数,这个频率通过一个常数除以得到 LPDDR 时钟。LPDDR 时钟通过系统 PLL 经过一个固定的二分频和一个可编程分频器来驱动。LPDDR 时钟频率为:

其中 div 为 0x2 到 0x20000 范围内的偶数。使用系统 PLL 时,div 的值根据 XN 文件中指定的 LPDDR 频率计算得出。以下目标 XN 文件片段展示了提供 100MHz LPDDR 时钟所需的参数:

<Extmem SizeMbit="1024" Frequency="100MHz">

与驱动强度规定一同展示如下:

<Packages>
<Package id="0" Type="XS3-UnA-1024-FB265">
<Nodes>
<Node Id="0" InPackageId="0" Type="XS3-L16A-1024" Oscillator="24MHz" SystemFrequency="600MHz" ReferenceFrequency="100MHz">

<Boot>
<Source Location="bootFlash"/>
</Boot>

<Extmem SizeMbit="1024" Frequency="100MHz">

<!-- Padctrl 和 Lpddr XML 元素的属性根据数据手册中同名的 'Node Configuration' 寄存器进行设置 -->
<!--
Padctrl 属性应用于下列信号集合中的每个命名信号:
[6] = 施密特触发使能,[5] = 斜率控制,[4:3] = 驱动强度,[2:1] = 拉选项,[0] = 读使能

因此:
0x30: 8mA 驱动,快速斜率输出
0x31: 8mA 驱动,快速斜率双向
-->
<Padctrl clk="0x30" cke="0x30" cs_n="0x30" we_n="0x30" cas_n="0x30" ras_n="0x30" addr="0x30" ba="0x30" dq="0x31" dqs="0x31" dm="0x30"/>

<!--
LPDDR emr_opcode 属性:
emr_opcode[7:5] = LPDDR 对 xcore.ai 的驱动强度

0x20: 半驱动强度
-->
<Lpddr emr_opcode="0x20"/>
</Extmem>

驱动强度规定应基于电路板设计参数提供。上述值适用于 XMOS XK-EVK-XU316 开发板,可作为新电路板设计的参考。

利用次级 PLL

次级 PLL 可作为 LPDDR 时钟的来源,这样做能够突破仅使用主系统 PLL 时所遇到的 LPDDR 频率的限制。实现此目的,需要配置两组参数:

  1. 为了获得特定的 PLL 输出频率而必须设置的 PLL 配置值。

  2. 一个用于将 PLL 输出频率分频以得到 LPDDR 时钟频率的分频值(该值必须是在 0x2 至 0x20000 范围内的偶数整数)。

PLL 输出频率的计算公式为:

f_out = (振荡器频率 x 次级 PLL 反馈分频值/2) / (次级 PLL 输入分频值 x 次级 PLL 输出分频值)

下面的 XN 配置段落示例说明了如何设置这些参数,以实现 322MHz 的 PLL 输出频率和 166MHz 的 LPDDR 时钟频率:

<Node Id="0" InPackageId="0" Type="XS3-L16A-1024" Oscillator="24MHz"
SystemFrequency="600MHz" ReferenceFrequency="100MHz"
SecondaryPllInputDiv="1" SecondaryPllOutputDiv="3" SecondaryPllFeedbackDiv="83">

<Extmem SizeMbit="1024" SourcePll="SecondaryPll" Divider="2">

下面这段代码展示了上述配置段落的应用环境(注意,为了简洁,移除了使用主 PLL 时提供的详细注释):

<Node Id="0" InPackageId="0" Type="XS3-L16A-1024" Oscillator="24MHz"
SystemFrequency="600MHz" ReferenceFrequency="100MHz"
SecondaryPllInputDiv="1" SecondaryPllOutputDiv="3" SecondaryPllFeedbackDiv="83">

<Extmem SizeMbit="1024" SourcePll="SecondaryPll" Divider="2">

<!-- Padctrl 和 Lpddr XML 元素的属性与数据表中相同名字的 'Node Configuration' 寄存器保持一致 -->
<Padctrl clk="0x30" cke="0x30" cs_n="0x30" we_n="0x30" cas_n="0x30" ras_n="0x30" addr="0x30" ba="0x30" dq="0x31" dqs="0x31" dm="0x30"/>

<Lpddr emr_opcode="0x20"/>
</Extmem>

一级缓存

一级缓存位于 xCORE 瓦片和 LPDDR 内存之间,是一个集成了指令和数据的全相联缓存,支持写回策略。此缓存共有 8 行,每行容量为 32 字节,采用伪最近最少使用(pseudo-LRU)作为替换策略。

提供了 xCORE 指令来预取、使缓存失效和刷新这个缓存。

建议不要让超过两个逻辑核心同时访问 LPDDR,否则会出现“缓存抖动”现象,即某个逻辑核心需要的数据被另一个逻辑核心反复置换出缓存,导致数据必须被重新加载。