深入探究:6输入LUT如何实现32位移位寄存器?
LUT不仅可以实现任意组合逻辑,还可以配置为分布式RAM或移位寄存器。其中,用LUT实现移位寄存器是FPGA中非常高效且常用的技巧。本文将详细探讨一个6输入LUT(LUT6)是如何摇身一变,成为一个32位移位寄存器的。
一、LUT的多重身份
在Xilinx 7系列及之后的FPGA中,一个6输入LUT(LUT6)本质上是一个64×1的RAM(64个存储单元,每个单元1比特)。这是因为:
- 6个地址输入可以寻址2⁶ = 64个不同的存储位置。
- 每个存储位置可以存储1比特数据。
这种RAM特性使得LUT可以工作于多种模式:
- 逻辑模式 :作为查找表实现组合逻辑,内容由综合工具根据逻辑函数真值表生成。
- 分布式RAM模式 :作为小型RAM使用,支持同步写、异步读。
- 移位寄存器模式(SRL) :作为串行输入、串行/并行输出的移位寄存器。
二、SRL32的工作原理
一个LUT6配置为移位寄存器时,称为 SRL32 (Shift Register LUT,深度32位)。为什么是32位,而不是64位?因为SRL模式只使用了LUT的一半存储单元(32个),另一半通常用于其他功能或保留。在Xilinx的术语中,有SRL16(深度16)和SRL32(深度32)之分,SRL32使用LUT6实现,而SRL16使用LUT5或LUT6的一半资源。
2.1 内部结构
SRL32的核心是一个32比特的移位寄存器链。它的主要口包括:
- D(输入) :串行数据输入。
- CLK(时钟) :移位时钟,上升沿有效。
- CE(时钟使能) :可选,高电平时允许移位。
- A[4:0](地址) :5位地址,用于选择32个抽头中的一个作为输出Q(异步读)。
- Q(输出) :根据地址A选择的抽头输出(组合逻辑输出)。
- Q31(输出) :最后一个寄存器的输出(第32级),通常用于级联。
注意 :SRL32没有专用的复位引脚。如果需要复位,可以通过在输入数据路径上添加逻辑实现,或者使用输出寄存器配合复位。
2.2 工作过程
- 移位操作 :在每个时钟上升沿(且CE有效时),内部32个寄存器的值依次向右移动一位。新数据从D输入进入第0级,原来第31级的数据移出丢失(除非通过Q31引出)。
- 读操作 :地址A[4:0]选择32个抽头中的一个(0~31),其当前值直接出现在输出Q上。这是 异步读 ,意味着Q随地址变化立即变化,不依赖时钟。
- 级联输出 :Q31输出第31级(最后一级)的值,可用于连接下一个SRL32的D输入,形成更长移位寄存器。
这种结构使得SRL32非常灵活:既可以作为简单的串行移位寄存器,又可以通过地址抽头实现可变的延迟线。
三、如何实例化SRL32?
在Xilinx的设计环境中,可以通过原语(Primitive)直接实例化SRL32。常用的原语有:
- SRLC32E :带时钟使能的32位移位寄存器,支持级联输出(Q31)和可选抽头输出(Q)。
- SRL16E :16位移位寄存器(使用半个LUT6)。
SRLC32E原语接口
verilog
SRLC32E #(
.INIT(32'h00000000) // 初始值
) inst_name (
.Q(Q), // 抽头输出,由A决定
.Q31(Q31), // 最后一级输出(第31级),用于级联
.A(A), // 5位地址,选择抽头位置
.CE(CE), // 时钟使能
.CLK(CLK), // 时钟
.D(D) // 串行数据输入
);
- A的范围是0~31,决定哪个抽头连接到Q。当需要固定延迟时,可以将A设为常数,此时Q输出固定延迟后的数据。
- Q31始终输出第31级的值,用于级联下一级SRL。
- INIT参数设置32个寄存器的初始值。如果不指定,默认全0。
四、级联多个SRL32实现更长移位寄存器
单个SRL32提供32位延迟。要构建64、96、128甚至更长的移位寄存器,只需将多个SRL32级联。
4.1 级联方法
- 将第一个SRL32的Q31连接到第二个SRL32的D输入。
- 所有SRL32共享相同的时钟和时钟使能。
- 如果需要抽头输出,可以用地址选择每个SRL32的Q输出,但注意这些抽头是异步的。
例如,构建64位移位寄存器:
verilog
wire [31:0] q31_tmp; // 每级的Q31输出
wire [63:0] tap; // 如果需要所有抽头,可能需要多级组合
// 第一级:低32位
SRLC32E #(.INIT(0)) srl0 (
.Q(tap[31:0]?), // 如果需要抽头,需要额外的多路选择
.Q31(q31_tmp[0]),
.A(addr_low), // 地址控制低32位的抽头
.CE(ce),
.CLK(clk),
.D(din)
);
// 第二级:高32位
SRLC32E #(.INIT(0)) srl1 (
.Q(tap[63:32]?),
.Q31(q31_tmp[1]), // 可用于再下一级
.A(addr_high),
.CE(ce),
.CLK(clk),
.D(q31_tmp[0])
);
4.2 级联延迟与性能
级联多个SRL32时,数据路径上只有Q31到D的组合逻辑,没有额外触发器,因此级联延迟非常小(仅为LUT内部布线延迟),可以支持很高的工作频率。但要注意,抽头输出Q是组合的,如果多个SRL的抽头需要组合成大范围的多路选择,可能会引入较大的组合延迟,建议在需要同步输出时,用触发器重新寄存。
五、SRL32与触发器链、BRAM的对比
实现移位寄存器有多种方式:触发器链、SRL、BRAM。它们的适用场景不同:
| 实现方式 | 资源消耗 | 最大深度 | 性能 | 灵活性 | 典型应用 |
|---|---|---|---|---|---|
| 触发器链 | 每位移位需1个FF | 任意 | 高(可流水) | 灵活,每个抽头可直接访问 | 小深度(<64),需要多抽头且高性能的场景 |
| SRL32 | 每32位消耗1个LUT6 | 32的倍数 | 高 | 抽头需通过地址选择 | 中等深度(32~几千),延迟线、FIFO |
| BRAM | 每18Kb可配置 | 很大 | 较高 | 需配合计数器生成地址 | 深度很大(>512),如FIFO、行缓冲 |
- SRL32的优势 :用LUT实现移位寄存器,比用触发器链节省大量资源(1个LUT6可替代32个触发器)。同时,由于LUT位于逻辑单元内,布线灵活,适合中等深度的延迟线。
- SRL32的局限 :
- 抽头输出是异步的,如果需要在时钟沿同步采样,需外加触发器。
- 没有专用复位,复位需要额外逻辑。
- 不支持异步清零/置位。
六、设计实例与应用场景
6.1 可编程延迟线
许多数字系统需要可变的延迟,例如时钟相位调整、脉冲展宽。用SRL32可以轻松实现:
verilog
module variable_delay #(
parameter MAX_DELAY = 32
)(
input clk,
input ce,
input [4:0] delay, // 0~31
input din,
output reg dout
);
wire q_tap;
SRLC32E srl (
.Q(q_tap),
.Q31(),
.A(delay),
.CE(ce),
.CLK(clk),
.D(din)
);
always @(posedge clk) dout <= q_tap; // 同步输出,避免组合毛刺
endmodule
6.2 FIFO的存储体
异步FIFO通常使用BRAM作为存储体,但小深度FIFO(如16~64深度)用SRL实现更节省资源。FPGA厂商提供的FIFO IP核通常会自动选择用SRL还是BRAM,取决于深度配置。
6.3 流水线平衡
在高速设计中,经常需要插入流水线寄存器来平衡延迟。用SRL可以方便地在数据路径上插入固定延迟,而不必手动添加大量触发器。
七、使用SRL的注意事项
- 时钟使能 :SRL支持时钟使能,当CE无效时,数据保持不变。这在需要暂停移位的场景很有用。
- 初始化 :SRL可以像BRAM一样在配置时初始化,通过INIT参数指定初始值。如果不初始化,上电后SRL的内容是随机的(通常为0,但取决于工艺)。
- 复位 :SRL内部寄存器没有复位引脚。如果设计需要复位,可以在数据路径上添加一个选择器:当复位有效时,将D输入强制为0,同时让CE保持有效,这样移位寄存器会在几个时钟周期后清零。或者,可以使用输出触发器并复位输出。
- 功耗 :SRL工作时,每个时钟周期内部32个寄存器都会翻转,功耗比用BRAM高,但比用触发器链低。在深度较大且翻转频繁时,考虑用BRAM更省电。
- 同步读 :如果需要同步读抽头,务必在Q后加一级触发器,否则组合逻辑可能产生毛刺或时序违例。
八、本章总结
| 问题 | 答案 |
|---|---|
| 一个6-input LUT如何实现32位移位寄存器? | LUT6本质是一个64×1的RAM,在移位寄存器模式下,将其中的32个存储单元组织成串行移位链,通过内部写地址指针实现移位,并通过5位地址选择抽头输出。 |
| 核心优势 | 1个LUT6替代32个触发器,大幅节省资源,同时保持较高性能。 |
| 如何级联? | 通过Q31输出连接到下一级的D输入,实现深度扩展。 |
| 主要应用 | 可变延迟线、小深度FIFO、流水线平衡等。 |
| 注意事项 | 无复位、异步抽头输出、需要时钟使能、初始化等。 |
通过合理使用SRL,可以在FPGA设计中实现资源与性能的平衡。