版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/github_33678609/article/details/89880849
FPGA开发过程中是免不了要用到移位寄存器的,传统的移位寄存器是通过寄存器(或者叫触发器FF)实现的,占用的是FPGA内部的逻辑资源,当要移位的次数过多时,自然会耗费更多FF资源。
但是如果用LUT(look up table)查找表实现的话就很轻松了,LUT是通过提前存储下一张真值表来实现逻辑运算的,所以非常节省逻辑资源。常用的移位寄存器SRL种类很多,这里以16bit的SRL16E为例,说一说怎么使用它。
Xilinx SRL16E的源码如下,这个代码在安装vivado之后,都会默认自带,我的代码路径如下C:\Xilinx\Vivado\2018.3\data\verilog\src\unisims。此处推荐一个全盘快速搜索工具everything,谁用谁知道。
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1995/2016 Xilinx, Inc.
// All Right Reserved.
///////////////////////////////////////////////////////////////////////////////
// ____ ____
// / /\/ /
// /___/ \ / Vendor : Xilinx
// \ \ \/ Version : 2017.1
// \ \ Description : Xilinx Unified Simulation Library Component
// / / 16-Bit Shift Register Look-Up-Table with Clock Enable
// /___/ /\ Filename : SRL16E.v
// \ \ / \
// \___\/\___\
//
///////////////////////////////////////////////////////////////////////////////
// Revision
// 03/23/04 - Initial version.
// 03/11/05 - Add LOC paramter;
// 05/07/08 - 468872 - Add negative setup/hold support
// 12/13/11 - 524859 - Added `celldefine and `endcelldefine
// 04/16/13 - 683925 - add invertible pin support.
// End Revision
///////////////////////////////////////////////////////////////////////////////
`timescale 1 ps/1 ps
`celldefine
module SRL16E #(
`ifdef XIL_TIMING
parameter LOC = "UNPLACED",
`endif
parameter [15:0] INIT = 16'h0000,
parameter [0:0] IS_CLK_INVERTED = 1'b0
)(
output Q,
input A0,
input A1,
input A2,
input A3,
input CE,
input CLK,
input D
);
`ifdef XIL_TIMING
wire CE_dly;
wire CLK_dly;
wire D_dly;
`endif
reg [15:0] data = INIT;
reg first_time = 1'b1;
initial
begin
assign data = INIT;
first_time <= #100000 1'b0;
`ifdef XIL_TIMING
while ((((CLK_dly !== 1'b0) && (IS_CLK_INVERTED == 1'b0)) ||
((CLK_dly !== 1'b1) && (IS_CLK_INVERTED == 1'b1))) &&
(first_time == 1'b1)) #1000;
`else
while ((((CLK !== 1'b0) && (IS_CLK_INVERTED == 1'b0)) ||
((CLK !== 1'b1) && (IS_CLK_INVERTED == 1'b1))) &&
(first_time == 1'b1)) #1000;
`endif
deassign data;
end
`ifdef XIL_TIMING
generate
if (IS_CLK_INVERTED == 1'b0) begin : generate_block1
always @(posedge CLK_dly) begin
if (CE_dly == 1'b1) begin
data[15:0] <= {data[14:0], D_dly};
end
end
end else begin : generate_block1
always @(negedge CLK_dly) begin
if (CE_dly == 1'b1) begin
data[15:0] <= {data[14:0], D_dly};
end
end
end
endgenerate
`else
generate
if (IS_CLK_INVERTED == 1'b0) begin : generate_block1
always @(posedge CLK) begin
if (CE == 1'b1) begin
data[15:0] <= {data[14:0], D};
end
end
end else begin : generate_block1
always @(negedge CLK) begin
if (CE == 1'b1) begin
data[15:0] <= {data[14:0], D};
end
end
end
endgenerate
`endif
assign Q = data[{A3, A2, A1, A0}];
`ifdef XIL_TIMING
reg notifier;
wire sh_clk_en_p;
wire sh_clk_en_n;
wire sh_ce_clk_en_p;
wire sh_ce_clk_en_n;
always @(notifier)
data[0] = 1'bx;
assign sh_clk_en_p = ~IS_CLK_INVERTED;
assign sh_clk_en_n = IS_CLK_INVERTED;
assign sh_ce_clk_en_p = CE && ~IS_CLK_INVERTED;
assign sh_ce_clk_en_n = CE && IS_CLK_INVERTED;
`endif
specify
(A0 => Q) = (0:0:0, 0:0:0);
(A1 => Q) = (0:0:0, 0:0:0);
(A2 => Q) = (0:0:0, 0:0:0);
(A3 => Q) = (0:0:0, 0:0:0);
(CLK => Q) = (100:100:100, 100:100:100);
`ifdef XIL_TIMING
$period (negedge CLK, 0:0:0, notifier);
$period (posedge CLK, 0:0:0, notifier);
$setuphold (negedge CLK, negedge CE, 0:0:0, 0:0:0, notifier,sh_clk_en_n,sh_clk_en_n,CLK_dly,CE_dly);
$setuphold (negedge CLK, negedge D, 0:0:0, 0:0:0, notifier,sh_ce_clk_en_n,sh_ce_clk_en_n,CLK_dly,D_dly);
$setuphold (negedge CLK, posedge CE, 0:0:0, 0:0:0, notifier,sh_clk_en_n,sh_clk_en_n,CLK_dly,CE_dly);
$setuphold (negedge CLK, posedge D, 0:0:0, 0:0:0, notifier,sh_ce_clk_en_n,sh_ce_clk_en_n,CLK_dly,D_dly);
$setuphold (posedge CLK, negedge CE, 0:0:0, 0:0:0, notifier,sh_clk_en_p,sh_clk_en_p,CLK_dly,CE_dly);
$setuphold (posedge CLK, negedge D, 0:0:0, 0:0:0, notifier,sh_ce_clk_en_p,sh_ce_clk_en_p,CLK_dly,D_dly);
$setuphold (posedge CLK, posedge CE, 0:0:0, 0:0:0, notifier,sh_clk_en_p,sh_clk_en_p,CLK_dly,CE_dly);
$setuphold (posedge CLK, posedge D, 0:0:0, 0:0:0, notifier,sh_ce_clk_en_p,sh_ce_clk_en_p,CLK_dly,D_dly);
$width (negedge CLK, 0:0:0, 0, notifier);
$width (posedge CLK, 0:0:0, 0, notifier);
`endif
specparam PATHPULSE$ = 0;
endspecify
endmodule
`endcelldefine
SRL16E例化原语如下,输入是时钟CLK,使能CE,D,和四位输出位选择控制地址A3A2A1A0,输出是Q。首先要给出一个16bit的初始值,后面的移位就是按照时钟节拍对初始进行操作的,以代码为例,输入是D(0),意味着这16个周期内每个周期给序列最右边增加一个0,相应的每个周期对应的序列最左边的值也会被挤走。第0次移位得到0000000000001111,第二位是1,输出Q就是1,第一次移位得到0000000000011110,第二位是1,输出Q就是1,第二次移位得到0000000000111100,第二位是0,输出Q就是0,以此类推,输出依次是1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,输出第几位由A3A2A1A0做地址控制(第二位输出对应0001).
modelsim仿真示意图如下:
SRL16E #(
.INIT (16'h0000 ), // Initial contents of shift register
.IS_CLK_INVERTED(1'b0 ) // Optional inversion for CLK
)
u_ca_gain(
.Q (o_ca_out ), // 1-bit output: SRL Data
.CE (1'b1 ), // 1-bit input: Clock enable
.CLK (i_clk ), // 1-bit input: Clock
.D (i_ca_in ), // 1-bit input: SRL Data
// Depth Selection inputs: A0-A3 select SRL depth
.A0 (1'b1 ),
.A1 (1'b1 ),
.A2 (1'b0 ),
.A3 (1'b1 )
);