Broadcom LED Interface

系统中使用了 Broadcom 的 BCM56960 交换芯片,其中包含了三个 LED 接口,现对此接口的工作原理及具体实现总结如下。其它型号的交换芯片大同小异。

BCM56960 包含三个独立的 LED 接口:LED-0、LED-1 和 LED-2。

  • LED-0 提供 pipe0(端口 1~32)和 pipe3(端口 97~128)的端口状态。
  • LED-1 提供 pipe1(端口 33~64)和 pipe2(端口 65~96)的端口状态。
  • LED-2 提供管理口(129~132)的端口状态。

其中的端口状态信息包括 link 状态、发送接收 activity 和速率设置。

每个 LED 接口包含以下四个子模块:

  • LED 链式连接(Chain Connection):位于端口级别,以链式输出端口状态。
  • CMIC LED 功能:将链式连接输出的端口状态重新排序(如有必要)并保存在 LED_DATA_RAM 中。
  • LED 处理器:包括程序 RAM 和数据 RAM,程序 RAM 中的程序使用 LED 处理器汇编语言编写,将位于 CMIC 的端口状态处理后保存在数据 RAM 中。
  • LED 串行接口:包括两个管脚:LED_CLKLED_DATA,通过这两个管脚将端口状态串行输出。

下面分别介绍这四个子模块。

LED 链式连接

芯片包含三个独立的 LED 链:LED-0、LED-1 和 LED-2,用于处理 pipe0、pipe1、pipe2 和 pipe3 的端口状态。输出的端口状态发送到 CMIC 模块的 LED 输入接口。受限于物理实现,LED 链的端口连接不是按数字升序排列;实际输出的信号顺序取决于各个链的实际端口连接。为了简化 LED 处理,CMIC 模块将 LED 链输出的信号顺序以升序重新映射后存入 LED_DATA_RAM 中。三个 LED 链输出的端口状态顺序如下:

  • LED-0:32~1, 128~97
  • LED-1:33~64, 65~96(即 33~96)
  • LED-2:未使用的端口 0,131,未使用的端口 2,129

需要注意的是,在 LED-2 中,虽然有两个未使用的端口,但 LED 链输出给 CMIC 的信号仍然包含了这两个端口,即一共有四个端口状态的信号输出给 CMIC 模块。

CMIC LED 功能

CMIC 模块包含了四个步骤:

  1. 三个 LED 链将各个 pipe 的端口状态输出到 LED 控制逻辑(上图步骤 1 只显示了一个链)。
  2. LED 控制逻辑将端口状态移到 LED_DATA_RAM 的端口状态区(Status from Ports area),每个端口的状态信息共两个字节(16 bits),各个 bit 定义详见下文。此步骤由硬件完成,LED 程序不参与。例如,LED-0 链包含 64 个端口,则 LED_DATA_RAM 的最低 128 字节(0x00~0x7F)用于存放这 64 个端口的状态。CMIC 提供了重映射寄存器,用于将端口状态重新排序。
  3. 使用 SDK 提供的软件编写的 LED 程序位于 LED_PROGRAM_RAM 中,LED 程序对步骤 2 的端口状态处理之后将处理结果存放在 LED_DATA_RAM 的扫描链组装区(Scan Chain Assembly Area)。LED 程序将确定从 LED 串行端口输出 LED 状态的哪些 bit 位及输出顺序。
  4. CMIC 模块根据 LED 程序的指令,将 LED_DATA_RAM 扫描链组装区的 LED 状态输出到 LED 串行接口中。

LED 状态编码

各个端口包含的两字节(16 bits)LED 状态编码如下:

LED 重映射寄存器

重映射寄存器的设置比较特殊,与手册上的描述有出入,需要特别注意。如 CMIC_LEDUP0_PORT_ORDER_REMAP_0_3 寄存器,包含四个字段:REMAP_PORT_0REMAP_PORT_1REMAP_PORT_2REMAP_PORT_3。其中 REMAP_PORT_N 中的 N 表示的是在 LED 链中原始输出的端口索引。以 LED-0 为例,原始输出顺序如下:

32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17,
16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
128, 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113,
112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97

所以:

  • REMAP_PORT_0 指的是 Port 32
  • REMAP_PORT_31 指的是 Port 1
  • REMAP_PORT_32 指的是 Port 128
  • REMAP_PORT_63 指的是 Port 97

同样地,各个字段的值也表示对应端口的状态需要重新映射到新的输出顺序的索引值。例如:

  • REMAP_PORT_0 = 31 表示 Port 32 重新映射到第 31 个位置(从 0 开始计数,下同)。
  • REMAP_PORT_31 = 0 表示 Port 1 重新映射到第 0 个位置。
  • REMAP_PORT_32 = 63 表示 Port 128 重新映射到第 63 个位置。
  • REMAP_PORT_63 = 32 表示 Port 97 重新映射到第 32 个位置。

