硬件总线基础04:LPC & eSPI总线(1)

说在开头:关于科学是什么(1)

我们通过科学的“眼睛”看到了宇宙诞生以及最终的命运:世界从虚空而来到热寂中去;而在这中间是短暂的智慧文明认知世界、逃脱宿命的过程。我们相信科学给我们描绘的这个世界以及它最终的命运,但似乎又有所心有不甘,世界真如科学所描绘?那我们的存在是否有意义。

科学创造出了那么多辉煌的成就,虽然相对论和量子论在有些问题上还存在一些困惑,但在日常生活中,科学理性真是无敌的存在啊,它体现在我们生活的方方面面,我们触手可及,放眼望去的绝大多数东西(电脑、手机,书桌,台灯,高楼大厦……)都是科学的成果;而我们在经过了这么多年科学的熏陶之后,如果对一个事情表达不理解时,往往会脱口而出:这不科学!我们在享受科学所带来的成果时,科学也已经融入了每个人的血液里:科学万岁!如霍金在《大设计》中所说科学已经取代(终结)了哲学,担负起了探索宇宙本源的责任(当然对于我们从小就接受科学教育的一代来说,从来都是认为科学才是探索宇宙的正道)。那么对于我们经常挂在嘴边的科学,如果想再追问一下:科学到底是啥?这似乎又是让人一头懵逼。

我们对科学的最直观理解是:科学是对客观世界的正确反映;科学首先要观察客观世界,然后对客观世界的现象进行解释(形成科学理论),最后该科学理论还能做出正确的预测。想想牛爵爷和爱大神PK的时候(“水星进动”现象的观测),就是看谁预测的更准确,我们必须要检验这些预测,如果蒙对了才算是正确和有用的。所以符合客观经验的就是正确的科学理论,相反不符合的就是错误的科学理论,而这个科学观,叫作:实证主义。

实证主义说:所有的科学经验,必须有经验来证明它是正确的。嗯?这句废话有什么问题么?当然,它的问题是:这根本没有办法执行。绝大多数科学理论都是无法被证明的,举个栗子:我们要证明所有的天鹅都是白色的,那么唯一的做法就是检查全世界所有天鹅的颜色;甚至这还不够,必须的找到历史上出现过的,以及未来即将出现的所有天鹅,才能证明上述的结论;这显然不是人类目前所能做到的。那么如果尽我所能去找天鹅,只要它们都是白色的,就能证明“所有的天鹅都是白色的”么?也不行啊。休谟早就已经抨击过这个错误:从理性逻辑来说,再多的偶然观测也不能得出必然的结论。在17世纪之前,欧洲人见到的所有天鹅都是白鹅的,无数次的观测结果让欧洲人相信,天鹅一定的白色的,直到1697年他们发现了黑色的天鹅。这个栗子刚好证明了实证主义的错误,即便人们发现再多的白天鹅,也不能证明所有的天鹅是白色的!休谟嘿嘿一笑。

行吧,既然人类没能力证明“所有的天鹅都是白色的”,那就用概率!我们每发现一只白天鹅,就增加这个命题为真的概率,这样研究的天鹅数越多,那么这个命题就越可靠(概率越高)。这法子看上去似乎很不错,该理论就叫“概率真理”:虽然不可能找到绝对真理,但起码能提高科学理论为真的概率

可惜这个理论也有问题:“亨普尔悖论”,假如我们多发现一只天鹅是白色的,就增加“所有天鹅是白色的”(命题A)为真的概率,但是这个命题的逆命题是“所有的天鹅是黑色的”(命题B);从逻辑上来说,A和B这两个命题是等价的,那么按照概率真理观,每发现一个不是天鹅且不是白色的东西,就可以增加命题A为真的概率。就是说,我坐在屋子里随便看,每次看到不是白色的东西,而且还不是天鹅,那就增加了“所有天鹅都是白色的”命题为真的概率,更荒谬的是还增加了“所有天鹅都是黑色的”命题为真的概率。(参考自:林欣浩-哲学家们都干了些什么)

