哎呀,这个HSTX的功能听起来挺复杂的,不过我来给你简单捋一捋哈.
首先呢,HSTX就是把数据从系统时钟域传到最多8个GPIO上,而且它的速率跟系统时钟是独立的,就是说他其实可以选择不同的时钟,在RP2350上,GPIO12到GPIO19是支持这个功能的,不过它只能用来输出数据.
你看这个图啊,里面有个32位宽的异步FIFO,DMA可以高速写入数据到这个FIFO,然后呢,命令扩展器会处理这些数据流,输出移位寄存器再把32位的数据分配到连续的HSTX时钟周期里,中间还有个多选一的Crossbar,还可以支持数据反转,它的输出是双倍数据速率,即DDR,一个时钟周期两个数据.

HSTX通过GPIO的DDR输出寄存器来传数据,每个引脚每个时钟周期最多能传两位,这就是DDR,它还能把所有GPIO输出的延迟平衡在300皮秒以内,这样当用相邻的GPIO做伪差分驱动时,,能最小化共模分量,这个设计还能在输出数据的同时驱动时钟,保持目标设备的建立和保持时间,总之,一切就是为了模拟更好的高速信号传输.
HSTX时钟的最大频率是150 MHz,跟系统时钟一样.通过DDR输出操作,每个引脚的最大数据速率能达到300 Mb/s.系统时钟和HSTX时钟的频率比没有限制,但每个时钟都得足够快才能维持所需的吞吐量,不过呢,如果系统时钟频率特别低,而HSTX频率特别高,可能会遇到系统DMA带宽限制,因为DMA在每个系统时钟周期内最多只能写一次HSTX FIFO.即发生Overrun了.
再来说说数据FIFO,它深度是8,32位宽,在系统时钟域和HSTX时钟域之间缓冲数据,通过AHB的FASTPERI仲裁器访问,DMA可以单周期写入.FIFO的状态也有专门的寄存器来指示.
HSTX端也是从FIFO里一次弹出32位数据,这些数据在传到输出移位寄存器之前,还可以选择让命令扩展器来操作一下.