这样配置之后,重新映射后的端口顺序如下:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128

LED-1 和 LED-2 的重映射寄存器配置方法相同,不再赘述。

LED 处理器

LED 处理器是整个 LED 接口的核心,也是四个子模块中面向软件驱动需要花费最多时间的部分。

LED_CTRL 寄存器(CMIC_LEDUP0_CTRLCMIC_LEDUP1_CTRLCMIC_LEDUP2_CTRL)包含一个 LEDUP_EN 控制位,在芯片复位时此控制位清零,使 LED 控制器暂停运行;程序 RAM 初始化完成后,必须将此控制位设置为 1 使 LED 控制器开始运行。LED_CTRL 寄存器另有两个延时字段,LEDUP_SCAN_INTRA_PORT_DELAYLEDUP_SCAN_START_DELAY,用于控制 LED 扫描链中的延时。这两个字段由硬件设置,不可修改。

LED_STATUS 寄存器(CMIC_LEDUP0_STATUSCMIC_LEDUP1_STATUSCMIC_LEDUP2_STATUS)是只读寄存器。最低 8 bits PROGRAM_COUNTER 包含 LED 处理器的程序计数器,在对汇编程序进行调试时非常有用。Bit 8 LEDUP_RUNNING 标识处理器是否正在运行。bit 9 LEDUP_INITIALISING 标识处理器是否正在初始化,当处理器最开始在 LED 串行接口输出 300 个 0 时有效(值为 1)。

BCM56960 有三个 LED 处理器,每个处理器包含 256 字节程序 RAM 和 256 字节数据 RAM。每个字节寻址时以 4 递增,这一点在 SDK 的 LED 回调函数中会使用到。主控端可在任意时刻读写这两个 RAM,但需要在改写程序 RAM 之前将 LED 处理器禁用,改写完成后再重新启用。

LED 处理器架构

LED 处理器是 8 位处理器:寄存器是 8 位宽,内存、程序计数器及所有的地址也都是 8 位宽。程序空间和数据空间是分离的,深度都是 1024 字节。因此,尽管 LED 处理器可以读写数据 RAM(LED_DATA_RAM)的任意字节,但只有主控端可以修改程序 RAM(LED_PROGRAM_RAM)。

注意:尽管程序空间和数据空间的深度都是 1024 字节,但地址只有 256 个,每个地址递增 4,因此地址是 0、4、8 一直到 1020。这样就与两个 256 字节的 LED_PROGRAM_RAMLED_DATA_RAM 对应起来了,地址 0 对应 RAM 的字节 0,地址 4 对应 RAM 的字节 1,依此类推,地址 1020 对应 RAM 的字节 255。

可从以下实际运行中的寄存器 CMIC_LEDUP0_PROGRAM_RAM 打印核对:

