1. 项目概述:从芯片手册到实战指南
如果你正在开发基于飞思卡尔(现恩智浦)Symphony DSP56720或DSP56721的音频处理系统,那么内存映射和JTAG接口这两个主题,绝对是你绕不开的“硬骨头”。芯片手册里那些密密麻麻的地址表格和信号描述,常常让人看得一头雾水,感觉懂了,又好像没完全懂。我在十多年的嵌入式音频开发中,无数次与这两款经典的多核DSP打交道,深知仅仅“知道”地址分配是远远不够的,关键在于理解其设计逻辑,并能在实际调试和编程中灵活运用。
简单来说,内存映射定义了软件(你的C/汇编代码)如何与硬件(DSP内核、RAM、外设寄存器)对话。它就像一座城市的详细地图,告诉你银行(程序存储器)、仓库(数据存储器)和各个政府部门(外设控制寄存器)分别在哪条街、哪个门牌号。而JTAG接口则是这座城市的“万能钥匙”和“后台监控系统”,允许你在系统不工作甚至“死机”时,深入芯片内部,查看状态、设置断点、单步执行,是开发和调试阶段不可或缺的生命线。
DSP56720/21作为一款双核音频处理器,其内存架构和调试接口的设计比单核芯片更为复杂。核心间的数据如何通过共享内存高效交换?如何为两个核心分别配置最优的程序和数据RAM布局?JTAG如何同时调试两个核心?这些问题都需要我们深入其内存映射和JTAG设计的细节中寻找答案。本文将结合手册内容与我的实际项目经验,为你拆解这两个核心机制,不仅告诉你“是什么”,更重点解释“为什么这么设计”以及“实际中怎么用”,希望能帮你节省大量查阅手册和试错的时间。
2. 内存映射:架构设计与核心逻辑
内存映射绝非简单的地址分配表,其背后是处理器架构师对性能、灵活性和成本权衡的智慧结晶。对于DSP56720/21这样的多核音频DSP,理解其内存映射设计,是进行高效编程和系统优化的第一步。
2.1 哈弗架构与X/Y数据存储器
DSP56720/21的每个DSP56300核心都采用了经典的哈弗架构。这与我们熟悉的冯·诺依曼架构(程序和数据共享同一总线)不同。哈弗架构将程序存储空间和数据存储空间物理上分开,各自拥有独立的地址总线和数据总线。
为什么音频DSP要采用这种设计?答案在于并行性。音频处理算法,如FIR滤波器、FFT等,大量涉及乘加运算(MAC),通常需要同时从内存中读取两个操作数(例如,一个滤波器的系数和一个数据样本)。DSP56300核心拥有两个地址生成单元(AGU)和两条数据总线(XDB和YDB)。通过将数据存储器划分为X数据空间和Y数据空间,核心可以在一个指令周期内,同时从X和Y空间各取一个操作数,并送入数据ALU进行运算,这极大地提升了数据处理吞吐量,是实现实时高清音频处理的关键。
因此,当你查看内存映射图时,会看到三个并行的地址空间:程序(P)空间、X数据空间和Y数据空间。它们都从$000000开始编址,但指向的是不同的物理存储块。这种设计要求开发者在分配数据时要有意识地将经常需要同时访问的数据对,分别放置到X和Y空间,以最大化硬件并行能力。
2.2 内存空间全局布局解析
从宏观上看,整个24位地址空间($000000-$FFFFFF)被划分为几个关键区域,下图展示了其概览:
地址范围 | 所属空间 | 主要功能 ---------------------|-------------------|----------------------------------- $000000 - $02FFFF | P/X/Y空间低地址区 | 核心内部RAM(程序、X数据、Y数据) $030000 - $03FFFF | P/X/Y空间共享区 | 双核共享内存块(8x8K) $040000 - $F7FFFF | P/X/Y空间扩展区 | 外部存储器(通过EMC接口扩展) $F80000 - $FFEFFF | P/X/Y空间 | 内部ROM(引导程序、固定数据) $FFF000 - $FFFFFF | P/X/Y空间高地址区 | 内部I/O寄存器(外设控制)这个布局有几个关键点需要深入理解:
地址重叠与空间独立性:注意,同一地址值(如
$000100)在P、X、Y三个空间中指向的是完全不同的物理位置。编程时,你必须明确指定访问的是哪个空间。在C语言中,这通常通过编译器扩展的关键字(如pm、x、y)或特定的段(section)属性来指定;在汇编中,则通过不同的地址寄存器或指令前缀来区分。共享内存的巧妙设计:地址
$030000到$03FFFF这块64K字(Word)的空间是精髓所在。手册指出,当任一核心访问此区域的共享内存时,无论从P、X还是Y空间进行访问,都会映射到同一块物理RAM上。这意味着,你可以将需要双核交互的数据(如音频缓冲区、状态标志、消息队列)放在这里。核心A从X空间写入,核心B从Y空间读取,访问的是同一物理位置,实现了零拷贝的数据共享。这是多核间高效通信的基础。I/O寄存器的统一映射:所有外设的控制与状态寄存器(如ESAI、S/PDIF、DMA控制器等)都被映射到高地址区域(
$FFF000以上)。同样,无论从P、X还是Y空间访问$FFFxxx这个地址,都会到达同一个外设寄存器。这简化了编程模型,你可以用相同的内存读写指令来操作外设。
实操心得:在项目初期规划内存时,我习惯画一张内存分配图。将X和Y空间分别列出,明确哪些算法变量放在X空间,哪些放在Y空间,并特别标注出共享内存区域用于核心间通信的数据结构。对于外设寄存器,通常利用芯片厂商提供的头文件(
.h)中的宏定义来访问,避免直接使用魔数(Magic Number)地址,提高代码可读性和可维护性。
2.3 内存开关模式:动态RAM配置的艺术
这是DSP56720/21内存映射中最灵活也最容易让人困惑的部分。芯片内部的RAM资源是固定的,但如何在这些RAM块之间划分给程序(P)、X数据和Y数据使用,却是可以动态配置的。这就是内存开关模式。
控制这一机制的,是操作模式寄存器中的三个位:
- MS(主内存开关模式)位:总开关。
MS=0时,使用默认内存映射;MS=1时,启用可配置的内存映射模式。 - MSW0和MSW1位:具体配置位。当
MS=1时,这两位组合起来,定义了四种不同的RAM分配方案。
手册中的图3-2到图3-6以及对应的表格,详细展示了这五种模式(1种默认+4种可配置)。我们以DSP56720为例,解读其变化逻辑:
- 默认模式(MS=0):这是上电复位后的状态。程序RAM固定为8K字,X数据RAM为36K字,Y数据RAM为48K字。这是一种保守的配置,保证了较大的数据存储空间。
- 模式1(MS=1, MSW1=1, MSW0=1):程序RAM增加到16K字,Y数据RAM减少到40K字,X数据RAM保持36K字不变。这意味着,有8K的Y数据RAM被“切换”给了程序空间。
- 模式2(MS=1, MSW1=1, MSW0=0):程序RAM进一步增加到24K字,Y数据RAM减少到32K字。
- 模式3(MS=1, MSW1=0, MSW0=1):程序RAM32K字,Y数据RAM24K字。
- 模式4(MS=1, MSW1=0, MSW0=0):程序RAM达到最大的36K字,Y数据RAM24K字,X数据RAM也减少到32K字。
背后的设计考量是什么?音频应用多样。有些算法复杂度高,代码量大(如多通道、高阶效果的算法),但数据缓冲区需求相对固定;有些算法则相反,代码简单但需要巨大的数据缓冲区(如长延迟线、��样回放)。内存开关模式允许开发者在硬件资源固定的前提下,根据实际应用在代码空间和数据空间之间进行权衡。
如何配置?通常在上电初始化代码(启动文件或main()函数最开始)中,通过写操作模式寄存器(OMR)来设置MS、MSW0、MSW1位。关键点在于:这个配置必须在访问任何受影响的RAM区域之前完成。一旦切换,原来存放在被“切走”的RAM区域的数据将失效,因此通常只在初始化阶段配置一次。
注意事项:内存映射切换后,链接器(Linker)的存储分配文件(.lcf或.scatter文件)必须与之匹配。如果你在模式4(最大程序RAM)下编译链接了程序,却以默认模式启动,程序可能会因为找不到足够的程序空间而跑飞。务必确保软件配置与硬件设置一致。
3. 外设寄存器映射详解与编程访问
理解了全局内存布局,我们深入到最常打交道的部分:外设寄存器。手册中表3-10和表3-11提供了极其详细的列表,但直接看表格容易迷失。我们需要从编程的角度来理解它。
3.1 核心0与核心1的映射关系
DSP56720/21有两个几乎完全相同的DSP56300核心(Core-0和Core-1)。它们有自己独立的外设(Dedicated Peripherals),也有共享的外设(Shared Peripherals)。
- 独立外设:例如,每个核心都有自己的程序中断控制器、DMA控制器、主机接口等。在寄存器映射表里,Core-0的PIC寄存器在
X:$FF_FFFF,而Core-1的对应外设叫PIC_1,其寄存器也有一套独立的地址,通常与Core-0的地址不同。这保证了两个核心可以独立配置和控制自己的专属资源,互不干扰。 - 共享外设:例如GPIO Port H、芯片配置寄存器、内部时钟控制模块等。这些外设的寄存器地址对两个核心是相同的。这意味着两个核心都能访问并修改它们。这就引入了共享资源访问的同步问题,必须小心处理,通常需要通过软件锁(如使用ICC通信)或硬件信号量来避免冲突。
3.2 关键外设模块地址速查与功能解读
面对长达数十页的寄存器列表,无需死记硬背,但需要知道关键模块在哪里,以及如何快速查找。
中断控制:这是系统的“神经系统”。
PIC和PIC_1的寄存器位于X空间最高端($FF_FFFF附近),包括中断优先级寄存器(IPR)。配置好PIC,才能让DMA传输完成、串口收到数据等事件正确触发核心中断。直接内存访问:
DMA和DMA_1是性能加速器。它们的控制寄存器、源/目的地址寄存器、计数器等,集中在X:$FF_FFF4到X:$FF_FFD0这片连续区域。每个核心有8个DMA通道,可以自动在内存与外设间搬运数据,极大减轻CPU负担。音频应用中,常用DMA将ADC采样的数据搬入处理缓冲区,或将处理完的数据搬至DAC。音频串行接口:
ESAI、ESAI_1、ESAI_2、ESAI_3是芯片与外部编解码器(Codec)通信的主要通道。它们的寄存器分布在X和Y空间。你需要配置时钟控制寄存器(RCCR, TCCR)、控制寄存器(RCR, TCR, SAICR)和数据寄存器(RX/TX)。例如,ESAI的发送数据寄存器TX0在X:$FF_FFA0,而ESAI_1的对应寄存器TX0_1则在Y:$FF_FF80。务必根据你使用的物理ESAI端口号,去查找对应的寄存器块。异步采样率转换器:
ASRC是专业音频处理中的明星外设,用于连接不同采样率的数字音频流。它的控制寄存器集中在Y:$FF_FC00附近。配置ASRCTR、ASRPMx等寄存器,可以设置转换率、滤波器等参数。核心间通信:
ICC寄存器(Y:$FF_FFDB-Y:$FF_FFD0)是实现双核软件同步和简单数据传递的硬件基础。通过写ICDR寄存器可以向另一个核心发送数据并产生中断,另一个核心通过读ICDR获取数据。这是实现双核任务调度、状态同步的关键。
编程访问示例: 在C语言中,我们通常将寄存器地址定义为易失性指针。例如,要配置ESAI的发送控制寄存器(假设使用ESAI模块,地址为X:$FF_FFB5):
// 定义寄存器地址(24位地址,通常左移一位以适应寻址) #define ESAI_TCR (*(volatile unsigned int *)0xFFFFB5) void configure_esai_tx(void) { // 假设我们需要设置字长24位,使能发送器 unsigned int config_value = 0x00002400; // 具体位域需参考手册 ESAI_TCR = config_value; }在汇编中,则使用move指令直接操作内存地址。
避坑指南:访问外设寄存器时,务必注意位域的定义和读写特性。有些位是只读的(如状态位),有些是只写的(如清除中断标志位),胡乱写入可能导致不可预期的行为。强烈建议使用芯片供应商提供的标准外设库(如果存在)或基于手册精心编写自己的驱动层,并对寄存器进行位域(bit-field)或掩码(mask)操作,避免直接写入整个寄存器而破坏其他配置。
4. JTAG/OnCE接口:硬件调试的生命线
当你的代码没有按预期运行,或者系统直接“挂死”时,JTAG接口就是你最重要的救生索。DSP56720/21的JTAG接口兼容IEEE 1149.1标准,并集成了飞思卡尔的OnCE(On-Chip Emulation)调试逻辑。
4.1 信号定义与硬件连接要点
JTAG接口主要包含以下5个关键信号(此外还有可选的TRST复位信号):
| 信号名 | 方向 | 内部上/下拉 | 功能描述 |
|---|---|---|---|
| TCK | 输入 | 上拉 | 测试时钟。所有JTAG逻辑的同步时钟,由调试器提供。频率决定了调试通信的速度。 |
| TMS | 输入 | 上拉 | 测试模式选择。这个信号的状态在TCK上升沿被采样,用于控制JTAG状态机(TAP Controller)的状态转换。它是JTAG链配置的关键。 |
| TDI | 输入 | 上拉 | 测试数据输入。指令和数据通过此信号串行移入芯片。 |
| TDO | 输出 | - | 测试数据输出。芯片内部的指令和数据通过此信号串行移出。 |
| TRST | 输入 | 下拉 | 测试复位(可选)。低电平有效,用于异步复位JTAG逻辑。如果不用,建议外部下拉。 |
硬件连接实操:
- 上拉/下拉:根据手册,TCK、TMS、TDI内部有上拉电阻,TDO为推挽输出,TRST内部有下拉。在实际PCB设计中,我仍然建议在靠近DSP芯片引脚的位置,为TCK、TMS、TDI添加外部上拉电阻(如4.7kΩ到10kΩ),为TRST添加外部下拉电阻。这可以确保在调试器未连接或上电过程中,这些信号处于确定的无效状态,避免意外激活JTAG逻辑或产生总线冲突。
- 信号完整性:TCK是时钟信号,应作为传输线处理,确保走线阻抗连续,并远离高速数字或模拟信号源,以减少抖动。TMS和TDI同样重要,不良的信号质量可能导致状态机错乱或数据错误。
- 多设备菊花链:如果你的板卡上还有CPLD、FPGA等其他支持JTAG的器件,可以将它们以菊花链形式连接(前一个设备的TDO接后一个设备的TDI)。此时,需要计算整个链的总IR长度(所有器件指令寄存器长度之和)和总DR长度,并在调试软件中正确设置。对于DSP56720/21,其JTAG链通常独立连接调试器,以避免复杂度。
4.2 JTAG与GPIO复用及HDI_SEL引脚
手册中提到,JTAG信号与Port G的某些引脚复用。具体是哪个引脚,需要查阅芯片的数据手册(Data Sheet)引脚分配表。例如,TCK可能与PG18复用。
- 复位后默认状态:芯片上电复位后,这些复用引脚默认被配置为GPIO功��且为断开(高阻)状态。这意味着,JTAG功能在复位后是默认使能的,调试器可以连接。这是非常友好的设计。
- 如何禁用JTAG:如果你的应用需要释放这些引脚用作GPIO,你需要在软件中重新配置相应的端口控制寄存器(
PCRG,PRRG),将它们设置为GPIO的输入或输出模式。一旦配置为GPIO,JTAG调试功能将失效,所以务必在开发调试完成后再考虑此操作。 - HDI_SEL引脚:这是一个特殊的配置引脚,仅存在于DSP56721的144引脚封装中。它用于选择
HDI24主机接口使用哪一组物理引脚。当HDI_SEL为低时,使用HDI24信号;为高时,使用HDI24_1信号。这个选择会影响与HDI24复用的GPIO引脚。该引脚内部有下拉电阻,即默认选择HDI24信号组。在硬件设计时,如果需要使用另一组信号,需要通过电阻上拉此引脚。
4.3 OnCE调试逻辑与实战应用
JTAG是物理接口标准,而OnCE是飞思卡尔集成在芯片内部的调试模块。通过JTAG接口,调试器可以访问OnCE模块,从而实现对DSP核心的深度控制。
OnCE提供的核心调试功能包括:
- 硬件断点:设置程序地址断点、数据访问(读/写)断点。这对于调试难以复现的随机内存覆盖问题至关重要。
- 单步执行:可以以汇编指令或高级语言(C)语句为单位单步执行。
- 寄存器与内存查看/修改:实时查看和修改所有核心寄存器、任何内存位置(包括外设寄存器)的内容。
- 实时变量监视:在不停止程序运行的情况下,监视特定变量的变化。
- 代码追踪:有限的指令追踪能力,帮助分析程序流。
使用JTAG/OnCE的典型工作流程:
- 硬件连接:将调试器(如Lauterbach TRACE32、iSystem debugger,或开源OpenOCD搭配适配器)通过JTAG接口连接到目标板。确保供电和接地良好。
- 调试器配置:在调试软件中创建新工程,选择正确的处理器型号(DSP56720/21),设置JTAG时钟频率(初始建议较低,如1-5MHz,稳定后可提高),并加载编译好的程序符号文件(ELF格式)。
- 连接与复位:连接目标,对DSP进行复位。调试器应能通过JTAG读取到芯片的IDCODE,确认连接成功。
- 下载与调试:将程序下载到目标内存(Flash或RAM),设置断点,开始调试。
常见问题与排查:
- 问题:调试器无法连接,报错“找不到设备”或“IDCODE不匹配”。
- 排查:
- 检查物理连接:确认JTAG线缆是否接反、虚焊。用万用表测量TCK、TMS、TDI对地上拉电压是否正常,TDO是否有输出。
- 检查电源与复位:确认DSP核心电压、I/O电压是否稳定且在正常范围内。测量复位信号是否已释放为高电平。
- 检查时钟:确认DSP的主时钟是否起振。JTAG逻辑本身可能不需要主时钟,但芯片的整体状态需要。
- 检查复用配置:确认你的程序是否意外地将JTAG复用引脚配置为了GPIO,从而禁用了JTAG功能。可以尝试在不运行用户程序的情况下(即芯片刚复位后)连接调试器。
- 降低TCK频率:过高的JTAG时钟在长线或信号完整性差的板子上会导致通信失败。将频率降至1MHz以下再试。
- 问题:可以连接并停止核心,但单步或运行程序时行为异常。
- 排查:
- 内存映射一致性:确认调试器加载的程序(包括链接地址)与当前芯片通过OMR设置的内存映射模式是否匹配。如果不匹配,单步执行时看到的代码和实际执行的代码可能不同。
- 看门狗:检查看门狗定时器是否被使能。如果使能了但未及时喂狗,程序运行一会儿就会被复位。在调试初期,可以先禁用看门狗。
- 中断冲突:不恰当的中断配置可能导致程序跑飞。检查PIC的初始化代码。
5. 多核系统下的内存与调试策略
DSP56720/21作为双核处理器,其内存映射和调试带来了新的维度和挑战。
5.1 双核内存访问协调与数据一致性
虽然两个核心有独立的程序和数据RAM,但共享内存和共享外设的存在带来了数据一致性问题。核心A写入共享内存的数据,核心B可能不会立即看到,这是由于每个核心可能有自己的数据缓存(尽管DSP56300核心没有硬件缓存,但编译器优化和流水线可能导致类似问题)以及内存访问顺序问题。
常用策略:
- 使用ICC硬件中断进行同步:这是最可靠的方式。核心A在数据准备好后,通过ICC向核心B发送一个消息(中断)。核心B在中断服务例程中读取数据。这确保了B在读取时,A的写入一定已经完成。
- 使用共享内存中的标志位:在共享内存中设置“数据就绪”标志。核心A写完数据后,执行一个存储屏障(具体指令需查汇编手册,如
nop或特定同步指令)确保写入对总线可见,然后设置标志位。核心B轮询该标志位,发现置位后读取数据。这种方法效率较低,且需要小心处理编译器优化(将标志位声明为volatile)。 - 避免同时读写同一位置:设计为生产者-消费者模型,使用双缓冲区。核心A写缓冲区1,核心B读缓冲区2;完成后交换缓冲区指针。这需要额外的同步机制来安全地交换指针。
5.2 双核JTAG调试技巧
大多数高端调试器(如TRACE32)都支持同时调试多核处理器。你需要为Core-0和Core-1分别创建调试会话(Session),但它们共享同一条JTAG物理链路。
调试流程建议:
- 先连接并停止所有核心:在下载程序前,先通过调试器连接并暂停(Halt)两个核心。这可以确保在初始化阶段,两个核心处于可控状态。
- 分别加载程序:为每个核心加载其独立的程序镜像(或同一镜像的不同部分)。注意它们的链接地址必须符合各自核心的内存映射。
- 控制运行与同步:你可以选择同时运行两个核心,也可以单独运行某一个。在调试通信或同步问题时,让一个核心单步执行,同时观察另一个核心的状态和共享内存的变化,是非常有效的手段。
- 利用系统视图:好的调试器会提供系统级视图,同时显示两个核心的寄存器、调用栈和断点状态,方便对比分析。
个人经验:在调试双核音频流水线时,我经常遇到一个核心工作正常,另一个核心看似“卡死”的情况。除了检查各自的代码,首要怀疑对象就是共享资源冲突。例如,两个核心是否同时配置了同一个共享外设(如某个时钟分频器)?是否在没有同步的情况下竞争同一块共享内存?这时,我会在访问共享资源的代码前后设置断点,然后让两个核心交替单步,观察冲突发生的具体时刻。另外,确保两个核心的中断优先级配置合理,避免高优先级任务长时间阻塞另一个核心的低优先级任务,也至关重要。
6. 从原理到实践:一个简单的启动与调试示例
让我们将以上所有知识串联起来,看一个简化的系统启动和调试流程。
步骤1:硬件上电与JTAG连接板卡上电,DSP复位释放。JTAG引脚因内部上拉/下拉和默认GPIO断开状态,处于准备好调试的状态。连接调试器,配置为DSP56720,TCK设为2MHz。
步骤2:通过JTAG进行初始内存配置在运行用户程序前,我们可能想先检查或修改内存映射。通过调试器的命令窗口,我们可以直接读写OMR寄存器。
// 假设我们要设置为内存映射模式1 (MS=1, MSW1=1, MSW0=1) // 首先读取当前OMR值 Data.Long X:0xFFFFFF = ? ; // OMR位于X内存空间最高地址(需查确切地址,此处为示例) // 假设OMR地址为X:0xFFFFFF,我们需要设置bit0(MS)=1, bit5(MSW0)=1, bit6(MSW1)=1 // 计算新值(需参考手册OMR位定义),然后写入 Data.Long X:0xFFFFFF = 0x00000063 ; // 示例值,具体掩码需计算步骤3:加载并验证程序将编译链接好的程序(例如core0_app.elf和core1_app.elf)分别加载到Core-0和Core-1对应的内存地址。利用调试器的内存查看功能,确认共享内存区域(如$030000起始处)的初始值是否正确。
步骤4:设置初始断点与运行在双核的main()函数开始处设置断点。先让Core-0运行,观察其是否能完成外设初始化(如配置ESAI时钟)。然后让Core-1运行,观察其是否能成功从共享内存中读取Core-0准备好的配置信息。
步骤5:调试典型问题——DMA传输失败假设Core-0的DMA无法将数据从ESAI搬运到内存。排查步骤:
- 检查DMA寄存器:通过调试器查看Core-0的DMA通道控制寄存器(
DCRx)是否使能,源地址(DSRx)、目的地址(DDRx)、计数器(DCOx)设置是否正确。 - 检查ESAI状态:查看ESAI状态寄存器(
SAISR),确认接收数据寄存器是否满(RDF标志)。如果ESAI本身没有收到数据,DMA自然无数据可搬。 - 检查中断:如果DMA配置为传输完成中断,查看PIC的中断状态寄存器,确认DMA中断是否被触发、是否被正确响应。
- 检查内存映射:确认DMA的目的地址在当前内存映射模式下是有效的、可访问的RAM地址。
这个过程深刻体现了内存映射和JTAG调试的紧密结合:内存映射告诉我们寄存器在哪里(地址),而JTAG提供了查看和修改这些寄存器内容的能力。
理解DSP56720/21的内存映射和JTAG接口,是驾驭这款强大多核音频处理器的基石。它不仅仅是查阅手册中的地址表格,更是在脑海中构建一幅清晰的硬件资源地图,并掌握与之交互的所有途径。从静态的内存分区到动态的RAM重配置,从独立的核内寻址到共享的通信区域,从物理的JTAG引脚到强大的片上调试逻辑,每一个细节都影响着最终系统的稳定性、性能和开发效率。希望这篇结合了手册原理与实战经验的解析,能帮助你在下一个音频DSP项目中,更加自信地进行系统设计和调试。记住,当遇到问题时,不妨回到这两个基础概念上来,往往能找到突破口。