作者:孤独的单刀,文章来源:CSDN博客
注:本文由作者授权转发,如需转载请联系作者本人
一、移位寄存器SRL
1.1、概述
移位寄存器内的数据可以在移位脉冲(时钟信号)的作用下依次左移或右移。移位寄存器不仅可以存储数据,还可以用来实现数据的串并转换、分频,构成序列码发生器、序列码检测器,进行数值运算以及数据处理等,它也是数字系统中应用非常广泛的时序逻辑部件之一。
在FPGA的底层结构----可配置逻辑块CLB中,一个CLB由4个Slice组成,这4个Slice又可以分SliceM和SliceL(其比例大致为1:3),其中M是Memory的首字母,L是Logic的首字母,比较SliceM和SliceL,其区别就是SliceM的查找表具有RAM和ROM的功能,而SliceL的则不具备,所以SliceM比SliceL多的功能就是做存储器和移位。
1.2、概念
SLICEM可以在不使用触发器的条件下配置为32位移位寄存器(注意:只能左移)。这样,每个LUT可以将串行数据延迟1到32个时钟周期。移位输入D(LUT DI1脚)和移位输出Q31(LUT MC31脚)可以进行级联,以形成更大的移位寄存器。一个SLICEM的4个LUT6级联可以实现128个时钟周期的延时。多个SLICEM也可以进行组合。但SLICEM之间没有直接连接以形成更长的移位寄存器,在LUT B/C/D处的MC31输出也没有。由此产生的可编程延迟可用于平衡数据pipeline的时间。
1.3、应用
1.4、结构
上图是由一个LUT构成的最高支持32位移位的移位寄存器SRLC32E结构。
1.5、级联
通过两个32位的移位寄存器SRL32与一个MUX2,即可级联成最高支持64位的移位寄存器。
通过三个32位的移位寄存器SRL32与三个MUX2,即可级联成最高支持96位的移位寄存器。
通过四个32位的移位寄存器SRL32与三个MUX2,即可级联成最高支持128位的移位寄存器。
而4个移位寄存器由4个LUT组成,刚好一个SLICEM中有4个LUT,这说明一个SLICEM可以实现最多支持128位的移位寄存器。由于4个LUT同在一个SLICE里面,所以布线方便且延迟短,有利于时序收敛。
三、移位操作
3.1、静态操作(移位长度固定)
3.2、动态操作(移位长度可变)
3.3、Timing
四、实例化
在实际的编写RTL过程中,我们可通过以下方式来生成一个移位寄存器。
4.1、源语
(1)源语类型
以上均可以实现移位寄存器功能,后文以SRLC32E为例进行讲解。
(2)源语例化
// SRLC32E: 32-bit variable length cascadable shift register LUT (Mapped to a SliceM LUT6)
// with clock enable
SRLC32E #(
.INIT(32'h00000000) // Initial Value of Shift Register
) SRLC32E_inst (
.Q(Q), // SRL data output
.Q31(Q31), // SRL cascade output pin
.A(A), // 5-bit shift depth select input
.CE(CE), // Clock enable input
.CLK(CLK), // Clock input
.D(D) // SRL data input
);
// End of SRLC32E_inst instantiation
关于参数与信号在上面已讲解,不赘述。
(3)示例代码
module test(
input clk,
input ce,
input shift_in, //移位输入
output Q, //移位输出
output shift_out //移位输出,可级联
);
SRLC32E #(
.INIT(32'h00000000) // Initial Value of Shift Register
) SRLC32E_inst (
.Q(Q), // SRL data output
.Q31(shift_out), // SRL cascade output pin
.A(5'b01001), // 5-bit shift depth select input,移位长度固定为10----01001+1
.CE(ce), // Clock enable input
.CLK(clk), // Clock input
.D(shift_in) // SRL data input
);
endmodule
上面的模块是直接使用SRL32源语例化的一个移位寄存器,移位长度固定为10。 下面是综合出来的结构,可以看到只使用了一个LUT。
(4)Testbench
`timescale 1ns / 1ns
module tb_test();
reg clk;
reg ce;
reg shift_in;
wire Q;
wire shift_out;
test test_inst(.clk(clk),.ce(ce),.shift_in(shift_in),.Q(Q),.shift_out(shift_out));
initial begin
clk=0;
ce=1;
shift_in = 1;
#330 $finish;
end
always
#5 clk=~clk;
always
#10 shift_in<=~shift_in;
endmodule
(5)仿真结果
在第一个周期输入信号从0跳转到1;移位长度设置为10,在第10个周期Q输出为1,此后输出与shift_in一致;在第32个周期,shift_out开始输出1,然后输出与shift_in一致。仿真结果符合Timing。
4.2、推断方式
除了直接例化SRL源语外,也可以通过编写常规的RTL代码来实现移位寄存器的功能。但是需要注意的是,要注意编写RTL的风格,有些风格可能导致vivado无法综合出SRL,而是使用多个REG来实现,会造成大量的资源浪费。
(1)错误的推断方式
module test(
input clk,
input ce,
input shift_in,
input rst,
output Q,
output shift_out
);
reg [31:0] dff;
always@(posedge clk) begin
if(rst)
dff<=0;
else if(ce) begin
dff[31:0]<={dff[30:0],shift_in}; //拼接运算实现向左移位
end
end
assign shift_out=dff[31];
assign Q=dff[9];
endmodule
上面代码的综合结果如下,显然不是用的SRL32,而是数个LUT+数个FF。其原因在于RTL中使用了复位信号,而SRL32这个元件是没有复位端口的,因为流水线的结构根本就不需要复位!复位的使用完全是画蛇添足,导致不必要的资源浪费。
(2)正确的推断方式
module test(
input clk,
input ce,
input shift_in,
output Q,
output shift_out
);
reg [31:0] dff;
always@(posedge clk) begin
if(ce) begin
dff[31:0]<={dff[30:0],shift_in}; //拼接运算实现向左移位
end
end
assign shift_out=dff[31];
assign Q=dff[9];
endmodule
上面的代码是删除复位后的代码,综合结果如下。
使用的资源:3个FF+2个SRL。因为我们要做的是10位移位,所以结果在第10位被引出。vivado自动帮输入以及输出做了寄存,所以资源消耗得多一些。
仿真结果:
在第一个周期输入信号从0跳转到1;移位长度设置为10,在第10个周期Q输出为1,此后输出与shift_in一致;在第32个周期,shift_out开始输出1,然后输出与shift_in一致。仿真结果符合Timing。
五、应用
5.1、同步移位寄存器
移位寄存器原语不会使用同一SLICE中可用的寄存器。要实现完全同步的读和写移位寄存器,输出引脚Q必须连接到触发器FF。移位寄存器和触发器共享同一时钟,如图所示。
5.2、固定长度的移位寄存器
32位移位寄存器可级联实现任何静态长度模式的移位寄存器,而不需要使用专用的多路复用器(F7AMUX, F7BMUX和F8MUX)。下图说明了如何构建72位移位寄存器。只有最后一个SRLC32E原语需要将其地址输入绑定到ob00111。另外,可以将移位寄存器的长度限制为71位(绑定到obo0110的地址),并且可以使用一个触发器作为最后一个寄存器。(在SRLC32E原语中,移位寄存器长度为地址输入+ 1)。
六、总结