CMIC_LEDUP0_PROGRAM_RAM(0).cmic0[9][0x20800]=2: 
CMIC_LEDUP0_PROGRAM_RAM(1).cmic0[9][0x20804]=0: 
CMIC_LEDUP0_PROGRAM_RAM(2).cmic0[9][0x20808]=0x28: 
CMIC_LEDUP0_PROGRAM_RAM(3).cmic0[9][0x2080c]=0x60: 
CMIC_LEDUP0_PROGRAM_RAM(4).cmic0[9][0x20810]=0xe1: 
CMIC_LEDUP0_PROGRAM_RAM(5).cmic0[9][0x20814]=0x67: 
CMIC_LEDUP0_PROGRAM_RAM(6).cmic0[9][0x20818]=0x13: 
CMIC_LEDUP0_PROGRAM_RAM(7).cmic0[9][0x2081c]=6: 
CMIC_LEDUP0_PROGRAM_RAM(8).cmic0[9][0x20820]=0xe1: 
CMIC_LEDUP0_PROGRAM_RAM(9).cmic0[9][0x20824]=0xf2: 
CMIC_LEDUP0_PROGRAM_RAM(10).cmic0[9][0x20828]=4: 
CMIC_LEDUP0_PROGRAM_RAM(11).cmic0[9][0x2082c]=0xd2: 
CMIC_LEDUP0_PROGRAM_RAM(12).cmic0[9][0x20830]=0x40: 
CMIC_LEDUP0_PROGRAM_RAM(13).cmic0[9][0x20834]=0x74: 
CMIC_LEDUP0_PROGRAM_RAM(14).cmic0[9][0x20838]=2: 
CMIC_LEDUP0_PROGRAM_RAM(15).cmic0[9][0x2083c]=0x86: 
CMIC_LEDUP0_PROGRAM_RAM(16).cmic0[9][0x20840]=0xe0: 
CMIC_LEDUP0_PROGRAM_RAM(17).cmic0[9][0x20844]=0x3a: 
CMIC_LEDUP0_PROGRAM_RAM(18).cmic0[9][0x20848]=0x10: 
CMIC_LEDUP0_PROGRAM_RAM(19).cmic0[9][0x2084c]=6: 
CMIC_LEDUP0_PROGRAM_RAM(20).cmic0[9][0x20850]=0xe1: 
CMIC_LEDUP0_PROGRAM_RAM(21).cmic0[9][0x20854]=0x67: 
CMIC_LEDUP0_PROGRAM_RAM(22).cmic0[9][0x20858]=0x42: 
CMIC_LEDUP0_PROGRAM_RAM(23).cmic0[9][0x2085c]=0x71: 
CMIC_LEDUP0_PROGRAM_RAM(24).cmic0[9][0x20860]=0x2a: 
CMIC_LEDUP0_PROGRAM_RAM(25).cmic0[9][0x20864]=0x80: 
CMIC_LEDUP0_PROGRAM_RAM(26).cmic0[9][0x20868]=0x67: 
CMIC_LEDUP0_PROGRAM_RAM(27).cmic0[9][0x2086c]=0x42: 
CMIC_LEDUP0_PROGRAM_RAM(28).cmic0[9][0x20870]=0x71: 
CMIC_LEDUP0_PROGRAM_RAM(29).cmic0[9][0x20874]=0x2a: 
CMIC_LEDUP0_PROGRAM_RAM(30).cmic0[9][0x20878]=0x80: 
CMIC_LEDUP0_PROGRAM_RAM(31).cmic0[9][0x2087c]=0x67: 
CMIC_LEDUP0_PROGRAM_RAM(32).cmic0[9][0x20880]=0x42: 
CMIC_LEDUP0_PROGRAM_RAM(33).cmic0[9][0x20884]=0x71: 
CMIC_LEDUP0_PROGRAM_RAM(34).cmic0[9][0x20888]=0x2a: 
CMIC_LEDUP0_PROGRAM_RAM(35).cmic0[9][0x2088c]=0x80: 
CMIC_LEDUP0_PROGRAM_RAM(36).cmic0[9][0x20890]=0x67: 
CMIC_LEDUP0_PROGRAM_RAM(37).cmic0[9][0x20894]=0x42: 
CMIC_LEDUP0_PROGRAM_RAM(38).cmic0[9][0x20898]=0x71: 
CMIC_LEDUP0_PROGRAM_RAM(39).cmic0[9][0x2089c]=0x2a: 
CMIC_LEDUP0_PROGRAM_RAM(40).cmic0[9][0x208a0]=0x75: 
CMIC_LEDUP0_PROGRAM_RAM(41).cmic0[9][0x208a4]=0x56: 
CMIC_LEDUP0_PROGRAM_RAM(42).cmic0[9][0x208a8]=0xe2: 
CMIC_LEDUP0_PROGRAM_RAM(43).cmic0[9][0x208ac]=3: 
CMIC_LEDUP0_PROGRAM_RAM(44).cmic0[9][0x208b0]=0x67: 
CMIC_LEDUP0_PROGRAM_RAM(45).cmic0[9][0x208b4]=0x4e: 
CMIC_LEDUP0_PROGRAM_RAM(46).cmic0[9][0x208b8]=0x71: 
CMIC_LEDUP0_PROGRAM_RAM(47).cmic0[9][0x208bc]=0x5e: 
CMIC_LEDUP0_PROGRAM_RAM(48).cmic0[9][0x208c0]=0x80: 
CMIC_LEDUP0_PROGRAM_RAM(49).cmic0[9][0x208c4]=0x67: 
CMIC_LEDUP0_PROGRAM_RAM(50).cmic0[9][0x208c8]=0x4e: 
CMIC_LEDUP0_PROGRAM_RAM(51).cmic0[9][0x208cc]=0x71: 
CMIC_LEDUP0_PROGRAM_RAM(52).cmic0[9][0x208d0]=0x5e: 
CMIC_LEDUP0_PROGRAM_RAM(53).cmic0[9][0x208d4]=0x80: 
CMIC_LEDUP0_PROGRAM_RAM(54).cmic0[9][0x208d8]=0x67: 
CMIC_LEDUP0_PROGRAM_RAM(55).cmic0[9][0x208dc]=0x4e: 
CMIC_LEDUP0_PROGRAM_RAM(56).cmic0[9][0x208e0]=0x71: 
CMIC_LEDUP0_PROGRAM_RAM(57).cmic0[9][0x208e4]=0x5e: 
CMIC_LEDUP0_PROGRAM_RAM(58).cmic0[9][0x208e8]=0x80: 
CMIC_LEDUP0_PROGRAM_RAM(59).cmic0[9][0x208ec]=0x67: 
CMIC_LEDUP0_PROGRAM_RAM(60).cmic0[9][0x208f0]=0x4e: 
CMIC_LEDUP0_PROGRAM_RAM(61).cmic0[9][0x208f4]=0x71: 
CMIC_LEDUP0_PROGRAM_RAM(62).cmic0[9][0x208f8]=0x5e: 
CMIC_LEDUP0_PROGRAM_RAM(63).cmic0[9][0x208fc]=0x67: 
CMIC_LEDUP0_PROGRAM_RAM(64).cmic0[9][0x20900]=0x5a: 
CMIC_LEDUP0_PROGRAM_RAM(65).cmic0[9][0x20904]=0x57: 
CMIC_LEDUP0_PROGRAM_RAM(66).cmic0[9][0x20908]=0x12: 
CMIC_LEDUP0_PROGRAM_RAM(67).cmic0[9][0x2090c]=0xa0: 
CMIC_LEDUP0_PROGRAM_RAM(68).cmic0[9][0x20910]=0xf8: 
CMIC_LEDUP0_PROGRAM_RAM(69).cmic0[9][0x20914]=0x15: 
CMIC_LEDUP0_PROGRAM_RAM(70).cmic0[9][0x20918]=0x1a: 
CMIC_LEDUP0_PROGRAM_RAM(71).cmic0[9][0x2091c]=0: 
CMIC_LEDUP0_PROGRAM_RAM(72).cmic0[9][0x20920]=0x57: 
CMIC_LEDUP0_PROGRAM_RAM(73).cmic0[9][0x20924]=0x28: 
CMIC_LEDUP0_PROGRAM_RAM(74).cmic0[9][0x20928]=0x32: 
CMIC_LEDUP0_PROGRAM_RAM(75).cmic0[9][0x2092c]=7: 
CMIC_LEDUP0_PROGRAM_RAM(76).cmic0[9][0x20930]=0x97: 
CMIC_LEDUP0_PROGRAM_RAM(77).cmic0[9][0x20934]=0x57: 
CMIC_LEDUP0_PROGRAM_RAM(78).cmic0[9][0x20938]=0x28: 
CMIC_LEDUP0_PROGRAM_RAM(79).cmic0[9][0x2093c]=0x32: 
CMIC_LEDUP0_PROGRAM_RAM(80).cmic0[9][0x20940]=0: 
CMIC_LEDUP0_PROGRAM_RAM(81).cmic0[9][0x20944]=0x32: 
CMIC_LEDUP0_PROGRAM_RAM(82).cmic0[9][0x20948]=1: 
CMIC_LEDUP0_PROGRAM_RAM(83).cmic0[9][0x2094c]=0xb7: 
CMIC_LEDUP0_PROGRAM_RAM(84).cmic0[9][0x20950]=0x97: 
CMIC_LEDUP0_PROGRAM_RAM(85).cmic0[9][0x20954]=0x57: 
CMIC_LEDUP0_PROGRAM_RAM(86).cmic0[9][0x20958]=0x32: 
CMIC_LEDUP0_PROGRAM_RAM(87).cmic0[9][0x2095c]=0xe: 
CMIC_LEDUP0_PROGRAM_RAM(88).cmic0[9][0x20960]=0x87: 
CMIC_LEDUP0_PROGRAM_RAM(89).cmic0[9][0x20964]=0x57: 
CMIC_LEDUP0_PROGRAM_RAM(90).cmic0[9][0x20968]=0x32: 
CMIC_LEDUP0_PROGRAM_RAM(91).cmic0[9][0x2096c]=0xf: 
CMIC_LEDUP0_PROGRAM_RAM(92).cmic0[9][0x20970]=0x87: 
CMIC_LEDUP0_PROGRAM_RAM(93).cmic0[9][0x20974]=0x57: 
CMIC_LEDUP0_PROGRAM_RAM(94).cmic0[9][0x20978]=0x16: 
CMIC_LEDUP0_PROGRAM_RAM(95).cmic0[9][0x2097c]=0xe0: 
CMIC_LEDUP0_PROGRAM_RAM(96).cmic0[9][0x20980]=0xca: 
CMIC_LEDUP0_PROGRAM_RAM(97).cmic0[9][0x20984]=0xa: 
CMIC_LEDUP0_PROGRAM_RAM(98).cmic0[9][0x20988]=0x74: 
CMIC_LEDUP0_PROGRAM_RAM(99).cmic0[9][0x2098c]=0x5a: 
CMIC_LEDUP0_PROGRAM_RAM(100).cmic0[9][0x20990]=0x77: 
CMIC_LEDUP0_PROGRAM_RAM(101).cmic0[9][0x20994]=0x56: 
CMIC_LEDUP0_PROGRAM_RAM(102).cmic0[9][0x20998]=0x57: 
CMIC_LEDUP0_PROGRAM_RAM(103).cmic0[9][0x2099c]=0: 
...
CMIC_LEDUP0_PROGRAM_RAM(255).cmic0[9][0x20bfc]=0: 

