Chapter 1 嵌入式计算
嵌入式计算系统是任何内部包含了一部可编程计算机的设备
微处理器就是一种单芯片的CPU
集成电路设计高成本且耗时,因此通过重新设计软件来重用硬件的能力成为关键
嵌入式计算操作特点:实时、多速率
对实时性能的需求使我们需要用不同的架构来实现系统,多处理器可以最好地实现实时性能
嵌入式系统设计面临的挑战:
- 需要多少硬件:成本和性能的折中
- 如何满足时限:通过加速硬件而加快程序的运行速度(提高CPU频率)。程序执行速度还受限于内存系统
- 最小化功耗:通过减慢机器非关键部分的运行速度来降低系统的功耗,同时又满足必要的性能目标
- 可升级性,可靠性,安全性
- 受限的开发环境:我们通常在某种类型的机器上(比如 PC)将代码编译好,然后将编译好的可执行文件下载到嵌入式系统中。
嵌入式计算的核心是实时计算,程序必须满足时限需求(性能目标)
Chapter 2 指令集
一些类型的处理器允许同时多发射指令。超标量 (superscalar) 处理器在运行时使用专门的逻辑识别可以同时执行的指令。VLIW 处理器依靠编译器来确定哪组指令可以同时执行而不产生错误。但是,超标量处理器的高耗能和高价格使其无法被广泛应用于嵌入式系统,而 VLIW 处理器常被用于高性能的嵌入式计算平台中。
CPU只是一个完整计算机系统的一部分。除了内存,计算机还需要I/O设备才能构建一个可用的系统。我们可以使用几种不同的芯片来构建计算机,但是许多可用的计算机系统都只是基于单个芯片。微控制器 (microcontroller) 就是单芯片计算机的一种,它由处理器、内存和I/O设备构成。这类系统通常使用的是较小的CPU, 并且使用一些只读存储器作为存储。而当我们提到片上系统 (System on Chip) 时,通常指使用的处理器较大,并且包含一个片上 RAM, 同时还带有更大容量的片外存储器。
编译器在一块被称为帧 ( frame) 的内存中传递和返回变量,帧也被用于分配局部变量,帧作为栈的元素整个压入栈中。
大多数寄存器都是内存映射的,即寄存器被赋予了内存空间中的地址。
C55x 支持24位地址空间,提供16MB内存。数据、程序和输入/输出访问都被映射到同一物理内存。但是这三种空间的寻址方式不同。程序空间按字节寻址,因此指令地址的长度为24位。数据空间按字寻址,因此数据地址为23位(它的最低有效位被设置为 0)
Chapter 3 CPU
CPU 与设备内部之间的接口是一组寄存器。CPU 通过读取和写入寄存器与设备进行通信。设备通常有如下几个寄存器:
- 数据寄存器保存被设备视为数据的值,例如磁盘读取或写入的数据
- 状态寄存器提供有关设备操作的信息,例如当前操作是否已经完成
UART( Universal Asynchronous Receiver/Transmitter, 通用异步接收器/发送器)是早期用于串行通信的器件,如PC上的串行端口连接器。
每个字符都以一个起始位0开始,以一个停止位1结束。起始位用于在接收器中识别新字符的开始;停止位用于标识一次传输结束,接收方可以开始数据识别和转换了。起始位和停止位之间,数据位以高电压1和低电压0状态匀速发送。这个发送速率被称为波特率,一个位的传输时间就是波特率的倒数。
微处理器可以通过两种方式为输入和输出提供编程支持:I/O指令和内存映射 I/O。最常见的实现I/O的方法是通过内存映射IO(MMIO),即使提供I/O指令的CPU也可以实现内存映射I/O。顾名思义,内存映射I/O为每个I/O设备中的寄存器提供一个在内存中的地址。 程序使用CPU的标准读写指令与设备进行通信。
在程序中与设备通信的最简单方法是忙等待I/O。设备通常比CPU慢,可能需要多个周期来完成操作。 通过读取设备的状态寄存器来询问 I/O设备是否空闲的操作通常称为轮询。
忙等待I/O是非常低效的,因为 CPU在I/O事务进行时只会测试设备状态而不做其他事。在许多情况下,在I/O事务处理的过程中,CPU可以与之并行地执行很多任务。为了能够支持并行处理,我们引入中断机制。
中断机制允许设备向CPU发出信号并强制执行特定的代码段。当中断发生时,程序计数器的值被改变为指向处理该设备的中断处理程序 (也通常称为设备驱动程序),此程序可以用于写入下一个数据,读取刚刚就绪的数据,等等。中断机制当然保存了中断时程序计数 器的值,以使CPU可以返回到被中断的程序。因此,中断允许 CPU 中的控制流很容易地在不同的上下文之间进行切换。
- 当需要来自CPU的服务时,I/O设备发送中断请求 (interrupt request) 信号;
- 当CPU准备处理I/O设备的请求时,CPU将发送一个中断应答(acknowledge)信号。
中断允许大量的并发操作,这使得 CPU 的效率更高。
保护现场:任何由中断处理程序写入的CPU寄存器在被改写之前都必须先保存原来的值,并且在处理程序结束之前恢复其原来的值。
CPU通过在每个指令开始执行时检查是否有中断请求来实现中断。如果已发出中断请求,CPU就不去读取PC指向的指令,而是将PC设置为中断处理程序的开始位置。
我们有两种方式使中断能够处理多个设备,并为相关的硬件和软件提供更多更灵活的定义方式:
- 中断优先级 (interrupt priorities) 使得CPU能够分辨出中断之间在重要性上的差异。
- 中断向量 ( interrupt vectors) 使得中断设备指定其中断处理程序。
完整的中断处理过程:
- CPU:CPU在指令开始时检查未决中断。它响应优先级最高的中断,并且需要确认该中断的优先级高于中断优先级寄存器中给定的优先级。
- 设备:设备接收中断应答信号,并向CPU发送其中断向量。
- CPU:CPU使用中断向量作为索引在中断向量表中查找设备中断处理程序的地址,并用类似于子程序的机制保存PC的当前值和CPU的其他内部状态,例如通用寄存器等。
- 软件:设备驱动程序保存更多的CPU状态,然后在设备上执行所需的操作。接着恢复保存的状态并执行中断返回指令。
- CPU:中断返回指令恢复PC和其他自动保存的状态,然后返回到被中断的代码处继续执行。
CPU架构师希望给CPU的实现提供灵活性,在指令集级别提供这种灵活性的方法是利用协处理器。协处理器是连接在CPU上的附属器件,并能够用于执行一些特殊指令。例如,通过在CPU上添加一个专门用于实现浮点指令的芯片,将浮点运算引入到Intel体系结构中。
复杂的系统通常由几个相互通信的程序来实现,这些程序在操作系统的控制下协同工作。
存储管理单元MMU负责在CPU和物理内存之间进行地址转换,能够将地址从逻辑空间映射到物理空间,通常称这个过程为内存映射。现代MMU多用来实现虚拟地址管理。
用于地址转换的高速缓存被称为转译后备缓冲器(Translation Lookaside Buffer, TLB)
程序性能会受流水线、超标量执行和高速缓存的影响。其中,高速缓存所引起的指令执行时间的变化是最大的。
CPU可以提供用于管理功耗的静态(独立于程序行为)或动态(受当前执行的指令影响)方法。
Chapter 4 计算平台
大多数总线协议是基于四次握手协议( four-cycle handshake)构建的。握手用于确保当两台设备想要通信的时候,一台准备好发送,另一台准备好接收。 握手时使用一对专用于握手的线路:enq(表示询问)与 ack(表示确认)。其他线路用于握手期间 的数据传输。 握手的每一步都由 enq 或 ack 线路上的电平转换来标识:
1.设备1升高它的输出(enq)电平以发出查询信号,告诉设备2做好监听数据的准备。
2.当设备2准备好接收时,它升高输出(ack)电平以发出确认信号。这时,设备1与设备2就可以发送或接收信号了。
3.一旦数据传输完毕,设备2就降低它的输出(ack)电平,表示它已接收完数据。
4. 看到ack信号被释放,设备1就降低它的输出(enq)电平。
在握手结束时,双方握手信号均为低电平,就和开始握手前一样,系统回到其初始状态,为下一次以握手方式传输数据做好准备。
我们将总线上的读或者写称为事务,总线事务的操作由总线协议管理。大多数现代总线使用时钟来同步总线上的设备操作,总线时钟频率通常比CPU慢。
我们也可以使用总线握手信号来执行突发传输 ( burst transfer), 如图所示。 在这个突发读事务中,CPU发送一个地址但是收到一个数据值序列。这些数据来自于从给定地址开始的连续内存空间。
标准总线事务需要CPU参与到每个读或写事务中。然而,有些数据传输类型并不需要 CPU 的参与。例如,一个高速 I/O 设备想要向内存发送一个数据块,虽然可以编写一个程序轮流从设备读取数据,并向内存写入数据,但是,如果CPU不参与的话,设备与内存之间的直接通信速度会更快。这种能力要求除了CPU之外的其他单元能够对总线进行控制操作。 直接内存访问(DMA)是一种不由CPU控制的总线读写操作。DMA传输由 DMA控制器控制。它从CPU请求总线控制权,得到控制权后,DMA控制器直接在设备与内存之间进行读写操作。
AMBA总线:高速总线AHB(高级高性能总线),低速总线APB(高级外设总线),二者通过总线桥连接。
RAM既可以被读取,又可以被写入。它们与磁盘不同,地址可以以任何顺序读取,因此被称为随机访问。现代系统中大多数大容量存储器都是动态RAM (DRAM)。DRAM 芯片的集成度非常高,因此单片的存储容量较大,然而,它的存储器单元内的值会随着时间而衰减,因此它需要周期性地刷新其值。
闪存Flash是ROM的主要形式,可重新编程,能够永久性存储。
代码最终运行的硬件称为目标机(target)。主机和目标机一般通过USB连接,但也可以使用高速链路连接,如以太网。交叉编译器是在某一种机器上运行但是为另一种机器生成代码的编译器。编译之后,可执行代码通常通过USB下载到嵌入式系统。
平台级性能度量:总线带宽(即数据传输速率)。我们可以通过两种方式增加带宽:增加总线的时钟速率,或者增加每个时钟周期传输的数据量。
系统级别的性能不仅取决于CPU, 还取决于内存与总线。
I/O设备使用逻辑电路来连接到总线,使得CPU可以读写设备的寄存器。
Chapter 5 程序设计与分析
嵌入式软件常用的三个结构/组件:状态机、循环缓冲区和队列。状态机适合于交互式系统,例如用户界面;循环缓冲区和队列应用于数字信号处理领域。
当程序输入是随机出现的非周期性信号时,自然而然地,系统可被认为是对输入的反应。大多数系统的反应可通过接收的输入及系统当前的状态来表征(即在某个状态下,收到某种输入数据,系统必然做出某种确定的反应),这种表征方式自然地产生了有限状态机的形式,以此来描述交互式系统行为。
FIR滤波器是经典的面向流的处理示例。对于每个数据样本,过滤器必定产生依赖前 n 项输入的输出结果。循环缓冲区是一种数据结构,它允许程序以高效的方式处理流数据。
新样本输入循环缓冲区数据变化示意图:
部分实现代码:
int circ_get(int i){ /*获取缓冲区第i个值*/
int ii;
ii=(pos+i)%CMAX; /*CMAX为缓冲区容量4*/
return circ[ii];
}
int FIR(int xnew){/*输入一个新的采样值并计算滤波器输出*/
...
for(i=0,result=0;i<CMAX;i++)
result+=b[i]*circ_get(i);
return result;
}
如在t+4时刻,y[4]=b[0]*circ_get(0)+...+b[3]*circ_get(3)=h[0]x[4]+h[1]x[3]+h[2]x[2]+h[3]x[1]。
循环优化:循环展开,有利于提高代码的并行度
寄存器分配 (register allocation) 是非常重要的编译阶段。给定一个代码块,需要选择存储在寄存器中的变量 (声明过的变量和临时变量),以最小化确定所需寄存器的数量。我们可以通过构建冲突图 ( conflictgraph) 来解决寄存器分配问题,并通过解决图着色问题来求解出分配方案。
合理的操作调度能够提升寄存器分配。我们可以自由选择操作的执行顺序,这是一个很好的优势。例如,通过改变操作的执行顺序来改变寄存器的分配,从而影响变量的生命周期。
软件流水线技术可以通过在若干循环迭代中重新排序指令的方式来消除流水线气泡。软件流水线减少了流水线阻塞,从而减少了指令的平均能量消耗。
程序的执行时间=程序路径+指令执行时间(性能优化公式)
程序路径是程序执行的指令序列(或者是程序在高级语言中表示的等价代码)
内存和高速缓存优化对于性能优化非常重要。
Chapter 6 进程与操作系统
操作系统通过创建进程来创建系统的基本运行环境。进程是程序的运行体,嵌入式系统可能同时有多个进程在运行。一个独立的实时操作系统 ( Real-Time Operating System, RTOS) 控制进程何时能够在CPU上运行。进程对嵌入式系统设计十分重要,因为它们有助于处理同时发生的多个事件。
要完成复杂的时序任务就会向程序中引入极其复杂的控制,但是使用进程对功能进行划分并将进程间切换所需的控制封装在操作系统中,可以更容易地在进程中以相对简单的控制来满足时间约束。
一个进程就是指一个程序的单次执行。如果两次运行一个相同的程序,那么就创建了两个不同的进程。每个进程都拥有自己的状态,这不仅包括它的寄存器还包含它所有的内存。在有些操作系统中,存储管理单元用于保证每个进程都位于独立的地址空间;而另一些操作系统,尤其是轻量级 RTOS, 进程运行于同一地址空间。我们通常将这种共享同一地址空间的进程称为线程。
实时操作系统是实现进程抽象与调度的软件组件。
一个实时操作系统能够允许几个程序同时运行,因此帮助我们构建更加复杂的系统。进程和任务就是多任务系统这样一个复杂系统的基本构件,就如同C函数和代码模块是程序的基本构件。
一个进程的周期是指两次连续成功执行之间的时间。例如,数字滤波器的周期可以被定义为对输入信号的两次成功采样之间的时间间隔。进程的速率(rate)就是它周期的倒数。在多速率系统中,每个进程都以其自己的速率执行。
进程有三个基本调度状态: 阻塞 (waiting)、就绪 (ready)、运行 (executing), 操作系统 认为一个进程必须处于这三个基本调度状态之一。在任何时间,最多就只有一个进程在CPU 上执行。
调度策略定义如何从就绪态进程的集合中选择进入运行态的进程。每个多任务操作系统都会实现某种类型的调度策略。选择正确的调度策略不仅能保证系统满足其所有的时间约束,还对实现系统功能所需的CPU计算能力具有深远的影响。
我们需要找到一种易用的编程技术,能够允许以不同速率运行周期性进程。为了简单起见,我们将进程看作是子例程。
定时器是控制循环执行的一种更加可靠的方式。我们可以使用定时器生成周期性的中断。如果有几个定时器,那么就可以将每个定时器设置为不同的速率。然后用一个函数囊括以某个特定速率运行的所有进程。
但是可能并没有足够的定时器来支持系统所要求的所有速率。一个替代的解决方法是使用计数器来划分定时器的速率。比如,如果进程p2()必须以pl()速率的1/3运行,那么就可以使用以下代码:
不将任务放入定时器处理程序是因为我们想要确保任务在一次迭代中完成。如果任务在 timer_handler()中执行但是它的运行时间超过了一个设定的周期,那么定时器就将再次中断并停止前一次迭代的执行。这样的话被中断的任务就可能处于不一致的状态。
抢占式实时操作系统解决了存在于协作式多任务系统中的根本问题,它能够按照系统设 计者提出的时间要求执行进程。准确满足时间要求的最可靠方式就是构建一个抢占式操作系统,并使用优先级控制进程在给定时间内运行。
为了使操作系统能够工作,需要引入两个基本概念。第一,引入抢占来替代C函数调用以控制执行。第二,引入基于优先级的调度作为编程者控制进程运行顺序的方法。
如图是一个操作系统抢占式执行的例子。我们想要在两个进程之间共享CPU。内核(kernel)是指操作系统决定运行哪个进程的那部分,它定期被定时器激活。定时器周期的长度被称为时间片(time quantum), 它是控制CPU活动的最小时间增量。内核决定接下来运行哪个进程并使该进程运行。在下一个定时器中断时,内核可能会选择相同的进程,也可能选择不同的进程去执行。
这里定时器的使用不同于上一节对定时器的使用。之前,我们使用定时器控制循环迭代,一个循环迭代会包括几个进程完整的执行过程。这里,时间片通常小于任何一个进程的执行时间。
我们必须使用汇编语言而不是C语言来实现进程之间的切换。定时器中断使得控制权从当前执行进程转移到内核;汇编语言可以用于保存和恢复寄存器。类似地,我们可以使用汇编语言恢复任何一个我们想要的进程的相关寄存器,而不是被定时器中断的进程的信息。进程运行相关的一组寄存器的集合被定义为它的上下文(context), 从一个进程的寄存器集切换到另一个就被称为上下文切换(context switching)。保存进程状态的数据结构被称为记录(record)。
如果我们为每个任务都分配一个优先级,优先级用数字来表示,那么内核就可以依此来设计调度方法。内核首先查看进程及其优先级,观察哪些进程能够执行(一些进程可能正在等待数据或者某个事件而导致不能执行),然后选择已就绪的进程中优先级最高的来执行。这种机制既灵活又快速。
操作系统的基本任务是在计算系统中的程序之间进行资源分配,以满足这些程序的资源需求。显然,CPU是最稀缺的资源,所以调度CPU是操作系统最重要的任务。
在通用操作系统中一个常见的调度算法就是轮转(round-robin)。所有进程都在一个列表中,并依次调度。它通常与抢占(preemption)相结合,以使一个进程不会占据所有的CPU 时间。轮转调度提供了一种公平的方式,通过这种方式所有进程都得到了执行的机会。
然而,它不能保证任何一个任务的完成时间。当进程数量增长时,所有进程的响应时间也 随之增加。相比之下,实时系统要求的公平概念包括时序特性(按照特定的顺序保持其前后关系)和截止时限的满足(在指定的时间内必须响应或者必须执行完成某段程序)。
单调速率调度 ( Rate-Monotonic Scheduling, RMS)是一种静态调度策略,因为它为进程分配固定的优先级,并总是选择最高优先级的就绪进程来执行。优先级依据周期的长短进行分配,周期最短的进程被赋予最高的优先级。这种固定优先级的调度策略是对进程静态优先级的最优分配。它可以提供最高的CPU利用率,同时保证所有的进程都能满足其截止时限。总的CPU利用率小于ln2(0.69)则RMS可用。
最早截止时限优先 (Earliest Deadline First, EDF) 是一种动态优先级调度,会在执行期间基于起始时间改变进程优先级。因此,它可以实现比RMS更高的CPU利用率。与 RMS 不同的是,它会在每个时间片都更新进程的优先级。在EDF中,根据截止时限分配优先级:优先级最高的进程是距离截止时限最近的进程,优先级最低的进程是距离截止时限最远的进程。一旦EDF重新计算完优先级,其调度过程就同RMS一样,即选择优先级最高的就绪进程来执行。如果 CPU 利用率小于或等于1, 那么就存在一个可行的EDF调度方案。
主要问题在于如何保持进程按照到达截止时限的剩余时间进行排序因为进程到达截止时限的时间 在执行期间是变化的,我们不能同 RMS一样,将进程预先排序并放入数组。为了避免在每一次变化时都对整个记录集重排序,我们构建一个二叉树来保持排序记录并逐步更新排序。在每个周期结束时,我们重新调整记录在树中的位置以保证仍然是一棵有序的二叉树。
进程不仅需要对内存进行读写操作,它可能还需要与I/O设备进行通信,要使用共享内存与其他进程进行通信。对于共享资源访问可能出现竞争。考虑这样一种情况,I/O设备上有一个标记位,进程可以检测这个标记位,也可以修改它的值。在这种情况下,当有多个进程想访问该设备时,问题就出现了。为了防止这类问题发生,我们需要控制操作发生的顺序。例如,我们需要确保一个任务已经完成了I/O操作,才能允许其他任务对这个I/O设备进行操作,这可以通过将敏感代码段封装到一个不会被中断的,能够连续执行的临界区中来实现。我们使用信号量来创建临界区。在进入临界区之前,我们调用一个信号量函数,这个函数会直到资源可用时才会返回。当资源使用完时,就调用另一个信号量函数去释放它。总线支持这个原子操作不被中断。
共享资源带来了一个新的微妙的调度问题:低优先级进程可能会通过持有其资源而阻塞高优先级进程的执行,这种现象被称为优先级反转。
处理优先级反转问题最常见的方法是优先级继承:当任何一个进程请求操作系统的资源时,就提升它的优先级,这样该进程的优先级就暂时变得高于可能使用该资源的任何其他进程。这就可以确保一旦该进程获得资源就可以继续执行,以使它利用所获资源完成自己的工作,然后将资源返回给操作系统,供其他进程使用。而一旦进程利用资源资完成了自己的工作,它的优先级就降回到正常值。
进程之间经常需要进行通信,因此操作系统提供了进程间通信机制 (interprocess communication mechanism), 它是进程抽象概念的一部分。进程间通信有两种主要的类型:共享内存和消息传递。
消息传递通信机制是共享内存模型的一种补充。每个通信实体都具有自己的消息发送/接收单元。消息不在通信链路上存储,而是存储在端点的发送器/接收器中。
对于每个程序单元相对独立,各自自主运行的应用程序,单元间的通信非常适合使用消息传递机制。例如,家庭控制系统中的每个家用设备都具有一个微控制器。这些设备间的通信相对较少;此外,他们的物理间距也足够大,这样我们自然不会想要为它们创建一个中心共享内存用于数据通信。很自然地,我们会想到在这些设备之间传递通信数据包以实现设备之间的协作。许多不配有外部存储器的8位微控制器中的通信经常都由消息传递来实现。
队列 (queue) 是消息传递的常见形式,它使用 FIFO ( First In First Out 先进先出) 规则保存消息的记录。
一个实时操作系统的中断延迟 ( interrupt latency) 是指从设备发出中断到设备所请求操作完成之间的时间。
我们一般希望仅对性能关键进程使用高速缓存划分,因为高速缓存保留浪费了有限的高速缓存空间。
由于需要满足复杂的时间要求,特别是对于多速率系统,我们不得不使用进程这个抽象概念。使用单个程序来满足多个速率下所有进程的截止时限是非常困难的,因为这时程序的控制结构会变得非常难以理解。进程封装了计算状态,使我们能很容易地在不同计算之间进行切换。操作系统封装了用以协调进程的复杂控制。用来确定在进程间传递CPU控制权的方案被称为调度策略。
- 线程是CPU调度的最小单位(程序执行流的最小单元),它被包含在进程之中,是进程中的实际运作单元。一条线程是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
- 抢占是将CPU执行权从一个进程转移到另一个进程的行为。
- 调度策略是确定要运行哪个进程的一组规则。
- 进程间通信机制使得数据能够在进程间可靠地传递。
Chapter 8 物联网系统
IoT (物联网) = 传感器 + 无线网络 + 数据库。物联网 ( Internet of Things, IoT)
物联网系统是一种软实时网络嵌入式系统,网络是IoT系统的关键组成部分——无线网络相比于有线网络能够支持更大范围的传感器应用。应用广泛的三个无线网络——蓝牙 /低功耗蓝牙(Bluetooth Low Energy, BLE)、IEEE 802.15.4/ZigBee 和 Wi-Fi。
开放系统互联 ( Open System Interconnection , OSI ) 模型是一个七层网络模型。
互联网协议 (Internet Protocol, IP) 是互联网中的基础协议。它提供无连接的、基于分组的通信。术语互联网通常意味着通过IP连接起来的计算机全球网络。
IP不是定义在特定的物理实现上的,它是一个网络互联 (internetworking) 的标准。 在标准中认为网络数据包是由其他一些底层网络负责传输,例如以太网。一般来说,一个网络数据包从源到目的地将经过几个不同的网络。IP允许数据通过这些网络从一个终端用户无缝流动到另一个终端用户。
IP工作在网络层。当节点A要向节点B发送数据时,应用层的数据经过几层协议栈后到达IP,IP通过创建数据包来路由到目的地,这些数据包紧接着到达数据链路层和物理层。在几种不同类型的网络中传输数据的节点被称为路由器。
一个IP地址就是一个编号 (在IP的早期版本中是 32 位,在 IPv6 中是 128 位)。IP 地址通常的形式是 xxx.xx.xx.xx。用户和应用程序通常通过域名来访问互联网节点,例如 foo.baz.com, 这些域名通过调用域名服务器 ( Domain Name Server, DNS) 转换为IP地址,DNS 是建立在IP之上的更高层的服务之一。
互联网也提供在IP之上的更高层的服务,传输控制协议(Transmission Control Protocol, TCP)就是其中之一。它提供面向连接的服务,确保数据以正确的顺序到达,并且使用一个确认协议以确保数据包到达。因为许多更高层的服务建立在TCP之上,所以基础的协议通常被称为TCP/IP。如用于万维网服务的超文本传输协议( Hypertext Transport Protocol, HTTP)。
网络的拓扑结构 ( topology) 描述的是网络内部的通信结构。在OSI模型中,两个节点之间是通过一个链路直接连接的。不直接连接的两个节点之间的通信需要若干跳。路由发现 ( routing discovery) 用于确定新节点和其他节点之间的数据包将使用的路由。
IoT系统利用低成本并可以接入网络的设备来构建复杂的网络。许多不同的网络都可以用来构建IoT系统。很多实际的系统会将多个网络结合起来,数据库通常用于管理IoT设备的信息,时间轮用来管理系统中的事件。
我们通常将IoT系统分为边缘和云两个部分:边缘设备是系统的应用设备之一;来自边缘设备的数据会发往互联网服务器进行远程处理,这个互联网服务器就是云设备。
Chapter 10 嵌入式多处理器
许多嵌入式系统需要多个CPU。为了构建这样的系统,我们需要使用网络来连接处理器、内存和设备。因此必须对系统进行编程,以利用多处理器中固有的并行性,并考虑由网络引起的通信延迟。
多处理器(multiprocessor)是具有耦合在一起的两个或更多个处理器的计算机系统。用于科学研究或商业目的的多处理器的架构通常很有规律:几个相同的处理器访问统一的存储空间。术语处理元件( Processing Element, PE)用来表示任何负责计算的单元,无论它是否是可编程的。术语网络(或互联网络interconnection network)用来描述处理元件之间的连接关系。
由于高频的处理器成本非常高昂,因此将应用程序分割到多个较小的处理器上执行成为一个相对便宜的方法。除了降低成本,使用多个处理器还可以对实时性能有所帮助。当把这些时间敏感的进程分配到单独的多个处理器上时,通常能够满足时限并且更容易对交互做出应答。
两种主流的多处理器架构:
- 共享内存 ( shared memory) 系统具有一个处理器池( Pl , P2等),能够对一个共同的存储器集 (Ml, M2等) 进行读取或写入访问。
- 消息传递 ( message passing) 系统具有一个处理器池,池中的处理器彼此之间能够传递息。每个处理器都拥有自己的本地内存。
多处理器片上系统 ( MultiProcessor System-on Chip, MPSoC)就是具有多个处理器元件的片上系统。这与分布式系统 (distributed system) 正好相反,分布式系统是物理上分离的处理器元件组成的多处理器系统。一般而言,用于MPSoC的网络是高速的,能够在处理器元件之间提供低延迟的通信。分布式系统使用的网络的延迟要远高于芯片内部的网络,但是许多嵌入式系统确实会要求使用物理上相距很远的多个芯片。MPSoC和分布式系统之间的延迟差异会影响对应系统中使用的编程技术。
共享内存处理器也非常适用于需要处理大量数据的应用。信号处理系统流数据非常适合共享内存处理。大多数MPSoC都是共享内存系统。
许多高性能嵌入式平台使用的是异构多处理器技术。不同的处理元件执行不同的功能。PE是具有不同指令集的可编程处理器,或者是不可编程(或者很少编程接口)的特殊加速器。
嵌入式多处理器的一个重要处理元件就是加速器(accelerator)。加速器可以为存在计算核心 (computational kernel)的应用程序提供大幅度的性能提升,其中计算核心是指程序中(反复执行从而)花费大量运行时间的小的代码片段。加速器还可以为实现低延迟 I/O 功能的加速提供关键支持。
加速系统的设计是一种硬件/软件协同设计 ( hardware/software codesign) 的方法,即同时使用硬件和软件进行设计以满足系统目标的方法。
CPU加速器被接入CPU总线。CPU通常被称为主机(host),CPU通过加速器中的数据和控制寄存器与加速器进行通信。这些寄存器允许CPU监视加速器的操作并向加速器发出命令。
CPU和加速器还可以经由共享内存进行通信。如果加速器需要对大量数据进行操作,那么通常有效的方式是将数据存储在内存中,并且直接让加速器进行读写,而不是让CPU 将数据在内存和加速器的寄存器之间传送。CPU和加速器使用同步机制来确保它们不会破坏彼此的数据。
加速器不是协处理器。协处理器被连接到CPU的内部,并进行指令处理。加速器通过编程模型接口与CPU进行交互,而不是执行指令。其接口在功能上等同于I/O设备,虽然它通常不执行输入或输出。设计加速器的首要任务是确定该系统确实对加速器有所需求。我们必须确保想要加速的功能在加速器上的运行速度会比在CPU上作为软件执行的速度快。
为了确定对加速器的需求,就必须对要加速的算法有很好的理解,算法通常是以高级语言程序的形式而存在的。我们必须将算法描述翻译为硬件设计。此外,还必须对加速器核与CPU总线之间的接口进行设计。该接口包括多个总线握手逻辑。例如,必须明确CPU上的应用软件如何与加速器进行通信,并提供所需的寄存器,可能还需要实现共享内存的同步操作,以及添加地址生成逻辑以从系统内存中读取和写入大量数据。
最后,我们需要设计CPU与加速器的连接接口。应用软件必须与加速器进行交互,向加速器提供数据并告知加速器要执行的操作。我们必须以某种方式实现加速器与应用程序其他部分的同步,以使加速器知道何时获取所需的数据以及CPU知道何时能够接收到预期的结果。现场可编程门阵列 ( Field-Programmable Gate Array, FPGA) 为定制加速器提供了一个有用的平台。FPGA 具有可编程逻辑门和可编程互连的结构,可以将其配置以实现特定的功能。大多数FPGA还提供板载存储器,可配置为多种接口的自定义内存系统。一些 FPGA提供板载CPU以运行与FPGA架构通信的软件。小型CPU也可以直接在FPGA架构中实现,这些处理器的指令集可以针对所需的功能进行定制。
现在最感兴趣的是用加速器来替换软件所实现的加速效果,总加速S可以写作
其中,tCPU是完全使用软件的等价实现在CPU中的执行时间,n是函数的执行次数。的数值。显然,函数执行的次数越多,加速器提供的加速也越大。
当设计分布嵌入式系统时,我们必须解决如下所描述的调度 (scheduling) 和分配 (allocation) 的设计问题:
1、 必须及时对操作进行调度,包括网络上的通信和处理单元上的计算。显然,在PE中的调度操作和PE之间的通信是互联的。如果一个PE太晚完成其计算,则它可能干扰网络上的另一个通信,因为它尝试将其结果发送给需要它的PE。
2、必须向处理单元分配计算。向PE的计算分配确定了需要什么通信,举例来说,如果在一个PE上计算的值是另一个PE所需要的,那么这个值就必须通过网络传输。如果两个进程被分配给同一个PE, 则 它们可以使用PE的内部存储器进行通信,而且不会占用网络通信时间。
硬件/软件协同仿真在加速器设计中非常有用。由于协同仿真器能够在硬件仿真的基础上相对有效地运行软件,所以它能够在相对逼真的模拟环境中测试加速器。计算密集型任务适合使用加速器。
多处理器提供了绝对的性能和效率,但这也确实使得系统的复杂度上升到一个新的层次。多处理器通常是异构的,应用程序的不同部分需映射到相应的处理单元上以发挥其功效。通过诸如新指令的增加,可编程处理单元可能会专用于某一功能。加速器是一种设计用于执行非常特殊任务的处理单元。向系统安装加速器时,我们必须确保该系统可以与系统的其他部分以所需的速率发送和接收数据。
推荐阅读: