- 写在前面
-
正文
- 左移位寄存器
- 右移位寄存器
- 串行输入并行输出移位寄存器
- 并行输入串行输出移位寄存器
- 参考资料
- 交个朋友
写在前面
-
个人微信大众号:FPGA LAB -
个人博客主页 -
注:学习沟` l X * . ~通运用!
正文
在数字电子产品中,移位寄存器是级联的触发器,其间一个触发器的输出引脚q衔接到下一个触发器的数据输入引脚(d)。n M o } Y h | * 由于一切触发器都在同一时钟上工作,所以存储在移位寄存器中的位阵列将移位一个位置。
例如,假如一E ^ ] B j个5位右移寄存器的初始9 Q W % :值为10110,而且将移位寄存器的输入绑定到O,则下一个模式将为01011,下一个模式将为00101。
移位寄存器的种类有很多,需求根据需求来规划,但万变不离其宗,都是每$ v ^ D r一个时钟,寄存器阵列 K E }移位一次,下面就盘点各种移位寄存器:
-
左移位寄存器 -
右移位寄存器 -
串行输入并行输出移位寄存器 -
并行输入串行输出移位寄存器
下P J l z P J面就别离认识下吧。
左移位寄存器
所谓的左移,这儿约定成网K a 5 a U V 1 ( /高位移位,这是由于咱们一般界说变量都是:
reg [MSB:LS- ] ] g T t ) eB] VAR;
高位在左,底位在右,因? ` 3 R / –而左移便q ~ b y Z B R =是向高位移位。
其实这儿还能够持* 0 g T ^ O .续细分,是循环左移还是非循环的呢?
循环左移寄存器
所谓的循环左移,便是将最高& y M 9 v = 7 r位移位到最低位,次高位作K 6 _ ;为最高位,依次循环。# d 5 X O j } n 6
电路规划:
以四位循环左移为例,给出电路规划Verilog代码:
`timescale 1C v F Z { F sns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: Reborn Lee
// ModulC , K Z he Name: cycle_left_register
// Additiog z & & enal Comments:
// hM o Mttps://blog.csdn.nu S ; E / $ | det/reborn_lee
//////////////////////////////////E ` [ x f u///////////////////////////////////////N 7 S 8 { t v/////////
module cycle_left_register #(parameter MSB = 4)(
inpux t &t [MSB - 1 : 0] din,
input i_rst,
input i_load,
input i_clk,
output [MSB - 1 : 0] dout
);
reg [MSB - 1 : 0] dout_mid;
always@(posedge i_clk) begin
if(i_rst) begin
dout_mid <= '8 r { ;d0;
en/ m W [ Yd
else if(i_load) begin
dout_mid <= din;
end
else begin
dout_mid <= {dout_mid[MSB - 2 : 0], dout_mid[MSB - 1]};
end
end
assign dout = dout_mid;
endmodu_ Y 8 / +le
注:里面添加了! V u i ^ ~ 8 } ?一个信号,叫装载信号i_load,这个信号有效的时分,/ k B ` * C 4 s `将输入din赋值给中间寄存器dout_mid,这样才能实现每一个时钟上升沿来暂时,都对输入左移一次。
测验代码
简单给出测验代码:
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Engineer: Reborn Lee
// Module Name: cycle_left_register_td . y - Ab
// https://blog.csdn.net/reborn_lee
////////$ | o ( - %//////////////////////////////////////////////////////////////////////////
module cycle_left_rea N 5 b & ? j * ngister_tb(
);
p~ W s `arameter MSB = 4;
reg [MS$ 9 R - k J ,B - 1 : 0] din;
reg i_d u rrst;
reg i_clk;
reg i_load;
wire [MSB - 1 : 0] dout;
//generate clock
initial begin
i_clk = 0;
forever begin
#5 i_clkG m 1 A q 9 = = ~i_clk;
end
end
//generate rst and input data
initial( e L ] F begin
i_rst = 1;
din = 0;
i_load = 0;
# 22
i_rst = 0;
@(negedge i_clk) bZ p , =egin
din = 'b1011;
i_load = 1;
end
@(negedge i_clk) begin
i_load = 0;
end
r; 1 + { w 5epeat(51 k e Y P ^ 5 S) @(posedge i_clk);
@(negedge i_clk) begin
din = 'd0101;
i_load = 1;
end
@(negedge i_clk) i_load = 0;
repeat(4) @(posedge i_clk);
#10 $finish;
end
initial
$monitor (" i_rst = %0b, i_m j x r u ` = Aload = %0b, din = %b, dout = %b", iw V 2 5 ~_rst, i_load, din, dout);
cycle_left_register #(.MSB(MSB))inst_cyP O A h 4cle_left_register(
.ie u | v + B T ? !_clk(i_clk),
.i_rst(i_rst),
.i_load(i_load),
.din(din),
.dout(dout)
);
endmodule
给出仿真结果:
监控记载U r i:
i_rst = 1, i_load = 0, din = 0000,; Y V O dout = xxxx
i_rst = 1, i_load =I u , Z 0, din = 0000, dout = 0000
i_rsO : ; 2 , z ! } ot = 0, i_load = 0, din = 0000, dout = 0000
i_J H }rst = 0, i_load = 1j o J G R G, din = 1011, dout = 0000
i_rst = 0, i_load = 1, din = 1011, dout = 1011
i_rst = 0, i_load = 0, din = 1011, dout = 1011
i_rst = 0, i_load = 0, din = 1011, dout = 0111
i_rst = 0, iA , } g . ! 9 m k_load = 0, din = 1011, doutn o M = 1110
i_rst = 0, i_load = 0, din = 1011,m y A dout = 1101
i_rst = 0, i_load = 0, din = 1011, dout = 1011
i_rst = 0, i_load = 0, din = 1011, dout = 0111
i_rst = 0, i_load = 1, din = 0101, dout = 0111
i_rst = 0, i_loaZ = a e e . % 6 Pd = 1, din = 0101, dout = 0101O ^ X H o @ Z
i_rst = 0, i_load = 0, din = 0101, dout# y / z c 3 = 0101
i_rst = 0, i_load = 0, din = 0101, dout = 1010
i_rst = 0, i_load = 0, din = 0101, dout = 0101
i_rst = 0, i_load = 0, din = 0101, dout = 101U ? J 3 r H # ^0
i_rst = 0, i_load =u C _ 0, din = 0101, dout = 0101
非循环左移寄存器
非循环左移对于循环左移来说,便是最高 A x S 1位不移入最低位,而是丢弃,最低位补零。
修改其间移位句子即可:
dout_mid <= {dout_mid[MSB - 2 : 0], dout_mid[MS% 1 lB - 1]};
改为:
dout_mid <= {dout_mid[MSB - 2 : 0],1'b0};
行为仿真波形
i_rst = 1, i_load = 0, din = 0000, dout = xxxx
i_rst = 1, iB ` c_load = 0, din = 0000, dout = 0000
i_rstB P } y H C = 0, i_load = 0, din{ b ; = 0000, dout = 0000
i_rst = 0, i_load = 1, din = 1011, dout = 0000
i_rst = 0,7 % J i_load = 1l 0 m V V G L D, din = 1011,- ` q H T g j k W dout = 1011
i_rst = 0, i_load = 0, din = 1011, dout = 1011
i_rst = 0, i_load = 0, din = 1011Q - L a w 9 s,b x v H @ p u + ` dout = 0110
i_rst = 0, i_load = 0, din = 1011, dout = 1100
i_rst = 0, i_load = 0, din = 1011, dout = 1000
i_rst = 0, i_load = 0, din = 1011, dout = 0000
i_rst = 0, i_load = 1, din = 0101, dout =f j _ Q W P D , P 0000
i_rst = 0, i_load = 1, din = 0101, dout = 0101
i_rst = 0, i_load = 0, din = 0101, dout = 0101
i_rst = 0, i_load = 0, din = 0101, dout = 1010
i_rst = 0, i_load = 0, din = 0101, dout = 0100
i_rst = 0, i_load = 0, din = 0101, dout = 1000
i_rst = 0, i_load = 0, din = 0101, dout = 0000
注意事项:
循环移位句子:
dout_mid <= {dout_mid7 G d @ ?[MSB - 2 : 0],1'b0};
其间的最低位一定要写成1’b0,假如写成了0,即:
dout_mid <= {dout_mid[MSB - 2 :& E / D C / ] 0],0};
则仿真结果变为:
这意味着,直接赋值0给! 5 O l P 2 n q `dout_mid了。
右移位寄存器
右移位寄存器和左移位寄存器是对称的,便是每一个时钟上升沿到来,1 % 0 8 g 4 Y }都向低位移动一次,这儿也必要重新写了,咱们只需求改其间某条移位句子即可。
这儿又分为循环与不循环,别离点出。
循环右移位
即:
dout_mid <= {dout_mid[MSB - 2 : 0], do C out_mid[MSB - 1]};
改为:
dout_mid <= {dout_mid[0] x ? { g ? h $, dout_ma Y 5 D C mid[MSB - 1 : 1]};
为了照顾新手,还是给出完整规划代码:
`timescale 1ns / 1ps
///////////////v , @ - ! !/////////////* 0 J b A O/////////////////////////////////////////v H 0 */////////////
// Engineer: Reborn Lee
// Module Name: cycle_left_register
// AdF + c Sditional CommeH ; w 6 Cnts:
// https://b[ s M E B 1 2log.csdn.net/reborn_lee
///////////////////////////////////////////////////////6 , v b f d : S///////////////////////////
module shift_register #(parameter MSB = 4)(
inpuO % T =t [MSB - 1 : 0] dinU G F = C,
input i_rst,
input i_load,
input i_clk,
output [MSB - 1 : 0] dout
);
reg [MSB - 1 : 0] dout_mid;
always@(posedge i_clk) begin
if(i_rst) begin
dout_mi5 s }d <= 'd0;
e! f F A } * v ^nd
else if(i_load)p , x { begin
dout_mid <= din;
end
else begin
// dout_mid <= {dout_mid[MSB - 2 : 0], 1'b0}; // normal left shq L g oift
// dout_mid <= {dout_mid[MSB - 2 : 0], dout_mid[MSB - 1]}; //cycle left shift
dout_mid <= {dout_mid[0],dout_mid[MSB - 1 : 1]};
end
end
assign dout = dout_mid;
endmodul~ H a / 1e
仿真波形:
由于仿真文件和上述的循环f = g q M k h 1左移一致,就改一下例化即可,这儿就没必要给出了 ,直接给~ r P ^ 9 _ D +出仿真波形:
监控记载
i_rst = 1, i_load = 0, din = 0000,T Q 9 l dout = xxxx
i_rst = 1, i_load = 0, din = 0000, doutc q d U = 0000
i_rst = 0, i_load = 0, din = 0000,{ 7 5 J F ( ^ * d dout = 0000
iv R l $_rst = 0, i_load = 1, din = 1011, dout = 0000
i_rst = 0, i_lo, 7 5 Kad = 1, din = 1011, dout = 1011
i_rst = 0, i_load = 0, din = 1011, dout = 1011
i_rst = 0, i_load = 0, din = 1011, dout = 1101
i_rst = 0, i_load = 0, din = 1011, dout = 1110
i_rst = 0, i_load = 0, din = 1011s l s { r H L ^ @, dout = 0111
i_rst = 0, i_load = 0, di& Z n * ) ~ rn = 1011, dout = 1011
i_rst = 0, i_load = 0, din = 1011, dout = 1101
i_rst = 0, i_load = 1, din = 0101, dout = 1101
i_rst = 0, i_loaG 2 _ n d , 9 Jd = 1, din = 0101, dout = 0101
i_rst = 0, i_load = 0, din = 0101, dout = 0101
i_rst = 0, i_load = 0, din = 0101, dout = 1010
i_rst = 0, i_load = 0, din = 0101, dout = 0101
i_rst = 0, i_load = 0, din = 0101, dout = 1010
i_rst = 0, i_load = 0, din = 0101, dout = 0101
非循环右% $ :移寄存器
相对于循环右移寄存器来说,只需求改动为:最高位补零即可。
即:
将移位句子:
dout_mid <=u ) U ^ {dout_mid[0],dout_mid[MSB - 1 : 1]};
改为:
dout_k X d ` J - 6mid <= {1'b0,dout_mid[MSB - 1 : 1]};
仿真波形为:K _ ) I g z x % e
监控记载
i_r3 / D cst = 1, i_load| h : Q 9 Q I = 0, din = 0000, dout = xxxx i_rst = 1, i_load = 0, din = 0000, dout = 0000 i_rst = 0, i_load = 0, diT x un = 0000, dout = 0000 i_rst = 0, i_load = 1, din = 1011, dout = 0000 i_rst = 0, i_loF F M M % F ] 4ad = 1, din = 1011, dout = 1011 i_rst = 0, i_load = 0, din =o S Q E - { q 1011, dout = 1011 i_rst = 0, i_load = 0, din = 1011, dout = 0101 i_rst = 0, i_load = 0, din = 1011, dout = 0010 i_rst = 0, i_load = 0, din = 1011, do2 - # ( aut = 0001 i_rst = 0, i_load = 0, din h 1 * k o y = 101F S S x & 3 Q ( z1, dout = 0000 i_rst = 0, i_load = 1, din = 0101, dout = 0000 i_rst = 0, i_load = 1,E g l X x k # din = 0101, dout = 0101 i_rst = 0, i_load = 0, din = 0101, dout = 0101 i_rst = 0, iy ^ *_load = 0, din = 0101, dout = 0010 i_rst = 0, i_load = 0, din = 0101, dout = 0001 i_? T yrst = 0, i_load = 0, din = 0101, dout =$ 1 F $ ( f 0000
串行输入并行输出移位寄存器
该移位寄存器规划具有五个4 + _ Y _ v ]输入和一个n位输出,而且运用参数MSB对规划进行参数化以表明移位寄存器的宽度。 假如MSB为4,则它成为4位移* 4 F ( i o J @ q位寄存器。 假如MSB为8,则它成为8位移n e + : R k x J _位寄存器。
该移位寄存器具有一些关键功能F f { ) )。
-
可经过l / K驱动规划的en信号来启用或禁用 -
驱动dir时可左右移动 -
假如将rstn拉y E X c 为低电平,则会重置移位寄存器,输出将变为0 -
移位寄存器的输入数据值能l = + – V @够经过d引脚操控
因而,这儿没有必要再将左移位以及右移位分开来写了,合在一个规划里,经过一个信号dir操控,dir为0,左移,不然,右移!
规划代码
`timescalJ X C Qe 1ns / 1ps
/////////////////////////S P @/////////////////////////////////////////////////////////
// Engineer: Reborn Lee
// Module Name: shift_register
// https://blog.csdn.n * ~ ^net/reborn_lee
/////////////////////////////////////////. W J ( + R/////////////////////////////////////////
module shift_rey = Cgister+ r L 3 : v l Q -#(parameter MSB = 8)(
input i_clk,
input i_rst,
inpv # Y H S H u }ut i_dir,
input i_en,
input din,+ F b T & [
output reg [MA I zSB - 1 : 0] dout
);
ale O S ( j ( ^ 2 Nways @(posedge i_clk) begin
if (i_rs3 ] B m G 4 xt) begin
// reset
dout <= 'd0;
end
else if (i_en) begin
case(i_dir)
0: begin //left shift
dout <= {doutv Y B W d g[MS| / N j o P lB - 2: 0], din};
end
1: bev , s : Q I )gin
dout <= {din, dout[MSB - 1 : 1]};
end
endcase
end
else begin
dout <= dout;
end
end
endmodule
仿5 I i真代码
`timescale 1ns/1ps
module shift_register_tb;
parameter MSB = 8;
reg i_clk;
reg i_rst;
reg i_dir;
reg i_en;
reg din;
wire [MSB - 1 : 0] dout;
initial begin
i_clk = 0;
forever begin
# 5 i_clk = ~i_clk;
end
end
init@ L ;ial begin
i_rst = 1;~ L @ P d
i_en = 0;
i_dir = 0;
din = 0;
# 18
@(negedge i_clk) begin
i_rst = 0;
i_en = 1;
end
repeat(8) begin
@(negedge i_clk) begin
din = $random;
end
end
@(negedge i_clk) begin
i_rst = 1;
end
#18
i_rst = 0;
i_dir = 1;
repeat(8) begin
@(c h 3 W d Ynegedge iA + = $ + 8 I ( e_clk) begin
din = $random;
end
end
# 20 $finish;
end
shift_register #(.MSB(MSB))inst_shift_reg@ i W u o 4 j 1ister (
.i_clk(i_clk),
.i__ 4 erst(i_rst),[ B , S
.i_df 5 W ^ir(i_dir),
.i_en(i_en),
.din(din),
.dout(dout)
);
endmodule
仿真波形
并行输入串行输出移位寄存器
并行输入串行输出的原理图如下:
该电路由三个串联的D触发器组成。 这意味着,一个D触发器的输出被衔接为下一个D触发器的输入。 一切这些触发器彼此同步,由于相同的时钟# W = % 0 %信号被施加到每个/ g h ; N T触发器。
在该移位寄存器M W ( V & @ G d o中,咱们能够经过将Preset Enable(预置使能)设7 u Q # s 3 B #为1,将并行输入@ 4 { Y应用于每个D触发器。对于时钟信号的每个正沿触发,数据都会从一个级转移到下一个级。 因而,咱们将从最右边的D触发器获P q j Y取串行输出。
于此一起,咱们依然设置一个操控方向的使能信号i_dir,假@ k m x T如i_dir为0,则并行输出左移,取最高位作为串行输出;d ^ ) 5 g z不然,右移,取最低位作为串行输出。
电路规划
//paralell input and serial output shift register
module shift_register#(paQ $ O m y Q &ramb ? D i 8 F ; oeter MSB = 4)(
input i_clk,
input i_load,
input i_dir,
input [MF V E D y ;SB - 1 : 0]+ 5 p l H P W din,
output dout
);
reg [MSB - 1 : 0] q_mid = 0;
always@(posedge i_clk)n g X n 9 $ ` z u begin
if(i_load) begin
q_mid <= din;
end
else be) @ f ; Sgin
case(i_dir)
1'ba t G 7 ~ d0: begin
q_mid <= {q_mid[MSB - 2 : 0], 1'b0}; //no cycle
end
1'b1: begin
q_mid <= {1'b0, q_mid[MSB - 1 : 1]}; //no cycle
end
endcase
end
end
aL D ` O p $ssign dout = i_dir ? (q_mid[0]) : (q_mid[MSB - 1]);
endmodule
仿真文件
`times* ! Bcale` A k * [ n M = 1ns/1ps
module shift_registe{ L + w ur_tb;
parameter MSB = 4;
re4 : S ; ng iW p w + 8 k [ `_clk;
reg i_dir;
reg i_load;# { % D s
reg [MSB - 1 : 0] din;
wire dout;
initial begin
i_clk = 0;
forever begin
# 5 i_clk = ~i_clk;
end
end
initial begin
i_load = 0;
i_dir = 0;
din = $random;
# 18
@(negedge i_clk) begin
i_load = 1;
end
@(negedge i_clk) i_load = 0;
repeat(3) @(negedge i_clk); //finish shift output
din = $rag = d Q 5 D cndom;
i_load = 1;
@(negedge i_clk) i_n ^ ] x b [ b | =load = 0;
i_dir = 1;
repeat(3) begin
@(negedgeQ T d G f 5 i_clk);
end
# 20 $finish;
end
// Monitor valuu 7 T * R =es of these variables and prg E s O * { Dint them into the logfile for debug
initial
$monitor ("i_load=%0b, i_dir=%0b, din=%b, dout = %0b", i_load, i_dir, din, dout);
shift_register #(.MSB(MSB))inst_shift_registeb / Z ! ? ~ ) n .r(
.i_clk(i_clk),
.i_dir(i_dir),
.i_load(i_load),
.din(din),
.dout(dout)
);
endmodule
监控数据
i_loa] Z } d % q od=0, i_dir=0, din=0100, dout = 0
i_load=1, i_dir=0, din=0100, d, . G } * ,out = 0
i_load=0, i_dir=0, din=0100, dout = 0
i_load=0, i_dir=0, din=0100, dout = 1
i_load=0, i_dir=0, din=0100, dout = 0
i_load=1, i_dir=0, din=0001,S R ) G 2 m 8 dout = 0
i_load=0, i_dir=1,K & 3 s t ! c din=0001, dout = 1
i_load=0, i_dir=1, din=0001, dout = 0
参考资料
-
参考资料1 -
参考资料2 -
参考资料3