从中可以看出,地址 0x20800 对应字节 0,地址 0x20804 对应字节 1,依此类推,地址 0x20bfc 对应字节 255。

寄存器集

LED 处理器包含以下寄存器:

  • 两个累加寄存器 A 和 B: 用于指令的源操作数、目的操作数、内存寻址等。
  • 状态标志 C 和 Z: C 为进位标志,当指令操作导致 ALU 溢出(进位或借位)时置位;Z 为零标志,当指令操作结果为 0x00 时置位。
  • T 栈:4 级深、1 bit 宽,用于构建 LED 数据流。这是构建 LED 状态的串行数据最重要的部分。

寻址模式

LED 处理器包含 4 种寻址模式(注意:不是所有指令都支持全部 4 种寻址模式):

  • 寄存器寻址:例如 AB 等寄存器的值。
  • 立即数寻址:例如 30x21label(3<<2)+1 等 8 bit 的立即数。
  • 间接寻址:例如 (A)(B) 等寄存器的值指向的数据 RAM 的值。
  • 绝对寻址:例如 (3)(0x21)(label)((3<<2)+1) 等 8 bit 立即数指向的数据 RAM 的值。

子程序

与标准微处理器不同,LED 处理器没有使用数据内存实现 call/return 栈,而是使用寄存器实现了一个 2 级深度的返回地址栈(注意不要与 T 栈混淆)。