输出移位寄存器呢,每个周期要么从FIFO重新填充32位数据,要么通过旋转寄存器重新循环数据.这个可以用来执行左移或右移,甚至重复数据.
HSTX的内部数据路径宽度是32位,但输出比较窄,每个HSTX周期最多只能输出16位(8 GPIO * DDR).输出移位寄存器就是用来适配这些不匹配的数据宽度的,它是个32位移位寄存器,每次总是从命令扩展器输出或直接从数据FIFO中重新填充32位数据.
输出移位寄存器的数据源是由CSR的EXPAND_EN位来配置的,如果这个字段被设置,命令扩展器就会介入FIFO和输出移位寄存器之间.如果没置位,命令扩展器就被绕过,直接从FIFO送数据到移位寄存器.
每当CSR的EN为低,移位寄存器会被清空.一旦HSTX配置完成,并且EN被设置为高,移位寄存器就准备好接收数据,并在数据可用时立即弹取数据.
在拿到第一个数据字后,移位寄存器会在每个HSTX时钟周期进行移位,直到它变为空.移位行为由CSR的N_SHIFTS和CSR的SHIFT这两个位决定.CSR的N_SHIFTS决定了在寄存器被视为空之前要移位多少次,而CSR的SHIFT是每个周期应用于移位寄存器的右旋操作.这个寄存器后面还会详细说说.
CSR的N_SHIFTS和CSR的SHIFT只能在CSR的EN为低时更改,在将EN从低设置为高的同一寄存器写入操作中更改这些字段是安全的.SHIFT*N_SHIFTS可能大于32.举个例子,SHIFT为31可能用于每个周期将寄存器左移一位,因为右旋是个模运算,-1在模32下等于31.
当移位寄存器即将变空时,如果有数据可用,它会立即从命令扩展器或FIFO中重新填充新数据.当数据可用时,移位寄存器在任何周期都不会为空.如果数据不可用,移位寄存器将变为空并停止移位,直到提供更多数据.一旦提供数据,移位寄存器将重新填充并再次开始移位.
Bit Crossbar其实就是控制输出移位寄存器的哪些位在每个HSTX时钟周期的前半段和后半段出现在哪些GPIO上,每个引脚都有一个配置寄存器,从BIT0到BIT7:
- BITx.SEL_P 决定前半段输出哪个移位寄存器位 (0到31)
- BITx.SEL_N 决定后半段输出哪个移位寄存器位 (0到31)
- BITx.INV 反转输出 (我猜应该是用于伪差分)
- BITx.CLK 表示这个引脚应该连接到时钟发生器
假设现在要在单个引脚发生波形,引脚配置决定了通过HSTX传递的数据布局.我们可能不太习惯四维思维,所以通过一些单个引脚的例子来说明:
- N_SHIFTS = 32, SHIFT = 1, SEL_P = 0, SEL_N = 0:
- 每个HSTX时钟周期移出1位,LSB优先.
- 每个周期,移位寄存器向右移动一位,最低有效位在周期的两个半周期内都出现在引脚上,因为SEL_P和SEL_N都选了同一个位.
- N_SHIFTS = 32, SHIFT = 31, SEL_P = 31, SEL_N = 31:
- 每个HSTX时钟周期移出1位,MSB优先.
- 每个周期,移位寄存器向左移动一位,最高有效位出现在引脚上.
- N_SHIFTS = 16, SHIFT = 2, SEL_P = 0, SEL_N = 1:
- 每个HSTX时钟周期移出2位,LSB优先.
- 每个周期,移位寄存器向右移动两位.最低有效位在前半段出现,相邻位在后半段出现.
- N_SHIFTS = 16, SHIFT = 30, SEL_P = 31, SEL_N = 30:
- 每个HSTX时钟周期移出2位,MSB优先.
- 每个周期,移位寄存器向左移动两位/最高有效位在前半段出现,相邻位在后半段出现.
- N_SHIFTS = 8, SHIFT = 4, SEL_P = 0, SEL_N = 0:
- 在8个时钟周期内,移出每组4位中的最低有效位.
- 每个周期,移位寄存器向右移动四位,最低有效位出现在引脚上,所以引脚的位索引是0, 4, 8, 12, 16, 20, 24和28.
假设现在到多引脚控制.
- N_SHIFTS = 8, SHIFT = 4, BIT0.SEL_P = 0, BIT0.SEL_N = 2, BIT1.SEL_P = 1, BIT1.SEL_N = 3:
- 每个32位字由16个比特对组成,每个周期向BIT0和BIT1提供两个新的比特对.
- 移位寄存器每周期前进4位,把两个新的比特对引入最右侧四位.
- N_SHIFTS = 8, SHIFT = 2, BIT0.SEL_P = 0, BIT0.SEL_N = 1, BIT1.SEL_P = 16, BIT1.SEL_N = 17:
- 每个32位字由一对16位值组成,每个值以每周期两位的速率分别移位到BIT0和BIT1引脚.
- 移位寄存器每周期前进两位,向BIT0引脚的1:0位引入一个新的比特对,同时也向BIT1引脚的17:16位引入一个新的比特对.
根据软件需求,可能更倾向于把所有在同一周期输出的比特打包在一起,或者把所有通过同一引脚的比特打包在一起,所以HSTX支持两种方式.
作为一个具体的例子,以TMDS为例:每个32位字包含3个10位的TMDS符号,每个符号在10个TMDS位时间内通过差分对进行串行化。为了提高性能,最好通过利用DDR功能,使每个HSTX时钟周期等于两个TMDS位周期。因此,一个可能的配置是:
- CSR: N_SHIFTS = 5, SHIFT = 2
- BIT0: SEL_P = 0, SEL_N = 1, INV = 0
- BIT1: SEL_P = 0, SEL_N = 1, INV = 1
- BIT2: SEL_P = 10, SEL_N = 11, INV = 0
- BIT3: SEL_P = 10, SEL_N = 11, INV = 1
- BIT4: SEL_P = 20, SEL_N = 21, INV = 0
- BIT5: SEL_P = 20, SEL_N = 21, INV = 1
TMDS缺少的部分是时钟,其周期为10个TMDS位周期,或者当每个引脚每周期移位两位时为5个HSTX时钟周期.HSTX有一个特殊用途的时钟生成器,因此不需要将伪时钟位打包到FIFO数据流中.
这个时钟生成器其实就是一个计数器,它会在n个HSTX时钟周期内发出周期性的信号,具体周期数由CSR的CLKDIV位来配置.这个周期数必须是HSTX时钟周期的整数倍,范围是1到16.有意思的是,它既支持奇数周期也支持偶数周期,还用了DDR输出来确保在HSTX周期内能完成输出转换.不过要注意,整个系统只有一个时钟生成器——如果你想模拟多个时钟,那就变通一下,编码一个像时钟的数据流进数据线.
时钟生成器会在输出移位寄存器移位的周期上递增,一般来说,时钟周期会是CSR的N_SHIFTS的除数,这样时钟和数据就能保持对齐.比如在之前的TMDS示例中,CLKDIV设为5就很合适,因为每次移位寄存器刷新时时钟都会重复一次.这正好符合TMDS时钟周期为10个比特周期的要求,毕竟每周期要传输两位嘛.
时钟生成器的输出可以连接到任何设置了BITx的CLK位的引脚,如果你想生成差分时钟输出,可以把时钟连到两个引脚上,然后把其中一个反转一下.
CSR的CLKPHASE这个字段定义了时钟生成器的初始相位,单位是半个HSTX时钟周期.每当CSR的EN为低时,时钟生成器就会重置并保持在这个初始相位.一旦CSR的EN被设置并且输出移位寄存器开始移位,时钟生成器就会开始前进.
当CSR的EN为低时,时钟生成器的输出由时钟周期和初始时钟相位的关系决定,如果初始时钟相位小于半个时钟周期,输出一开始就是低电平,否则,它一开始就是高电平.可以把时钟生成器想象成在每个生成周期的前半段是低电平,后半段是高电平.
CSR的CLKPHASE的最大值只有15个半HSTX时钟周期,CSR的CLKDIV的最大值是16个完整的HSTX时钟周期,在最大时钟周期下,如果初始相位大于或等于180度,就得用位交叉开关反转控制来反转时钟,也是一种变通方法.
只有在CSR的EN为低时才能改CSR的CLKPHASE和CSR的CLKDIV,在把EN从低设置为高的同一寄存器写入中修改它们是安全的.
来说说中心对齐时钟这个例子,在传输源同步数据的时候,接收端这边得特别注意:数据转换不能太晚,不然在时钟有效边沿之前就看不到了.也不能太早,不然在时钟有效边沿之后又看得太早了.这就是时序违规.
因为HSTX的输出延迟都是相互平衡的,所以我们可以把时钟转换放在数据转换的中间位置,这样就能满足这些要求了,这就是所谓的中心对齐时钟.
这里有个小细节,时钟定位的时间分辨率是半个比特时间,所以每个引脚的最大数据速率就是每个HSTX时钟周期一个比特,因为时钟已经DDR了,所以没法再用DDR来提高数据速率了.所以呢,对于BIT0到BIT7,BITx的SEL_N和BITx的SEL_P都得设置成一样的。
如果是单数据速率数据,用上升沿的话,可以这样设置时钟生成器:
- CSR.CLKDIV = 1 (1个HSTX时钟周期)
- CSR.CLKPHASE = 1 (1/2个HSTX时钟周期)
这样时钟会延迟半个HSTX周期,就能和第一个数据的启动对齐了.
如果是用下降沿的话,可以这样设置:
- CSR.CLKDIV = 1(1个HSTX时钟周期)
- CSR.CLKPHASE = 2(1个HSTX时钟周期)
或者你也可以用和上升沿一样的设置,然后通过位交叉配置把时钟输出反转一下。
如果是双数据速率数据,同时用上升沿和下降沿的话,可以这样设置:
- CSR.CLKDIV = 2 (2个HSTX时钟周期)
- CSR.CLKPHASE = 1 (1/2个HSTX时钟周期)
不管是哪种情况,数据速率都是一样的,每个引脚每个HSTX时钟周期1个比特,时钟周期已经DDR了.
下面的图展示了从FIFO中弹出的命令和数据的混合,数据可以在扩展移位寄存器中重复或移位,并可选地通过编码器传递到输出移位寄存器.