一,LPC总线协议

如上一章结尾所述,LPC由英特尔于2002年推出V1.1版本,最开始是用来取代IBM的16位ISA(Industry Standard Architecture)/X-bus总线;ISA总线的最大速率只有8MB/s,虽然后续又推出了EISA总线,但这种修修补补显然已经满足不了CPU快速发展的需求了。

在当时看来LPC是更加先进的:

1. 降低ISA/X-bus总线的成本;

2. 总线数量更少;

——7个必选管脚+6个可选管脚。

3. 速率更快;

——支持33MHz总线频率。

4. 可访问内存空间更大。

——从X-bus的16MB内存空间,增加到4GB;支持远大于1MB的BIOS空间。

LPC接口设计的Super I/O芯片、Flash芯片都能享有脚位数减少、体积更小的好处,主板的设计也可以简化,这也就是取名LPC——Low Pin Count的原因

LPC支持如下种类的设备连接:

1. Super I/O:I/O Slave,DMA,Bus Master;

2. 音频设备(包括:AC’97 ):I/O Slave,DMA,Bus Master;

3. 通用存储设备(包括BIOS): Memory Slave(Firmware Memory Slave);

4. 嵌入式控制器:I/O Slave,Bus Master。

我之前用X86的LPC连接至CPLD,用于:80 Port和调试串口输出,以及CPLD的管理;当然如果是传统服务器架构,80 Port一般直接连BMC。

1,LPC总线管脚定义

如下图所示为7个必选管脚和6个可选管脚的定义,其中很多信号是与PCI总线共用的,而不需要在主控上定义新的管脚;主控和外设上必须支持7个必选管脚,而6个可选管脚则根据具体功能要求可选。

1. LAD[3:0](InOut):双向信号,总线通信地址、数据以及控制信号线;

1,包括了:Start/起始,Stop/停止,Transfer Type/事务模式(Memory、I/O、DMA),Transfer direction/传输方向(Read/Write),Address/地址,Data/数据,Wait State/等待状态,DMA Channel/DMA通道,Bus Master grant/总线主控准许;

——关于Memory,I/O以及DMA的事务方式: I/O事务一般专用于地址空间很小(举个栗子:64KB)外设设备:键盘、鼠标之类;而Memory事务则地址空间大(举个栗子:4GB)的存储设备; DMA事务则是提供了LPC设备直接读写CPU下挂内存的一个通道。

——I/O空间和Memory空间是相互独立的(源于X86对寄存器的编址方式:独立编址。现在I/O地址空间用的很少了,因为I/O地址空间已经不够用了)。统一编址的外设(I/O)寄存器和内存(Memory)所占的地址空间是统一编址的,举个栗子:ARM处理器等RISC架构CPU;而独立编址的外设寄存器(I/O)不占CPU的内存(Memory)地址空间,举个栗子:X86

2,并非所有的总线通信中LAD[3:0]有相同的方式,举个栗子:DMA读写操作不需要地址,而用的是DMA通道号。

2. LFRAME#(Out):帧头信号,表示一个新操作的开始和中断周期的结束;它指示了一个操作的启动/停止;

——我们在测量LPC总线时,可以以LFRAME#为指示来确定数据事务的内容。

3. LREST#:复位输出信号,与PCI总线的复位输出信号共用;

——如果系统已有PCI总线的复位信号PCIRST#,则在LPC控制器上不需要该管脚。

4. LCLK:时钟输出信号(33MHz),与PCI总线的时钟输出信号共用。

——如果系统已有PCI总线的时钟信号PCICLK,则在LPC控制器上不需要该管脚。从本质上来看,LPC与PCI的控制器是同源的,能实现LPC与HOST/PCI之间数据交换。如下图所示。

5. LDRQ#(In):用于外设进行DMA 或总线控制操作的总线请求信号,外设之间不能共用LDRQ#,1对1连接至主控制器(可选);