每次调用 CALL 指令时,下一条指令地址会压入返回地址栈。当有新的地址入栈时,最老的地址将被丢弃!且没有任何硬件异常!本人就在这个坑里周旋了很久。在编写汇编代码时,不能有超过两级嵌套的 CALL 调用,否则将无法返回两级之前的子程序中,必要时可将子程序展开多次实现相应的功能。

每次调用 RET 指令时,栈顶的地址弹出到程序计数器(PC),栈底使用 0xFE 填充。这样处理的好处是,可以在 0xFE 处设置一个 trap 以捕获程序异常,当过多的 RET 指令执行时将触发此异常。

指令集

指令的长度为一字节或两字节。不管指令是一字节还是两字节,每个指令都需要六个 CORECLK 时钟周期完成执行,即使指令需要访问两次数据内存也是如此。

下面介绍几个 LED 处理器特有的指令。

  • PORT:用于指定 PUSHST 指令将对哪个端口生效。
  • PUSHST:将 PORT 指令指定的端口对应的两个字节端口状态的特定 bit 位取出并压入 T 栈。
  • POP:将 T 栈顶部的一个数值弹出到 C 标志位。
  • TAND TOR TXOR TINV:前三个指令将 T 栈顶部两个数值弹出并做相应的 bit 位运算后,将结果再压入 T 栈。TINV 指令对 T 栈顶部一个数值做补码运算。
  • PACK:将 T 栈顶部的一个数值弹出到扫描链组装区(即 LED_DATA_RAM 偏移 128 字节开始)的下一个空位。扫描链组装区中数值的排列顺序就是 LED 串行接口上输出的顺序,因此需要特别注意 PACK 指令的执行顺序。
  • SEND:当 SEND 指令执行时,LED 处理器暂停执行直到下一个 LED 刷新周期。此时硬件将扫描链组装区中的数值依次输出到 LED 串行接口上。
  • JT JNT:用于测试 T 栈顶部的一个数值。

需要说明的是,LED 扫描链最长可以有 255 bits,需要 32 个字节,即数据 RAM 0x80~0x9F(之前的 0x00~0x7F 为端口状态区),但实际汇编代码中假如只使用了更短的 LED 扫描链,而后面的数据 RAM 可以自由使用。

LED 处理器工具

编写好 LED 汇编代码后,需要使用 SDK 提供的工具进行编译。

首先,汇编器本身也需要编译:

$ cd tools/led/tools/
$ make ledasm

编译完成后会在 tools/led/tools 目录下生成 ledasm 就是汇编器了。然后对 LED 汇编代码进行汇编:

$ cd ../example/
$ ../tools/ledasm usa-link-act

注意:汇编源文件名为 usa-link-act.asm,而汇编时使用 usa-link-act 即可,不需要加后缀。汇编器将自动将对应的 .asm 文件汇编生成同名的 .lst .hex .c 文件,其中 .lst 文件是中间过程文件,对于调试有帮助;.hex 文件是汇编好的二进制文件,可用于 SDK 命令行方式加载;.c 文件是对应的 C 语言数组实现,用于在 C 代码中加载。

除了汇编器 ledasm 之外,tools/led/tools 目录下还有反汇编器 leddasm 和模拟器 ledsim 等,编译及使用方法类似。模拟器 ledsim 对于理解 LED 处理器的工作原理非常有帮助,强烈推荐好好把玩一番。

