1. 项目概述与核心价值
在嵌入式通信设备开发领域,尤其是基于飞思卡尔(Freescale,现NXP)MSC8122/26ADS这类高性能多核通信处理器的平台,硬件系统的稳定性和灵活性是项目成败的关键。这类平台往往集成了处理器、内存、以太网、TDM接口、CODEC等多种复杂外设,如何让这些硬件模块协同工作,并在上电、复位、配置等关键阶段保持确定的行为,是底层硬件设计的核心挑战。过去,工程师们常常依赖大量的74系列逻辑芯片、CPLD/FPGA的“黑盒”IP或者处理器内部有限的GPIO来实现这些控制逻辑,不仅电路板面积大、功耗高,而且一旦设计定型,后期修改几乎等同于重新画板,灵活性极差。
MSC8122/26ADS参考手册中提供的这片Altera CPLD代码,正是解决上述问题的经典范例。它不是一个简单的“粘合逻辑”,而是一个完整的、可编程的“硬件系统管理单元”。这片代码用Verilog HDL编写,运行在一块CPLD上,承担了从电源时序管理、多路复位生成与去抖、总线仲裁、外设片选与使能,到以太网物理层模式动态切换、JTAG链管理、LED状态指示等数十项关键功能。对于从事此类平台开发的硬件工程师、嵌入式BSP(板级支持包)工程师乃至系统架构师而言,深入剖析这片代码,就如同拿到了一张通往硬件核心控制层的“地图”。它不仅能让你理解这块评估板(ADS)是如何工作的,更能让你掌握一种设计思想:如何用可编程逻辑器件,构建一个可靠、灵活且易于维护的硬件抽象层。这对于自主设计通信设备主板、进行故障排查和功能定制,具有不可替代的实战价值。
2. 代码整体架构与设计思路拆解
这片CPLD代码的工程名为BCSR_8122_12(Board Control and Status Register,版本1.2),其设计核心思想是**“寄存器映射+状态机+组合逻辑”**的三位一体架构。它不是一堆散乱的门电路描述,而是有清晰层次和模块化思维的硬件设计。
2.1 核心模块与接口定义
模块BCSR_8122_12定义了极其丰富的输入输出信号,总计超过150个端口。这直观地反映了其作为“系统枢纽”的定位。我们可以将其接口大致归类:
- 系统控制与时钟接口:如
clk(主机时钟)、extclk(外部时钟)、reset(上电复位)。这是CPLD逻辑运行的时序基础。 - 处理器总线接口:包括地址线(
A27-A29,XAt7-XAt10等)、数据线(Data[0:7])、控制线(nWE,nR_W,nCS0,nBCSR_CS等)。CPLD通过这些信号与MSC8122主机处理器通信,被映射到处理器的内存或IO空间,从而实现软件(运行在MSC8122上)对硬件状态的控制和读取。 - 配置与状态输入:大量来自DIP开关(如
MODCK1s/MODCK2s用于从处理器时钟模式选择)、按钮(如Aborth_In主机NMI按钮)和板卡检测信号(如PCI_PRSNTbCPCI背板存在检测)。这些是系统的静态配置和动态事件输入。 - 复位与调试输出:这是代码的核心功能之一。产生给主机(
nHRESETh,nSRESETh)、从处理器(nHRESETs,nSRESETs)以及外部板卡(nHRST[1:3])的复位信号,以及调试请求信号(HEE0_Out,SEE0_Out)。 - 外设使能与配置输出:种类繁多,包括Flash写保护(
nBOOTPh/Ps)、RS232使能(nRS232EN_1/_2)、以太网PHY复位与模式选择(nFETH_RST,RMII2,BCSR_MSEL/MB)、CODEC使能(nCODEC_EN)、I2C总线连接控制(I2C_CONT)等。几乎板上所有主要外设的“开关”都由此CPLD控制。 - 指示灯(LED)驱动输出:如
SIGH_LED,SIGS_LED,RUNhLED等,用于可视化显示系统状态(如复位、运行、信号)。
这种将所有关键硬件控制信号收归一个可编程器件管理的做法,极大简化了主板布线,并通过软件可配置性提供了无与伦比的灵活性。例如,要改变以太网从RGMII模式切换到SGMII模式,可能只需要软件写一次CPLD内部的配置寄存器,而无需改动任何硬件跳线。
2.2 寄存器映射:软件与硬件的对话窗口
代码中定义了多个BCSRx(x=0,1,2,3,6,7,8,9,10)寄存器,这是软件控制硬件的关键。每个寄存器位都有明确的功能定义,例如:
BCSR0[0](FLASHPRT1):控制主机Flash的硬件写保护。BCSR1[5](FETH_RST):控制快速以太网PHY的复位。BCSR2[1](CNFG):配置从处理器的配置源(BCSR或Flash)。BCSR7[5](ETH_ON):全局以太网功能开关。BCSR9[0:3](ETH_MODE):4位编码,用于选择多达16种不同的以太网连接模式(如Mac to Mac RMII over DSI, Mac to Phy SMII等)。
软件通过向特定的内存地址(由nBCSR_CS和地址线A26-A29解码决定)进行读写操作,来修改或读取这些寄存器,从而间接控制所有连接到CPLD的硬件功能。这种“内存映射IO”的方式对软件工程师非常友好,他们可以像操作内存变量一样操作硬件。
2.3 状态机与定时逻辑:实现可靠的控制序列
代码中包含了至少两个关键的状态机/定时逻辑:
- 去抖定时器:在
always @ (posedge extclk)块中,使用计数器(nHRESETh_count,SEE0_count等)实现了对复位按钮和调试请求信号的去抖(Debounce)和延时。例如,HEE0_Out(主机调试请求)信号在HEE0_In输入变化后,会先产生一个短暂的高脉冲,然后跟随输入,但仅在主机硬复位释放(nHRESETh_d有效)后才真正有效。这避免了因按键抖动或毛刺导致的误触发,是工业级可靠性的体现。 - 以太网交换芯片访问状态机:在最后一个
always @(posedge clk)块中,定义了一个状态机(state,使用S0-S14状态编码)来控制对Mezzanine子板上以太网交换芯片的读写访问时序(ESW_RDb,ESW_WRb)。这确保了在高速处理器总线(如133MHz)下,能生成符合以太网交换芯片时序要求的读/写周期(如40ns),替代了处理器GPIO模拟时序可能的不稳定性。
实操心得:为什么需要CPLD做总线时序转换?处理器总线的时序是固定的、高速的,而许多外设芯片(尤其是老式的或低速的)需要的读写周期、建立保持时间与之不匹配。用CPLD/FPGA实现一个轻量级的“总线桥接”或“时序转换器”是常见做法。这片代码中的以太网状态机就是一个典型例子。它利用CPLD内部的寄存器,在检测到芯片选通(
!ESW_CSb)和读写信号(BCTL0sb)后,插入固定的等待周期(状态S7-S10或S11-S14),从而产生符合外设要求的波形。这种方法比用处理器软件延时更精确、更可靠,且不占用CPU资源。
3. 核心功能模块深度解析
3.1 复位管理子系统:系统稳定的基石
复位逻辑是这片代码中最复杂、最重要的部分之一。它不仅要处理上电复位(reset),还要处理来自按钮(HReseth_In,SReseth_In等)、软件寄存器(BCSR2[5:6]的HRST/SRST位)以及cPCI背板(nHRST_In,BHRESEThb)的多路复位源,并生成针对不同对象、具有恰当时序和驱动能力的复位信号。
assign nHRESETs = (RST_FROM_cPCI || (`HRST == `ACTIVE_LOW) || (HRESETs == `ASSERTED)) ? `ASSERTED : 1'bz; assign nPRSTs = ((`RECONF == `ACTIVE_LOW) || ((!PER_SLOT)&&(nHRESETh == `ASSERTED)) || SYS_SLOT && (BHRESEThb == `ASSERTED)) ? `ASSERTED : 1'bz;关键逻辑解读:
- 从处理器硬复位 (
nHRESETs):其触发条件是一个“或”逻辑。任何条件满足都会拉低复位:RST_FROM_cPCI:来自cPCI外设插槽的复位请求。HRST:软件通过写BCSR2[5]位请求的复位。HRESETs:来自HResets_In按钮,并经过去抖定时器处理后的信号。 这种设计提供了多种复位途径,方便调试和系统管理。
- 从处理器上电复位 (
nPRSTs):这个信号通常直接连接到处理器的PORESET引脚。其触发逻辑更复杂:RECONF:软件请求重新配置(可能用于引导模式切换)。(!PER_SLOT)&&(nHRESETh ==ASSERTED):当本板不在外设插槽且主机处于复位状态时,复位从处理器。这确保了主机启动期间从处理器保持静止。SYS_SLOT && (BHRESEThb ==ASSERTED):当本板在系统槽且从cPCI背板收到主机复位信号时,复位从处理器。这实现了cPCI系统的协同复位。 注意nPRSTs和nHRESETs的区别:前者是更根本的“上电复位”,后者是“硬复位”。有些处理器需要PORESET有更长的低电平时间。
注意事项:复位信号的电平与驱动代码中大量复位输出使用了1‘bz(高阻态)。这意味着CPLD的引脚被配置为双向(inout),当不主动驱动为低电平时,它呈现高阻。这通常是因为复位网络上有其他驱动源(如上电复位芯片、其他板卡),CPLD需要与之“线或”(Wire-OR)。在设计PCB时,必须确保这些复位网络有适当的上拉电阻,当所有驱动源都释放(高阻)时,复位信号能被上拉到无效的高电平。错误的上拉电阻值可能导致复位无法释放或毛刺。
3.2 以太网子系统动态配置
这是代码中另一个亮点,展示了CPLD如何实现复杂的、多模式的硬件连接切换。通过BCSR9[0:3](ETH_MODE)这4位寄存器,软件可以在多达14种以太网连接模式中动态切换。
always //@(`ETH_MODE or Activate_ETH) if (Activate_ETH) begin case (`ETH_MODE) 4'b0001: begin // Mac2Mac RMII DSI to ETH-SW BCSR_PPC_Sb = 1'b0;//DSI open BCSR_TDM_Sb = 1'b1;//8103 side closed BCSR_SMIIb = 1'b0;//Like SMII Mode BCSR_MSEL = 4'b0111;//25MHz to Host PHY,50MHz 8122 & ETH-SW BCSR_MB= 4'b0010;//50MHz from ETH-SW to 8122 BCSR_RSMIIb= 1'b1;//Not R/SMII PHY end ... 4'b1011: begin // Mac2Phy MII DSI to RPHY(MII) BCSR_PPC_Sb= 1'b0;//DSI open BCSR_TDM_Sb = 1'b1;//8103 side closed BCSR_SMIIb = 1'b1;//Not SMII Mode BCSR_MSEL = 4'b0001;//25MHz to Host PHY,25MHz From Osc. BCSR_MB= 4'b0011;//25MHz from PHY BCSR_RSMIIb= 1'b0;//R/SMII PHY end逻辑解析:
BCSR_PPC_Sb,BCSR_TDM_Sb,BCSR_SMIIb,BCSR_RSMIIb:这些信号控制着物理层数据通路上的模拟开关或多路复用器(MUX)。例如,BCSR_PPC_Sb为低时,打开DSI(DSP系统接口)到以太网交换芯片的通路。BCSR_MSEL和BCSR_MB:这组信号控制时钟网络的多路复用器(ETH-MUX1),选择不同的时钟源(25MHz, 50MHz, 125MHz)并路由到不同的目的地(主机PHY、从处理器、以太网交换芯片、外部PHY)。不同的以太网模式(RMII, MII, SMII)需要不同的时钟频率和拓扑。Activate_ETH:这是一个使能条件,由ETH_ON位、ETH_SW_ONDIP开关和板卡类型(!B_8102)共同决定。只有全局使能、以太网子板存在且非8102旧平台时,复杂的以太网模式切换逻辑才生效。
设计精妙之处:这种设计允许同一块硬件平台,通过软件配置,无缝地在“处理器间以太网直连”、“通过交换芯片组网”、“连接外部PHY芯片”等多种应用场景间切换。无需更换硬件或跳线,极大地提升了开发板的评估灵活性和产品原型的适应性。对于通信设备研发,这种“硬件可重构”思想非常宝贵。
3.3 总线仲裁与地址解码
CPLD还承担了部分简单的总线仲裁和地址空间解码功能,管理着本地外设和cPCI背板外设的访问。
assign OFF_BRD_SLAVE = !CS6hb || (SYS_SLOT && ((SSEL != ON_BRD_SLAVE_ADDR) && !nHCS1)) && nHBCS; assign nDBUFBEN = nHCS1 && nHBCS && (!(ON_BRD_HOST_PRPH || OFF_BRD_SLAVE || VB_ACCESS)); assign nDBUFXEN = nHCS1 && nHBCS && (!(OFF_BRD_SLAVE || VB_ACCESS));逻辑解析:
OFF_BRD_SLAVE:判断当前访问是否针对cPCI背板上的“板外”从设备。条件包括:主机CS6信号有效,或者本板处于系统槽且地址选择(SSEL)非本板地理地址(ON_BRD_SLAVE_ADDR)且主机芯片选择1(nHCS1)有效。nDBUFBEN和nDBUFXEN:这两个信号控制着连接外部总线的数据缓冲器(Buffer)的使能。nDBUFBEN控制通往背板的总线缓冲,nDBUFXEN控制其他外部总线缓冲。它们的使能逻辑确保了在任何时刻,只有一个主设备(本地主机或外部主机)驱动共享的数据总线,防止了总线冲突。这是多主系统(如cPCI)中必备的电气隔离机制。
避坑指南:总线冲突与缓冲器方向在实际PCB设计中,连接到双向数据总线的缓冲器(如74LVTH162245)必须正确管理方向控制信号(DIR)。这片CPLD代码通过
EXT_PCI1和EXT_PCI信号来控制方向。EXT_PCI1的逻辑是:当访问板外从设备且为读操作时,或者在本板为外设插槽且为写操作时,方向设置为从外部到内部(输入)。EXT_PCI则简单地在非外设插槽时设置为输入。如果方向控制逻辑错误,会导致总线锁死、数据损坏,是最难调试的硬件问题之一。在调试此类系统时,用逻辑分析仪同时抓取片选、读写、方向和总线数据波形是必须的。
4. 关键代码段与实现细节剖析
4.1 寄存器读写与上电初始化
CPLD内部寄存器的读写操作是软件交互的基础。代码中通过两个主要的always块和组合逻辑assign来实现。
寄存器读取(软件读操作):
always @ (CFG_ADDR or READ_CFG_FROM_BCSR or BCSR_ADDR or Read_from_BCSR) if(READ_CFG_FROM_BCSR) case (CFG_ADDR) //读取主机硬配置字(HRCW) 2'b00 : DataO[0:7] = CFG_BYTE0_DEF; ... endcase else if(Read_from_BCSR) case (BCSR_ADDR) //读取BCSR寄存器 0 : DataO[0:7] = BCSR0[0:7]; 1 : DataO[0:7] = BCSR1r[0:7]; //注意BCSR1r是实时状态组合逻辑 ... endcase else DataO[0:7] = 8'bz; //非读周期,数据线高阻READ_CFG_FROM_BCSR逻辑允许主机在特定条件下(nFCFG == From_BCSR)从CPLD读取启动配置字,而不是从Flash读取。这为调试和特殊启动模式提供了可能。BCSR1r和BCSR2r是组合逻辑生成的“实时视图”,反映了按钮和DIP开关的即时状态,而非寄存器缓存值,这保证了状态读取的实时性。
寄存器写入与上电初始化(软件写操作与硬件复位):
always @ (posedge nWE or negedge nHRESETh) begin if(nHRESETh == `ASSERTED) begin // 主机硬复位有效时,初始化所有寄存器 BCSR0[0:7] = BCSR0_PON; BCSR1[0:7] = BCSR1_PON; ... end else begin // 主机正常运行期间,处理写操作 if (Write_to_BCSR) case (BCSR_ADDR) 0: begin //写BCSR0,但FLASHPRT位受FLUNLCK位保护 if(`FLUNLCK1 == `ASSERTED) `FLASHPRT1 = Data[0]; else `FLASHPRT1 = `ASSERTED; //未解锁则强制写保护 ... end 6: begin //写BCSR6,只有MARK位为1时才更新TEST相关位 BCSR6[0:1] = Data[0:1]; if (Data[7] == 1) BCSR6[2:6] = Data[2:6]; end ... endcase end end关键点分析:
- 异步复位/置位:寄存器初始化由
negedge nHRESETh(主机硬复位下降沿)触发,这是典型的异步复位设计,确保系统从一个确定的初始状态开始。 - 写保护机制:
BCSR0的Flash写保护位(FLASHPRT1/2)的修改,需要对应的解锁位(FLUNLCK1/2)先被置位。这是一个简单的硬件安全特性,防止软件意外或恶意地关闭Flash写保护。BCSR6的测试模式相关位(TESTSIG)的修改也需要MARK位为1,这增加了进入测试模式的门槛。 - 参数化初始值:所有
BCSRx_PON都是parameter定义的常量。这使得初始状态一目了然,且易于根据不同的硬件版本或需求进行修改。例如,RS232EN_2_PON = 1‘b0表示从处理器侧的RS232默认是使能的。
4.2 时钟分频与延时生成
代码中使用了多个计数器来实现时钟分频和长延时,用于去抖和产生满足时序要求的脉冲。
parameter div2st = 5; // 2^5 = 32个extclk周期延时 parameter div1st = 13; // 2^13 = 8192个extclk周期延时 reg [divc:0] counter; reg clock_divider; always @ (posedge extclk) begin if (reset == `ASSERTED) counter = (1 << div1st) - 2; // 复位时置位 counter = counter + 1; if (counter[div1st] != 1) clock_divider = 0; else clock_divider = 1; // 当counter最高位为1时,clock_divider翻转 end这段代码生成了一个频率为extclk / 2^(div1st+1)的慢速时钟clock_divider。div1st=13,假设extclk=20MHz,则clock_divider周期约为 2^14 / 20MHz = 819.2 us。这个慢时钟被用于按钮去抖的定时器(hhtime_elaps等函数),因为去抖通常需要10-20ms的延时,用extclk直接计数会需要非常大的计数器位宽,用分频后的时钟可以节省逻辑资源。
参数化设计的优势:div1st,div2st,divhreseth等延时参数都定义为parameter。这意味着如果需要调整去抖时间或复位脉冲宽度,只需要修改头部的参数定义,而不需要深入修改多个计数器逻辑,提高了代码的可维护性。
4.3 地理地址(GA)锁存与板卡识别
在cPCI多板卡系统中,每块板卡需要一个唯一的地理地址(Geographic Address)来进行寻址。
always @ (posedge nHRESETs) GAr[0:3] = GA[0:3]; //*** Latch GA lines ***//这条语句在从处理器硬复位nHRESETs的上升沿,将来自cPCI背板连接器的GA[0:3]信号锁存到内部寄存器GAr[0:3]中。为什么要在复位上升沿锁存?因为cPCI背板的GA信号在系统上电初期可能不稳定,选择在复位释放(系统趋于稳定)的时刻进行锁存,可以确保读取到正确的、稳定的地理地址。这个地址随后被用于ON_BRD_SLAVE_ADDR,在总线解码中判断访问是否针对本板。
5. 调试、测试与常见问题排查
基于这片CPLD代码进行开发或调试时,会遇到一些典型问题。以下是根据实际工程经验整理的排查思路和技巧。
5.1 常见问题速查表
| 问题现象 | 可能原因 | 排查步骤与解决方法 |
|---|---|---|
| 系统无法启动,处理器无反应 | 1. 核心复位信号异常。 2. 启动配置(Boot Mode)错误。 3. Flash无法访问(写保护使能)。 | 1. 用示波器测量nHRESETh、nPRSTs等关键复位信号。检查CPLD代码中复位逻辑的触发条件(按钮、软件寄存器、背板信号)是否意外满足。2. 检查 BM_Out[0:2]输出信号,是否与处理器启动模式选择引脚匹配。确认DSItoSYSDIP开关和BCSR2寄存器CNFG位设置是否正确。3. 测量Flash的片选( nFCSh)和写保护(nBOOTPh/Ps)信号。检查BCSR0的FLASHPRT位和BCSR6的FLUNLCK位状态。 |
| 以太网功能不工作或模式不对 | 1. 以太网全局未使能(ETH_ON)。2. 时钟配置错误。 3. 数据通路MUX开关未正确切换。 | 1. 确认BCSR7[5](ETH_ON)为1,且ETH_SW_ONDIP开关闭合,板卡识别正确(非B_8102)。2. 用示波器测量连接到PHY和交换芯片的时钟信号(如25M/50M/125M)。对照 BCSR_MSEL和BCSR_MB在特定ETH_MODE下的值,检查CPLD输出是否正确。3. 测量 BCSR_PPC_Sb,BCSR_TDM_Sb等控制模拟开关的信号电平,确认其与目标模式相符。 |
| cPCI板卡间通信失败 | 1. 总线缓冲器方向控制错误。 2. 地理地址冲突或解码错误。 3. 复位网络冲突。 | 1. 在访问周期内,用逻辑分析仪同时抓取nHCS1,nHBCS,nR_W,EXT_PCI1,EXT_PCI以及数据总线。检查方向信号在读写周期是否及时、正确地切换。2. 确认背板GA跳线设置,并测量CPLD锁存后的 GAr值。检查OFF_BRD_SLAVE逻辑计算是否正确。3. 检查 nHRST网络,确认cPCI系统槽和外设槽的复位驱动与上拉电阻配置正确,无总线争用。 |
| LED指示灯状态异常 | 1. LED使能被关闭(LEDEN)。2. 驱动信号源错误。 3. CPLD引脚配置或驱动能力不足。 | 1. 检查BCSR6[2](LEDEN)位,确保其为0(使能)。2. LED驱动信号如 SIGH_LED由SIGNALH0和nSRESETh等信号共同决定。确认你期望显示的状态对应的源信号是否正常。3. 检查CPLD的引脚分配(.pin文件),确认LED相关引脚被正确分配为输出。对于驱动多个LED或高亮度LED,确认CPLD的IO驱动电流是否足够,必要时增加外部驱动电路。 |
| 软件读写BCSR寄存器失败 | 1. 地址解码错误。 2. 读写时序不满足。 3. CPLD全局时钟或复位不稳定。 | 1. 确认软件访问的地址与nBCSR_CS和A26-A29的解码逻辑匹配。用逻辑分析仪验证片选和地址线。2. CPLD代码中的读写路径是纯组合逻辑,理论上延迟很小。但需确保处理器总线周期满足CPLD的 tSU/tH要求。在高速总线(如133MHz)下,可能需要检查时序约束(.sdc文件)。3. 测量CPLD的 clk和reset输入信号质量,确保无毛刺、抖动在容限内。 |
5.2 内部测试与调试功能
代码中预留了内部测试接口(Internal_Test),虽然在此版本中通过assign Internal_Test = 0;被硬关闭,但其设计思路值得借鉴。
TEST_EN和TEST_SIG[1:3]:当BCSR6[3](TEST)位被置位且MARK位也为1时,TEST_EN输出有效,可将处理器置于测试模式,TEST_SIG用于选择具体的测试模式。这为工厂生产测试或深度硬件诊断提供了钩子。SPR1(Spare DIP-SW):被注释为仅用于内部测试。在实际项目中,可以保留1-2个这样的通用输入引脚,连接到测试点或跳线,用于临时性的调试功能控制(如强制某个信号为高/低),这比重新编译、烧录CPLD代码要快捷得多。
5.3 版本管理与兼容性
代码开头注释//BCSR2_main Rev 012 for 8122ADS board以及参数BCSR_ver = 3‘b010都表明了版本信息。在大型或长期项目中,CPLD代码的版本管理至关重要。
- 参数化版本号:将版本号定义为
parameter,并可能通过未使用的寄存器位(如BCSR5的部分位)反馈给软件,这样软件可以读取并识别当前运行的CPLD版本,从而做出兼容性处理。 - 平台检测:代码通过
B_8102,B_8122_1,B_8122_2,B_8126等输入信号来检测硬件平台版本。这使得同一份代码可以适配略有不同的硬件变体,通过条件编译或运行时判断来启用或禁用特定功能(如以太网功能仅在8122/26平台上使能)。
个人实操心得:版本控制与回归测试每次修改CPLD代码后,除了功能仿真,必须进行严格的回归测试。重点测试那些“看似无关”的基础功能,特别是复位序列、电源使能时序和总线仲裁逻辑。因为一个小的改动可能会意外影响这些关键路径。建议建立一个简单的测试台(Testbench),模拟上电、按钮、软件读写等场景,并保存黄金波形(Golden Waveform)作为比对基准。对于MSC8122ADS这样复杂的平台,如果条件允许,搭建一个自动化的硬件在环测试环境是值得的,可以自动验证所有外设控制功能是否在修改后依然正常。