6. SERIRQ(InOut):终端请求信号,只有需要终端请求的外设需要(可选);

7. CLKRUN#(In/OD):与PCI总线的CLKRUN#功能相同,只有外设进行DMA或总线控制操作的才需要,用于停止/中断PCI总线(可选);

8. LPME#(In/OD):用于外设将主控从睡眠模式中唤醒,功能同PCI PME#(可选);

9. LPCPD#(O):Power Down,用于指示外设的电源将要下电(可选);

10. LSMI#(In):当外设需要执行重试I/O指令时启用LSMI#中断(可选)。

2,LPC总线通信协议

如下表格所示为LPC总线通信协议支持不同种类的事务类型(Cycle Type)操作,举个栗子:I/O读写,Memory读写,DMA读写,Firmware Memory读写等等。

主控和外设执行下述的周期类型,需要注意的是:

1. 外设不能尝试主控不支持的总线主周期。举个栗子:如果主控不支持总线主I/O周期,外设就不能尝试这些周期;

2. 外设必须忽略它们不支持的周期。

2.1 MemoryI/O以及DMA总线操作

LPC通过4bit(LAD[3:0])的串行总线实现数据的传输,总线的特点有:

1. LFRAME#,该控制信号由主控输出,用于指示总线传输的启动/停止;

2. LAD[3:0]总线,传递包括:cycle type/事务类型,cycle direction/事务方向,chip selection/从设备选择,address/地址,data/数据,以及wait states/等待状态等信息;

3. 边带信号,实现的边带信号传递中断和电源管理功能(可选)。

Cycle的一般流程如下:

1. 主控将LFRAME#信号拉低,指示循环流程开始,并将相应数据发送到LAD[3:0]上;

2. 主控按照Cycle类型,驱动相应的数据到LAD[3:0]上:地址、DMA通道号或总线主授权等等;

——LAD[3:0]数据线上的具体信息,如上所列。

3. 主控按照不同的Cycle类型选择输出数据,或则移交总线控制权给外设;

4. 外设获得总线控制权后,将相应的数据驱动到LAD[3:0]上,表示完成了该Cycle;

5. 外设释放总线控制权,结束Cycle。

一个典型的Cycle流程如下图所示(Memory Cycle):

1. START:该字段用于指示一个传输的启动/停止(如上图所示),当FRAME#信号有效时,所有的外设都要开始监测LAD[3:0]数据线上的信号,并在FRAME#信号有效的最后一个时钟进入START状态。START不同状态指示如下表所示;

——当LFRAME#有效时,可能有多个START状态值,所以外设在LFRAME#失效之前需要保持对START状态的监测,当监测到该状态值不是外设设置的值,那么忽略这次操作。

1,如下表所示,该状态可以指示非总线主控cycle的“启动(0000)/停止(1111)”,也可以指示主控总线号:Master0(0010)/Master1(0011);

2,START可以占据2个Clock周期,而且允许START状态值发生变化,但是以最后一个Clock采集到的状态值有效(忽略第一个Clock采集到的START值),如下图所示。

2. CYCLE TYPE + TYPE:该字段由HOST驱动,对Cycle的事务类型(Memory、I/O、DMA)以及传输方向进行说明;LAD[0]在该状态中被保留,外设需忽略该bit;具体定义如下表格所示;

3. ADDR:对于I/O操作是4个clock位宽(16bit,64KB地址空间),对于Memory操作时8个clock位宽(32bit,4GB地址空间),地址并不会在DMA操作中输出(DMA用的是DMA Channel);

——ADDR采用的是大端模式,举个栗子:对于Memory操作,第一个Clock输出的是地址A[31:28],最后一个Clock输出的是地址A[3:0]

4. TAR:Turn-Around,该字段用于交换总线的控制权(2个Clock周期),当主控要将总线转交给外设时(举个栗子:读操作),TAR由外设驱动;