系统中使用的汇编代码如下,实现的功能是:每四个端口共用一个 LED 灯,这四个端口全部 link down 时 LED 灯常灭;至少有一个端口 link up 时 LED 灯常亮;至少有一个端口有收发 activity 时 LED 以 70ms 亮、70ms 灭的频率闪烁(汇编中只实现相应计数的亮或灭,具体的亮灭保持时长将在「LED 串行接口」中说明)。由前文介绍的每个端口两字节的状态编码信息可知,无法从中获取到 link 状态,因此汇编代码中读取从 LED_DATA_RAM 0xA0 开始的空间,将 bit0 作为各个端口的 link 状态,此状态将在下文介绍的 SDK 回调函数中更新。

;
; $Id: sdk56960x.asm,v 1.0 Broadcom SDK $
;
; $Copyright: Copyright 2016 Broadcom Corporation.
; This program is the proprietary software of Broadcom Corporation
; and/or its licensors, and may only be used, duplicated, modified
; or distributed pursuant to the terms and conditions of a separate,
; written license agreement executed between you and Broadcom
; (an "Authorized License").  Except as set forth in an Authorized
; License, Broadcom grants no license (express or implied), right
; to use, or waiver of any kind with respect to the Software, and
; Broadcom expressly reserves all rights in and to the Software
; and all intellectual property rights therein.  IF YOU HAVE
; NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS SOFTWARE
; IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
; ALL USE OF THE SOFTWARE.  
;  
; Except as expressly set forth in the Authorized License,
;  
; 1.     This program, including its structure, sequence and organization,
; constitutes the valuable trade secrets of Broadcom, and you shall use
; all reasonable efforts to protect the confidentiality thereof,
; and to use this information only in connection with your use of
; Broadcom integrated circuit products.
;  
; 2.     TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS
; PROVIDED "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
; REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
; OR OTHERWISE, WITH RESPECT TO THE SOFTWARE.  BROADCOM SPECIFICALLY
; DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
; NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
; ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
; CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
; OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
; 
; 3.     TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL
; BROADCOM OR ITS LICENSORS BE LIABLE FOR (i) CONSEQUENTIAL,
; INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY DAMAGES WHATSOEVER
; ARISING OUT OF OR IN ANY WAY RELATING TO YOUR USE OF OR INABILITY
; TO USE THE SOFTWARE EVEN IF BROADCOM HAS BEEN ADVISED OF THE
; POSSIBILITY OF SUCH DAMAGES; OR (ii) ANY AMOUNT IN EXCESS OF
; THE AMOUNT ACTUALLY PAID FOR THE SOFTWARE ITSELF OR USD 1.00,
; WHICHEVER IS GREATER. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING
; ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.$
;
;
; This is the LED program for the S9100 USa board.
; To start it, use the following commands from BCM:
;
;       led 0 load usa-link-ack.hex
;       led 0 auto on
;       led 0 start
;       led 1 load usa-link-ack.hex
;       led 1 auto on
;       led 1 start
;
; There is only one LED per four ports for combine status of link and activity.
;
; Each chip drives only its own LED and needs to write them in the order:
;       LA00, LA04, ..., LA60
;
; There is one bit per Ethernet LED with the following colors:
;       ZERO            Black
;       ONE             Green
;
; Link up/down info cannot be derived from LINKEN or LINKUP, as the LED
; processor does not always have access to link status.  This program
; assumes link status is kept current in bit 0 of RAM byte (0xA0 + portnum).
; Generally, a program running on the main CPU must update these
; locations on link change; see linkscan callback in
; $SDK/src/appl/diag/ledproc.c.
;
; Current implementation:
;
; LA00 reflects port 0~3 link/activity status:
;       Black: All 4 ports are link down
;       Green: At least 1 port is link up
;       Blink: At least 1 port has activity
;
;
TICKS           EQU     1
SECOND_TICKS    EQU     (30*TICKS)
TXRX_ALT_TICKS  EQU     20

MIN_PORT        EQU     0
MAX_PORT        EQU     63
NUM_PORT        EQU     64


; Assumptions
; LED status [TX/RX Activity] is organized to map 1:1 for indexing with
; link status data thru' CMIC_LEDUP*_PORT_ORDER_REMAP_* registers.
; Link status is injected by SDK from Linkscan task at PORTDATA offset

;
; Main Update Routine
;
;  This routine is called once per tick.
;

update:
        ld      A,MIN_PORT
port_loop:
        port    A
        ld      (PORT_NUM),A
    
        ; Combine link and activity of consecutive 4 ports in one LED
        call    link_act
    
        ; Debug
        ;call led_green
        ; Debug


        ld      A,(PORT_NUM)
        add     A,4
        cmp     A,NUM_PORT
        jnz     port_loop
    
        ; Update various timers
        inc     (TXRX_ALT_COUNT)
        ld      A, (TXRX_ALT_COUNT)
        cmp     A, TXRX_ALT_TICKS
        jnz     send_data
        ld      A, 0
        ld      (TXRX_ALT_COUNT), A