命令扩展器可以在FIFO数据和输出移位寄存器之间插入数据.通常,输出流比输入流大,所以叫扩展器,通过设置CSR的EXPAND_EN来启用命令扩展器,一样的,只能在CSR的EN为低时修改此字段,同样的在将EN从低设置为高的同一寄存器写入中修改此字段是安全的.
当命令扩展器被禁用时,数据直接从数据FIFO传递到输出移位寄存器,不会被扩展器修改,即旁路了.
当命令扩展器启用时,数据FIFO携带数据和扩展器命令的混合,每个命令由一个4位操作码和一个12位长度组成,打包在数据FIFO字的16个最低有效位中,操作码位于位15到12,长度位于位11到0.这些后面寄存器部分会详细解释.可用命令大致有这几个.
- 0x0: RAW
- 0x1: RAW_REPEAT
- 0x2: TMDS
- 0x3: TMDS_REPEAT
- 0xf: NOP
当HSTX首次启用时,如果命令扩展器启用,它期望数据FIFO中的第一个字是命令,如果此命令不是NOP,接着下一个是一些数据,然后是另一个命令,循环这个操作.
计数字段确定此命令向下游输出移位寄存器输出的字数,范围为1到4095,计数为0保留为无限,此命令从数据FIFO读取的字数,以产生指定数量的下游数据,取决于命令和EXPAND_SHIFT的ENC_N_SHIFTS和EXPAND_SHIFT的RAW_N_SHIFTS寄存器字段.
扩展移位寄存器始终在命令开始时从FIFO取出一次,之后,带有x_REPEAT后缀的命令继续通过移位寄存器循环相同的内容,每次输出移位寄存器从命令扩展器拉取新数据时,向右旋转取决于EXPAND_SHIFT.ENC_SHIFT或EXPAND_SHIFT.RAW_SHIFT.使用0的移位来重复相同的数据而不移位,这在TMDS的水平消隐期间传输相同的TMDS控制符号时很有用,至少省事了.
RP2350仅实现了一个TMDS编码器,为未来的其他编码器保留了剩余的操作码空间.RAW和RAW_REPEAT命令绕过编码器.TMDS和TMDS_REPEAT命令在传递到输出移位寄存器之前进行TMDS编码,NOP命令没有数据,因此它们是否绕过编码器是一个什么鬼问题,至少手册里什么都没说.
EXPAND_SHIFT寄存器每个字段有两个副本,以RAW_为前缀的字段用于RAW和RAW_REPEAT命令.所有其他命令使用以ENC_为前缀.
比如TMDS中,用RAW_REPEAT命令的TMDS控制符号可能不移位,使用TMDS命令的像素数据可能一次移位一个像素,所以拥有分组的移位控制很有用.
EXPAND_SHIFT.ENC_N_SHIFTS和EXPAND_SHIFT.RAW_N_SHIFTS字段分别控制编码和原始命令的扩展移位寄存器重新填充的频率.x_REPEAT命令忽略这些字段,因为它们从不从FIFO重新拿数据,并且功能类似于控制输出移位寄存器的CSR.N_SHIFTS字段.
命令扩展器每个周期只能从数据FIFO取一次,因此大量使用命令可能会影响HSTX吞吐量,对于每个周期从HSTX输出的用例,将输出移位寄存器配置为CSR.N_SHIFTS > 1,这是必需的,因为命令扩展器在从FIFO取出命令的周期内无法输出数据,因此扩展移位寄存器至少有一个周期为空.
HSTX可以将最多8个PIO引脚输出连接到Crossbar,要求HSTX的CLK是SYS_CLK.
想用这个功能的话,得先设置CSR.COUPLED_MODE寄存器,然后通过COUPLED_SEL选要连哪个PIO实例.连上之后,PIO的12到19号输出就会出现在位交叉开关的31:24位置,相当于替换了输出移位寄存器的高8位.
这个模式的好处是能让PIO程序用上HSTX的DDR输出,比如你可以用它来驱动全速时钟,或者精确控制时钟和数据转换的时序,或者实现更复杂的时序,这真的是非常烧脑.
有个小细节要注意,不管GPIOBASE设成多少,耦合模式用的PIO输出总是19到12位.不过当GPIOBASE是16的时候,这些输出会出现在GPIO28到35引脚上.
最后提醒一下,用这个模式的话,输出会比直接从PIO到引脚多一个系统时钟周期的延迟.不过PIO本身的运行不会受到任何影响.
最后说一下寄存器,寄存器具体信息是机器翻译的,前面都说的挺清楚的了.到这里应该不会很难.
控制寄存器从0x400C_0000开始,总线访问会比较慢,可能要几个周期,因为要经过Crossbar.
偏移量 | 名称 | 描述 |
---|---|---|
0x00 | CSR | |
0x04 | BIT0 | 输出位0的数据控制寄存器 |
0x08 | BIT1 | 输出位1的数据控制寄存器 |
0x0c | BIT2 | 输出位2的数据控制寄存器 |
0x10 | BIT3 | 输出位3的数据控制寄存器 |
0x14 | BIT4 | 输出位4的数据控制寄存器 |
0x18 | BIT5 | 输出位5的数据控制寄存器 |
0x1c | BIT6 | 输出位6的数据控制寄存器 |
0x20 | BIT7 | 输出位7的数据控制寄存器 |
0x24 | EXPAND_SHIFT | 配置命令扩展器内部的可选移位器 |
0x28 | EXPAND_TMDS | 配置命令扩展器内部的可选TMDS编码器 |
控制寄存器
位域 | 描述 | 类型 | 复位值 |
---|---|---|---|
31:28 | CLKDIV: 生成的时钟周期,以HSTX时钟周期为单位。可以是奇数或偶数。生成的时钟仅在移位寄存器移位时推进。例如,clkdiv为5时,每5个HSTX时钟(或每10个半时钟)生成一个完整的输出时钟周期。CLKDIV值为0时,映射为16个HSTX时钟周期。 | RW | 0x1 |
27:24 | CLKPHASE: 设置生成时钟的初始相位。CLKPHASE为0时,时钟初始为低电平,第一个上升沿在生成时钟的半周期后出现(即clk_hstx的CLKDIV/2周期)。CLKPHASE每增加1,初始时钟相位将提前半个clk_hstx周期。例如,如果CLKDIV=2且CLKPHASE=1:* 时钟初始为低电平 * 第一个上升沿在第一个数据断言后的0.5个clk_hstx周期出现 * 第一个下降沿在第一个数据断言后的1.5个clk_hstx周期出现。此配置适用于以clk_hstx为比特率且中心对齐的DDR时钟进行串行化。当HSTX通过清除CSR_EN停止时,时钟生成器将返回到由CLKPHASE字段配置的初始相位。注意:CLKPHASE必须严格小于CLKDIV值的两倍(一个完整周期),否则其操作未定义。 | RW | 0x0 |
23:21 | 保留 | - | - |
20:16 | N_SHIFTS: 在从FIFO重新填充之前,移位寄存器的移位次数。(移位次数的计数,而不是总移位距离。)寄存器值为0表示移位32次。 | RW | 0x05 |
15:13 | 保留 | - | - |
12:8 | SHIFT: 每个周期右旋转移位寄存器的位数。使用旋转而不是移位允许通过从32中减去左移量来模拟左移。当SHIFT和N_SHIFTS的乘积大于32时,还允许数据重复。 | RW | 0x06 |
7 | 保留 | - | - |
6:5 | COUPLED_SEL: 选择用于耦合模式操作的PIO。 | RW | 0x0 |
4 | COUPLED_MODE: 启用PIO到HSTX的1:1连接。HSTX必须直接从系统时钟(而不仅仅是其他相同频率的时钟源)进行时钟,以便此同步接口正常工作。当COUPLED_MODE设置时,BITx_SEL_P和SEL_N索引24到31将选择来自8位PIO到HSTX路径的位,而不是移位寄存器位。索引0到23仍将正常索引移位寄存器。连接到PIO到HSTX总线的PIO输出是那些如果这些引脚的FUNCSEL设置为PIO而不是HSTX时出现在HSTX能力引脚上的输出。例如,如果HSTX在GPIO 12到19上,则当耦合模式启用时,PIO输出12到19连接到HSTX。 | RW | 0x0 |
3:2 | 保留 | - | - |
1 | EXPAND_EN: 启用命令扩展器。当为0时,原始FIFO数据直接传递到输出移位寄存器。当为1时,命令扩展器可以在FIFO和移位寄存器之间对数据执行简单操作,如游程解码。在EN设置时不要更改CXPD_EN。同时设置CXPD_EN和EN是安全的。 | RW | 0x0 |
0 | EN: 当EN为1时,HSTX将在FIFO中出现数据时移出数据。只要有数据,HSTX移位寄存器将每时钟周期移位一次,从FIFO弹出的频率由SHIFT和SHIFT_THRESH的比率决定。当EN为0时,FIFO不会弹出。移位计数器和时钟生成器也会在EN为低时重置为其初始状态。注意,时钟生成器的初始相位可以通过CLKPHASE字段配置。一旦HSTX再次启用,并且数据被推送到FIFO,生成时钟的第一个上升沿将在第一个数据启动后的半周期出现。 | RW | 0x0 |
BIT0 - BIT7
位域 | 描述 | 类型 | 复位值 |
---|---|---|---|
31:18 | 保留 | - | - |
17 | CLK:将此输出连接到生成的时钟,而不是数据移位寄存器。如果设置了此位,SEL_P 和 SEL_N 将被忽略,但 INV 仍可设置为生成反相时钟。 | RW | 0x0 |
16 | INV:反转此数据输出(逻辑非) | RW | 0x0 |
15:13 | 保留 | - | - |
12:8 | SEL_N:HSTX 时钟周期后半段的移位寄存器数据位选择 | RW | 0x00 |
7:5 | 保留 | - | - |
4:0 | SEL_P:HSTX 时钟周期前半段的移位寄存器数据位选择 | RW | 0x00 |
EXPAND_SHIFT
位域 | 描述 | 类型 | 复位值 |
---|---|---|---|
31:29 | 保留 | - | - |
28:24 | ENC_N_SHIFTS:当当前命令为编码数据命令(例如 TMDS)时,在从 FIFO 重新填充之前从移位寄存器消耗的次数。寄存器值为 0 表示移位 32 次。 | RW | 0x01 |
23:21 | 保留 | - | - |
20:16 | ENC_SHIFT:当当前命令为编码数据命令时,每次将数据推送到输出移位器时,移位寄存器右旋转的位数。 | RW | 0x00 |
15:13 | 保留 | - | - |
12:8 | RAW_N_SHIFTS:当当前命令为原始数据命令时,在从 FIFO 重新填充之前从移位寄存器消耗的次数。寄存器值为 0 表示移位 32 次。 | RW | 0x01 |
7:5 | 保留 | - | - |
4:0 | RAW_SHIFT:当当前命令为原始数据命令时,每次将数据推送到输出移位器时,移位寄存器右旋转的位数。 | RW | 0x00 |
EXPAND_TMDS
位域 | 描述 | 类型 | 复位值 |
---|---|---|---|
31:24 | 保留 | - | - |
23:21 | L2_NBITS: 用于通道2 TMDS编码器的有效数据位数,从旋转数据的第7位开始。字段值0→7表示1→8位。 | RW | 0x0 |
20:16 | L2_ROT: 应用于通道2 TMDS编码器之前当前移位数据的右旋转。 | RW | 0x00 |
15:13 | L1_NBITS: 用于通道1 TMDS编码器的有效数据位数,从旋转数据的第7位开始。字段值0→7表示1→8位。 | RW | 0x0 |
12:8 | L1_ROT: 应用于通道1 TMDS编码器之前当前移位数据的右旋转。 | RW | 0x00 |
7:5 | L0_NBITS: 用于通道0 TMDS编码器的有效数据位数,从旋转数据的第7位开始。字段值0→7表示1→8位。 | RW | 0x0 |
4:0 | L0_ROT: 应用于通道0 TMDS编码器之前当前移位数据的右旋转。 | RW | 0x00 |
FIFO寄存器从0x5060_0000开始.
偏移量 | 名称 | 描述 |
---|---|---|
0x0 | STAT | FIFO状态 |
0x4 | FIFO | 对FIFO的写访问 |
STAT
位域 | 描述 | 类型 | 复位值 |
---|---|---|---|
31:11 | 保留 | - | - |
10 | WOF: FIFO在满时被写入。写1清除。 | WC | 0x0 |
9 | EMPTY | RO | - |
8 | FULL | RO | - |
7:0 | LEVEL | RO | 0x00 |
FIFO
位域 | 描述 | 类型 | 复位值 |
---|---|---|---|
31:0 | 对FIFO的写访问 | WF | 0x00000000 |