——TAR的两个Clock周期:第一个Clock周期由主控或外设驱动:LAD[3:0] = 1111,第二个Clock周期由主控或外设/从机将LAD[3:0]设置为高阻态;由于LAC[3:0]内部有弱上拉,所以在TAR的两个时钟周期内,LAD[3:0]都是1111。

5. SYNC:该字段用于加入等待状态,持续时间为1~N个Clock周期,在Target或DMA事务操作时,SYNC由外设驱动;在Bus Master操作时,SYNC由主控驱动;如下所示;

1,如果外设需要等待状态,可以通过在LAD[3:0]上驱动“0101b”( Short Wait)或“0110b”(Long Wait)来实现,直到外设准备好;当外设就绪时选择驱动“0000b”(Ready)或“1010b”(Error),或则在DMA事务模式下选择“1001b”( Ready More);

2,如果主控或外设选择插入等待状态,那么必须选择一种类型的SYNC状态值(“0101b”或“0110b”),并且在等待状态完成之前不能更改。

(1)Short Wait:正常等待状态,一般只需要几个Clock周期就能完成;

——最大不超过8个Clock 周期,如果等待周期超过该值则总线会退出。

(2)Long Wait:需大量的Clock周期才能完成等待状态,等待时间一般超过1ms。

——外设不限定最大Clock周期,由主控决定。

6. DATA:该字段是2个时钟宽度,代表了1个Byte数据;

——数据是采用小端模式,即:第一个时钟驱动D[3:0],第二个时钟驱动D[7:4]。

7. SIZE:该字段用于确定在DMA事务机制中,每次传输的数据大小;由主控驱动输出,占1个Clock周期;其中Bit[3:2]是预留的,而且必须被驱动为00,Bit[1:0]定义如下表所示;

8. CHANNEL:该字段用于指示被准许外设的DMA通道号,由主控驱动,占1个Clock周期;其中Bit[2:0]指示的是DMA 通道号。

2.2 MemoryI/O协议

我们上面已经看了Memory操作各字段的定义图,如下所示;通过上节的描述也清楚了各个字段的意思以及操作目的,接下来再详细看下这些操作流程。

Memory读写操作流程如下图所示,在如下Memory Read流程中,SYNC值设置为5个Clock周期,该值参考的EEPROM器件的等待时长(典型值:120ns);Memory Write对于外设来说没有等待时长的要求,只需要外设的Ready指示。

I/O操作各字段的定义如下表所示;I/O操作通常用于对寄存器/FIFO的访问,有最小同步时间的要求(至少为1个clock周期)。

I/O读写操作流程如下图所示,在如下I/O Read流程中,对读操作没有等待状态要求,因为这个操作更像是从FIFO中取值;I/O Write对于外设来说也没有等待时长的要求,只需要外设的Ready指示。

由于正常的Memory读写操作一次只能操作1个byte,效率太低,所以Intel在LPC 1.1 Spec版本中引入了Firmware Memory Read/Write机制,提升对Flash的访问速度。这部分本章不做深入介绍,有兴趣的胖友可以找LPC总线规范-《Low Pin Count (LPC) Interface Specification. Revision 1.1》看一下,整体结构与Memory操作相差不大。

2.3 DMA协议

通过使用外设的LDRQ#信号和主控LAD[3:0]上的特殊编码来处理LPC上的DMA事务,LPC总线接口支持Single,Demand,Verify以及Increment模式;DMA通道0~3支持8bit数据位宽传输,而通道4~7支持16bit数据位宽传输。

有DMA事务需求的外设需要通过专用的LDRQ#信号(每个外设对应1个单独的LDRQ#),主控至少有2个LDRQ#,允许两个外设支持DMA事务模式。LDRQ#与LCLK时钟信号同步,如下图所示。

1. 通过使能LDRQ#(拉低 1个Clock周期)信号来开始流程,LDRQ#在空闲状态下为高;

2. 接下来的3bit为DMA 通道号,大端模式(先发MSB最后是LSB);