send_data:
        send    16 ; one bit for 4 ports

;
; link_act
;
;  This routine calculates the link and activity LED for the current 4 ports.
;  It extends the activity lights using timers (new activity overrides
;  and resets the timers).
;  Note: Since the return address stack is only 2-level deep, and new address
;        pushed onto the stack will cause the oldest entry on the stack being
;        discarded, so instead of reconstructing a new routine such link
;        get_link_quad() or get_act_quad(), just duplicate the call of get_link()
;        and get_act() four times in the implemention of this routine.
;
;  Inputs: (PORT_NUM)
;  Outputs: one bit sent to LED stream
;
;       Link/Activity status LED update

link_act:
        ld      A,(PORT_NUM)

        ; Port 0
        call    get_link
        jc      check_act       ; Continue to check ports' activity
    
        ; Port 1
        inc     A
        call    get_link
        jc      check_act       ; Continue to check ports' activity
    
        ; Port 2
        inc     A
        call    get_link
        jc      check_act       ; Continue to check ports' activity
    
        ; Port 3
        inc     A
        call    get_link
        jc      check_act       ; Continue to check ports' activity
    
        jnc     led_black       ; LED black if all 4 ports are link down

check_act:
        ; Back to base port
        sub     A,3
    
        ; Port 0
        call    get_act
        jc      led_blink       ; LED blink if port 0 has activity
    
        ; Port 1
        inc     A
        call    get_act
        jc      led_blink       ; LED blink if port 1 has activity
    
        ; Port 2
        inc     A
        call    get_act
        jc      led_blink       ; LED blink if port 2 has activity
    
        ; Port 3
        inc     A
        call    get_act
        jc      led_blink       ; LED blink if port 3 has activity
    
        call    led_green       ; Stay green if all 4 ports have no activity
        ret

;
; get_link
;
;  This routine finds the link status for a port.
;  Link info is in bit 0 of the byte read from PORTDATA[port]
;
;  Inputs: Port number in A
;  Outputs: Carry flag set if link is up, clear if link is down.
;  Destroys: A, B
;

get_link:
        ld      B,PORTDATA
        add     B,A
        ld      B,(B)
        tst     B,0
        ret

; Currently not usable routine
get_link_hw:
        port    A
        pushst  LINKUP
        pop
        ret

;
; get_act
;
;  This routine finds the activity status for a port.
;  Activity status is boolean OR result of bit 0 and bit 1 of
;  the port status fileds defined at the end of this file
;
;  Inputs: Port number in A
;  Outputs: Carry flag set if activity, clear if no activity.
;  Destroys: None
;

get_act:
        port    A
        pushst  RX
        pushst  TX
        tor
        pop
        ret

;
; led_black, led_green
;
;  Inputs: None
;  Outputs: One bit to the LED stream indicating color
;  Destroys: None
;

led_black:
        pushst  ZERO
        pack
        ret

led_green:
        pushst  ONE
        pack
        ret

;
; led_blink
;
;  Inputs: (TXRX_ALT_COUNT)
;  Outputs: One bit to the LED stream indicating color
;  Destroys: B
;

led_blink:
        ld      B, (TXRX_ALT_COUNT)
        sub     B, TXRX_ALT_TICKS/2
        jc      led_green
        jnc     led_black       ; Fast alternation of black/green
        ret

;
; Variables (SDK software initializes LED memory from 0xa0-0xff to 0)
;

TXRX_ALT_COUNT  equ     0xe0
PORT_NUM        equ     0xe1

;
; Port data, which must be updated continually by main CPU's
; linkscan task.  See $SDK/src/appl/diag/ledproc.c for examples.
; In this program, bit 0 is assumed to contain the link up/down status.
;

;Offset 0x1e00 - 0x80
;LED scan chain assembly area

;Offset 0x1e80 - 0xa0
PORTDATA        equ     0xa0    ; Size 48 + 1 + 4 bytes

;
; Symbolic names for the bits of the port status fields
;

RX              equ     0x0     ; received packet
TX              equ     0x1     ; transmitted packet
COLL            equ     0x2     ; collision indicator
SPEED_C         equ     0x3     ; 100 Mbps
SPEED_M         equ     0x4     ; 1000 Mbps
DUPLEX          equ     0x5     ; half/full duplex
FLOW            equ     0x6     ; flow control capable
LINKUP          equ     0x7     ; link down/up status
LINKEN          equ     0x8     ; link disabled/enabled status
ZERO            equ     0xE     ; always 0
ONE             equ     0xF     ; always 1

