文章目录
- 写在前面
- 正文
- 左移位寄存器
- 右移位寄存器
- 串行输入并行输出移位寄存器
- 并行输入串行输出移位寄存器
- 参考资料
- 交个朋友
写在前面
- 个人微信公众号:FPGA LAB
- 个人博客首页
- 注:学习交流使用!
正文
在数字电子产品中,移位寄存器是级联的触发器,其中一个触发器的输出引脚q连接到下一个触发器的数据输入引脚(d)。 因为所有触发器都在同一时钟上工作,所以存储在移位寄存器中的位阵列将移位一个位置。
例如,如果一个5位右移寄存器的初始值为10110,并且将移位寄存器的输入绑定到O,则下一个模式将为01011,下一个模式将为00101。
移位寄存器的种类有很多,需要根据需求来设计,但万变不离其宗,都是每一个时钟,寄存器阵列移位一次,下面就盘点各种移位寄存器:
- 左移位寄存器
- 右移位寄存器
- 串行输入并行输出移位寄存器
- 并行输入串行输出移位寄存器
下面就分别认识下吧。
左移位寄存器
所谓的左移,这里约定成网高位移位,这是因为我们通常定义变量都是:
1
2reg [MSB:LSB] VAR;
高位在左,底位在右,因此左移便是向高位移位。
其实这里还可以继续细分,是循环左移还是非循环的呢?
循环左移寄存器
所谓的循环左移,就是将最高位移位到最低位,次高位作为最高位,依次循环。
电路设计:
以四位循环左移为例,给出电路设计Verilog代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37`timescale 1ns / 1ps // // Engineer: Reborn Lee // Module Name: cycle_left_register // Additional Comments: // https://blog.csdn.net/reborn_lee // module cycle_left_register #(parameter MSB = 4)( input [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 <= 'd0; end 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; endmodule
注:里面添加了一个信号,叫装载信号i_load,这个信号有效的时候,将输入din赋值给中间寄存器dout_mid,这样才能实现每一个时钟上升沿来临时,都对输入左移一次。
测试代码
简单给出测试代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77`timescale 1ns / 1ps // // Engineer: Reborn Lee // Module Name: cycle_left_register_tb // https://blog.csdn.net/reborn_lee // module cycle_left_register_tb( ); parameter MSB = 4; reg [MSB - 1 : 0] din; reg i_rst; reg i_clk; reg i_load; wire [MSB - 1 : 0] dout; //generate clock initial begin i_clk = 0; forever begin #5 i_clk = ~i_clk; end end //generate rst and input data initial begin i_rst = 1; din = 0; i_load = 0; # 22 i_rst = 0; @(negedge i_clk) begin din = 'b1011; i_load = 1; end @(negedge i_clk) begin i_load = 0; end repeat(5) @(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_load = %0b, din = %b, dout = %b", i_rst, i_load, din, dout); cycle_left_register #(.MSB(MSB))inst_cycle_left_register( .i_clk(i_clk), .i_rst(i_rst), .i_load(i_load), .din(din), .dout(dout) ); endmodule
给出仿真结果:
监控记录:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19i_rst = 1, i_load = 0, din = 0000, dout = xxxx i_rst = 1, i_load = 0, din = 0000, dout = 0000 i_rst = 0, i_load = 0, din = 0000, dout = 0000 i_rst = 0, i_load = 1, 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, i_load = 0, din = 1011, dout = 1110 i_rst = 0, i_load = 0, din = 1011, 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_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 = 0101 i_rst = 0, i_load = 0, din = 0101, dout = 1010 i_rst = 0, i_load = 0, din = 0101, dout = 0101
非循环左移寄存器
非循环左移对于循环左移来说,就是最高位不移入最低位,而是丢弃,最低位补零。
修改其中移位语句即可:
1
2dout_mid <= {dout_mid[MSB - 2 : 0], dout_mid[MSB - 1]};
改为:
1
2dout_mid <= {dout_mid[MSB - 2 : 0],1'b0};
行为仿真波形
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18i_rst = 1, i_load = 0, din = 0000, dout = xxxx i_rst = 1, i_load = 0, din = 0000, dout = 0000 i_rst = 0, i_load = 0, din = 0000, dout = 0000 i_rst = 0, i_load = 1, 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 = 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 = 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
注意事项:
循环移位语句:
1
2dout_mid <= {dout_mid[MSB - 2 : 0],1'b0};
其中的最低位一定要写成1’b0,如果写成了0,即:
1
2
3dout_mid <= {dout_mid[MSB - 2 : 0],0};
则仿真结果变为:
这意味着,直接赋值0给dout_mid了。
右移位寄存器
右移位寄存器和左移位寄存器是对称的,就是每一个时钟上升沿到来,都向低位移动一次,这里也必要重新写了,我们只需要改其中某条移位语句即可。
这里又分为循环与不循环,分别点出。
循环右移位
即:
1
2dout_mid <= {dout_mid[MSB - 2 : 0], dout_mid[MSB - 1]};
改为:
1
2dout_mid <= {dout_mid[0], dout_mid[MSB - 1 : 1]};
为了照顾新手,还是给出完整设计代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37`timescale 1ns / 1ps // // Engineer: Reborn Lee // Module Name: cycle_left_register // Additional Comments: // https://blog.csdn.net/reborn_lee // module shift_register #(parameter MSB = 4)( input [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 <= 'd0; end else if(i_load) begin dout_mid <= din; end else begin // dout_mid <= {dout_mid[MSB - 2 : 0], 1'b0}; // normal left shift // 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; endmodule
仿真波形:
由于仿真文件和上述的循环左移一致,就改一下例化即可,这里就没必要给出了 ,直接给出仿真波形:
监控记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19i_rst = 1, i_load = 0, din = 0000, dout = xxxx i_rst = 1, i_load = 0, din = 0000, dout = 0000 i_rst = 0, i_load = 0, din = 0000, dout = 0000 i_rst = 0, i_load = 1, 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 = 1101 i_rst = 0, i_load = 0, din = 1011, dout = 1110 i_rst = 0, i_load = 0, din = 1011, dout = 0111 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 = 1, din = 0101, dout = 1101 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 = 0101 i_rst = 0, i_load = 0, din = 0101, dout = 1010 i_rst = 0, i_load = 0, din = 0101, dout = 0101
非循环右移寄存器
相对于循环右移寄存器来说,只需要改动为:最高位补零即可。
即:
将移位语句:
1
2
3dout_mid <= {dout_mid[0],dout_mid[MSB - 1 : 1]};
改为:
1
2dout_mid <= {1'b0,dout_mid[MSB - 1 : 1]};
仿真波形为:
监控记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18i_rst = 1, i_load = 0, din = 0000, dout = xxxx i_rst = 1, i_load = 0, din = 0000, dout = 0000 i_rst = 0, i_load = 0, din = 0000, dout = 0000 i_rst = 0, i_load = 1, 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 = 0101 i_rst = 0, i_load = 0, din = 1011, dout = 0010 i_rst = 0, i_load = 0, din = 1011, dout = 0001 i_rst = 0, i_load = 0, din = 1011, dout = 0000 i_rst = 0, i_load = 1, din = 0101, dout = 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 = 0010 i_rst = 0, i_load = 0, din = 0101, dout = 0001 i_rst = 0, i_load = 0, din = 0101, dout = 0000
串行输入并行输出移位寄存器
该移位寄存器设计具有五个输入和一个n位输出,并且使用参数MSB对设计进行参数化以表示移位寄存器的宽度。 如果MSB为4,则它成为4位移位寄存器。 如果MSB为8,则它成为8位移位寄存器。
该移位寄存器具有一些关键功能。
- 可通过驱动设计的en信号来启用或禁用
- 驱动dir时可左右移动
- 如果将rstn拉为低电平,则会重置移位寄存器,输出将变为0
- 移位寄存器的输入数据值可以通过d引脚控制
因此,这里没有必要再将左移位以及右移位分开来写了,合在一个设计里,通过一个信号dir控制,dir为0,左移,否则,右移!
设计代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43`timescale 1ns / 1ps // // Engineer: Reborn Lee // Module Name: shift_register // https://blog.csdn.net/reborn_lee // module shift_register#(parameter MSB = 8)( input i_clk, input i_rst, input i_dir, input i_en, input din, output reg [MSB - 1 : 0] dout ); always @(posedge i_clk) begin if (i_rst) begin // reset dout <= 'd0; end else if (i_en) begin case(i_dir) 0: begin //left shift dout <= {dout[MSB - 2: 0], din}; end 1: begin dout <= {din, dout[MSB - 1 : 1]}; end endcase end else begin dout <= dout; end end endmodule
仿真代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73`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 initial begin i_rst = 1; 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 @(negedge i_clk) begin din = $random; end end # 20 $finish; end shift_register #(.MSB(MSB))inst_shift_register ( .i_clk(i_clk), .i_rst(i_rst), .i_dir(i_dir), .i_en(i_en), .din(din), .dout(dout) ); endmodule
仿真波形
并行输入串行输出移位寄存器
并行输入串行输出的原理图如下:
该电路由三个串联的D触发器组成。 这意味着,一个D触发器的输出被连接为下一个D触发器的输入。 所有这些触发器彼此同步,因为相同的时钟信号被施加到每个触发器。
在该移位寄存器中,我们可以通过将Preset Enable(预置使能)设为1,将并行输入应用于每个D触发器。对于时钟信号的每个正沿触发,数据都会从一个级转移到下一个级。 因此,我们将从最右边的D触发器获取串行输出。
于此同时,我们仍然设置一个控制方向的使能信号i_dir,如果i_dir为0,则并行输出左移,取最高位作为串行输出;否则,右移,取最低位作为串行输出。
电路设计
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35//paralell input and serial output shift register module shift_register#(parameter MSB = 4)( input i_clk, input i_load, input i_dir, input [MSB - 1 : 0] din, output dout ); reg [MSB - 1 : 0] q_mid = 0; always@(posedge i_clk) begin if(i_load) begin q_mid <= din; end else begin case(i_dir) 1'b0: 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 assign dout = i_dir ? (q_mid[0]) : (q_mid[MSB - 1]); endmodule
仿真文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66`timescale 1ns/1ps module shift_register_tb; parameter MSB = 4; reg i_clk; reg i_dir; reg i_load; 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 = $random; i_load = 1; @(negedge i_clk) i_load = 0; i_dir = 1; repeat(3) begin @(negedge i_clk); end # 20 $finish; end // Monitor values of these variables and print 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_register( .i_clk(i_clk), .i_dir(i_dir), .i_load(i_load), .din(din), .dout(dout) ); endmodule
监控数据
1
2
3
4
5
6
7
8
9i_load=0, i_dir=0, din=0100, dout = 0 i_load=1, i_dir=0, din=0100, dout = 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, dout = 0 i_load=0, i_dir=1, din=0001, dout = 1 i_load=0, i_dir=1, din=0001, dout = 0
参考资料
- 参考资料1
- 参考资料2
- 参考资料3
交个朋友
-
个人微信公众号:FPGA LAB,左下角二维码;
-
知乎:李锐博恩,右下角二维码。
-
FPGA/IC技术交流2020
最后
以上就是可爱电灯胆最近收集整理的关于Verilog设计实例(6)基于Verilog的各种移位寄存器实现的全部内容,更多相关Verilog设计实例(6)基于Verilog内容请搜索靠谱客的其他文章。
发表评论 取消回复