3. ACT表示对指定的DMA通道的请求是有效还是无效的:1表示有效,0表示无效;

——ACT为低表示放弃了对该通道的先前请求

4. 在ACT后的1个Clock周期,必须保持高电平;然后结束本次LDRQ#序列,进入下一个序列。

DMA操作字段定义如下所示,自从第二个字段CYCTYPE定义为DMA后,后续字段与Memory和I/O操作的差别较大;这主要取决于DMA操作的特殊性,其操作的地址空间已由CPU指定,对于LPC来说只需要确定相关通道号以及数据大小,即可操作(读/写)。

我们再来简单看一下DMA操作的具体流程,如下图所示。

2.4 LPC复位

LPC接口的LRESET#复位时序与通常的系统复位是不同的,主控和外设需要遵从如下复位规则:

1. 当LRESET#解复位时,总线时钟需先恢复正常龚总,具体时钟与解复位的时序关系参考PCI总线规则;

2. 当LRESET#复位时,主控必须满足如下要求:

1,LFRAME#将被驱动为高;

2,LAD[3:0]将被置位高阻态;

3,忽略LDRQ[n]#状态。

3. 当LRESET#复位时,外设必须满足如下要求:

1,忽略LFRAME#状态;

2,LAD[3:0]将被置位高阻态;

3,LDRQ[n]#将被驱动为高。

3,LPC总线电气规格

LPC的AC/DC电气规格参数同PCI总线规范中定义相同,参考“PCI Local Bus Specification, Rev 2.3” “Section 4.2.2”章节。LPCPD#和LSMI#信号的电气规格参数如下表所示。

对于LPC总线LAD[3:0]的信号要求上拉以满足在TAR周期内保持高电平的要求,同时LDRQ[1:0]信号如果不连接LPC外设,那么要保持其无效状态,也需要外部上拉。推荐上拉电阻值如下表格所示。

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
LPC(Low Pin Count)总线是一种低速、低成本、低功耗的总线,通常用于连接嵌入式系统中的外设,如实时时钟、电池管理器、BIOS芯片等。下面我们来写一个简单的LPC总线驱动程序。 首先,我们需要定义LPC总线的基地址和相关寄存器的偏移量: ``` #define LPC_BASE_ADDR 0x10000000 // 基地址 #define LPC_SC_OFFSET 0x1C // 状态寄存器偏移量 #define LPC_IRQ_OFFSET 0x20 // 中断状态和控制寄存器偏移量 ``` 接下来,我们定义LPC总线的状态寄存器和中断状态/控制寄存器结构体: ``` typedef struct { volatile uint32_t sc; // 状态寄存器 volatile uint32_t irq; // 中断状态和控制寄存器 } lpc_regs_t; ``` 然后,我们定义一个全局变量来存储LPC总线的寄存器地址: ``` static lpc_regs_t* const lpc_regs = (lpc_regs_t*) LPC_BASE_ADDR; ``` 接下来,我们实现LPC总线的初始化函数,该函数将初始化LPC总线的状态寄存器和中断状态/控制寄存器: ``` void lpc_init(void) { // 将状态寄存器和中断状态/控制寄存器设置为默认值 lpc_regs->sc = 0; lpc_regs->irq = 0; } ``` 然后,我们实现LPC总线读取函数和写入函数: ``` uint8_t lpc_read(uint8_t offset) { return *(uint8_t*)(LPC_BASE_ADDR + offset); } void lpc_write(uint8_t offset, uint8_t value) { *(uint8_t*)(LPC_BASE_ADDR + offset) = value; } ``` 最后,我们实现LPC总线的中断处理函数: ``` void lpc_irq_handler(void) { // 处理LPC总线的中断 // ... } ``` 这就是一个简单的LPC总线驱动程序。需要注意的是,由于LPC总线的数据传输速率较慢,因此我们在使用LPC总线时需要考虑到传输速率的限制,避免出现数据丢失或传输错误的情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值