生成的 C 语言代码是一个 256 字节长的数组表示的微码:

/* usa_link_act.c -- autogenerated 'usa_link_act' led program */

const unsigned char ledproc_usa_link_act[] = {
 0x02, 0x00, 0x28, 0x60, 0xE1, 0x67, 0x13, 0x06,
 0xE1, 0xF2, 0x04, 0xD2, 0x40, 0x74, 0x02, 0x86,
 0xE0, 0x3A, 0x10, 0x06, 0xE1, 0x67, 0x42, 0x71,
 0x2A, 0x80, 0x67, 0x42, 0x71, 0x2A, 0x80, 0x67,
 0x42, 0x71, 0x2A, 0x80, 0x67, 0x42, 0x71, 0x2A,
 0x75, 0x56, 0xE2, 0x03, 0x67, 0x4E, 0x71, 0x5E,
 0x80, 0x67, 0x4E, 0x71, 0x5E, 0x80, 0x67, 0x4E,
 0x71, 0x5E, 0x80, 0x67, 0x4E, 0x71, 0x5E, 0x67,
 0x5A, 0x57, 0x12, 0xA0, 0xF8, 0x15, 0x1A, 0x00,
 0x57, 0x28, 0x32, 0x07, 0x97, 0x57, 0x28, 0x32,
 0x00, 0x32, 0x01, 0xB7, 0x97, 0x57, 0x32, 0x0E,
 0x87, 0x57, 0x32, 0x0F, 0x87, 0x57, 0x16, 0xE0,
 0xCA, 0x0A, 0x74, 0x5A, 0x77, 0x56, 0x57, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

可以看到,此数据与前文介绍的 CMIC_LEDUP0_PROGRAM_RAM 寄存器的值是一一对应的。

在 C 代码中加载微码并初始化 LED 回调函数的方法如下:

BCM_RC(soc_ledproc_config(0, ledproc_usa_link_act, (0 << 16) | sizeof(ledproc_usa_link_act)));
BCM_RC(soc_ledproc_config(0, ledproc_usa_link_act, (1 << 16) | sizeof(ledproc_usa_link_act)));
BCM_RC(bcm_linkscan_register(0, ledproc_linkscan_cb));

其中最后一行,注册 LED 扫描回调为 SDK 提供的 ledproc_linkscan_cb 函数,其中将各个端口状态存放在 LED_DATA_RAM 的 0xA0 开始的区域(link 状态放在 bit0),供汇编代码使用:

    portdata = soc_pci_read(unit,
                     led_info[led_ix].dram_base + CMIC_LED_REG_SIZE * byte);
    
    if (info->linkstatus == BCM_PORT_LINK_STATUS_UP) {
        portdata |= 0x01;
    } else {
        portdata &= ~0x01;
    }
    
    portdata &= ~0x80;
    
    soc_pci_write(unit, led_info[led_ix].dram_base + CMIC_LED_REG_SIZE * byte,
                      portdata);

LED 串行接口

LED 串行接口包括两个管脚:LED_CLKLED_DATA

每一个刷新周期开始时,LED_DATA 管脚会先输出一个同步信号,随后是从扫描链组装区输出的各个 LED 状态,LED_CLK 下降沿时 LED_DATA 发生相应的跳变,LED_CLK 上升沿时对 LED_DATA 锁存。全部的 LED 状态都输出完成后,两个管脚都恢复空闲状态(保持低电平),直到下一个 LED 刷新周期。

LED 刷新周期(对应的频率)由 CORECLK 根据 CLK_PARAMS 寄存器的 REFRESH_CYCLE_PERIOD 字段分频得到(通常为 30Hz),而 LED_CLK 本身的频率由 CORECLK 根据 CLK_DIV 寄存器的 LEDCLK_HALF_PERIOD 字段分频得到(通常为 5MHz)。系统中使用的配置如下:

/* 设置LED刷新周期为 7ms */
BCM_RC(sh_process_command(0, "s CMIC_LEDUP0_CLK_PARAMS 6000000"));
BCM_RC(sh_process_command(0, "s CMIC_LEDUP1_CLK_PARAMS 6000000"));
/* 设置LED_CLK频率为 5MHz */
BCM_RC(sh_process_command(0, "s CMIC_LEDUP0_CLK_DIV 100"));
BCM_RC(sh_process_command(0, "s CMIC_LEDUP1_CLK_DIV 100"));

由于将 LED 刷新周期设置为 7ms,在汇编代码中当有收发 activity 时每 10 个刷新周期 LED 状态翻转一次,由此实现 70ms 亮、70ms 灭的闪烁频率。

最后,在 LED 串行接口中输出的 LED 状态信息,由 FPGA 或 EPLD 解析后驱动各个端口对应的 LED 实现点亮、熄灭或闪烁。

以上。

comments powered by Disqus