Verilog移位寄存器

HDLBits链接


前言

今天更新一节寄存器相关内容,其中涉及CRC校验的内容是用线性反馈移位寄存器搭建而成的。


基础知识介绍

什么是移位寄存器?

移位寄存器(Shift Register)是一种特殊的寄存器,它能够在时钟脉冲的作用下,将存储的数据向左或向右移动一位或多位。

生活中的类比:

想象一排人站成一队,每个人手里拿着一个球(代表数据0或1)。当听到”移位”的命令时:

  • 每个人把手里的球传给旁边的人
  • 最边上的人要么把球传出去(丢弃),要么从外面接收一个新球

这就是移位寄存器的工作原理!

移位寄存器的分类

  1. 按移位方向分

    • 左移寄存器:数据向左移动
    • 右移寄存器:数据向右移动
    • 双向移位寄存器:既可以左移也可以右移
  2. 按输入输出方式分

    • 串行输入串行输出(SISO)
    • 串行输入并行输出(SIPO)
    • 并行输入串行输出(PISO)
    • 并行输入并行输出(PIPO)
  3. 特殊类型

    • 循环移位寄存器:移出的数据从另一端移回来
    • 算术移位寄存器:保持符号位不变
    • 线性反馈移位寄存器(LFSR):用于产生伪随机序列

为什么需要移位寄存器?

移位寄存器在数字系统中有很多应用:

  • 数据传输:串并转换(串行数据转并行数据)
  • 时序控制:产生特定的时序序列
  • CRC校验:通信中常用的检错码
  • 伪随机数生成:LFSR可以产生近似随机的序列
  • 数据缓冲:临时存储和调整数据流

入门者避坑指南

在设计移位寄存器时,初学者很容易犯一些常见的错误,下面我们来总结一下。

错误1:移位方向搞反

错误表现:

1
2
// ❌ 错误示例:想要右移,结果写成了左移
q <= {q[2:0], 1'b0}; // 这是左移!不是右移!

错误原因分析:

在Verilog中,移位的方向取决于你如何拼接位:

  • 左移:低位补0,高位丢弃 → {q[2:0], 1'b0}
  • 右移:高位补0,低位丢弃 → {1'b0, q[3:1]}

正确做法对比:

1
2
3
4
5
// ✅ 正确示例:右移
q <= {1'b0, q[3:1]};

// ✅ 正确示例:左移
q <= {q[2:0], 1'b0};


错误2:拼接操作符的顺序错误

错误表现:

1
2
// ❌ 错误示例:拼接顺序搞反了
q <= {q[0], q[3:1]}; // 想要循环右移,但顺序错了

错误原因分析:

在拼接操作符 {} 中,信号的顺序是从左到右对应高位到低位。对于循环右移,应该是把最低位移到最高位。

正确做法对比:

1
2
3
4
5
// ✅ 正确示例:循环右移
q <= {q[0], q[3:1]}; // 最低位q[0]变成新的最高位

// ✅ 正确示例:循环左移
q <= {q[2:0], q[3]}; // 最高位q[3]变成新的最低位


错误3:算术移位时没有保持符号位

错误表现:

1
2
// ❌ 错误示例:算术右移时没有正确处理符号位
q <= {1'b0, q[63:1]}; // 对于负数,符号位应该保持为1!

错误原因分析:

对于有符号数的算术右移,符号位(最高位)应该保持不变,而不是简单地补0!

正确做法对比:

1
2
3
4
5
// ✅ 正确示例:算术右移(保持符号位)
q <= {q[63], q[63:1]}; // 符号位q[63]保持不变

// ✅ 正确示例:算术右移8位
q <= {{8{q[63]}}, q[63:8]}; // 复制8次符号位


题库

题目描述1:

构建一个4bit的移位寄存器(右移),含异步复位、同步加载和使能。

  • areset:让寄存器复位为0
  • load:加载4bit数据到移位寄存器中,不移位
  • ena:使能右移
  • q:移位寄存器中的内容

Solution1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module top_module(
input clk,
input areset, // async active-high reset to zero
input load,
input ena,
input [3:0] data,
output reg [3:0] q);

always @(posedge clk or posedge areset)begin
if(areset)begin
q <= 4'b0;
end
else if(load) begin
q <= data;
end
else if(ena)begin
q <= {1'b0,q[3:1]};
end
else begin
q <= q;
end
end

endmodule

题目描述2:

构建一个100位的左右旋转器,同步load,左右旋转需使能。旋转器从另一端输入移位的位元,不像移位器那样丢弃移位出的位元而以零位移位。如果启用,旋转器就会旋转这些位,而不会修改或丢弃它们。

  • load:加载100位的移位寄存器数据
  • ena[1:0]:2’b01 右转1bit; 2’b10 左转1bit;其他情况不转
  • q:旋转器内容

Solution2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module top_module(
input clk,
input load,
input [1:0] ena,
input [99:0] data,
output reg [99:0] q);

always @(posedge clk) begin
if(load) begin
q <= data;
end
else begin
case (ena)
2'b01: q <= {q[0],q[99:1]};
2'b10: q <= {q[98:0],q[99]};
default: q <= q;
endcase
end
end

endmodule

题目描述3:

建立一个64位算术移位寄存器,同步加载。移位器可以左右移位,并按数量选择1位或8位的移位。

  • load:加载数据
  • ena:决定是否移位
  • amount:决定移位方向与数量:2’b00:左移1位;2’b01:左移8位;2’b10:右移1位;2’b11:右移8位
  • q:寄存器内容(输出)

Solution3:

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
module top_module(
input clk,
input load,
input ena,
input [1:0] amount,
input [63:0] data,
output reg [63:0] q);

always @(posedge clk)begin
if(load)begin
q <= data;
end
else begin
if(ena)begin
case(amount)
2'b00: q <= {q[62:0],1'b0};
2'b01: q <= {q[55:0],8'b0};
2'b10: q <= {q[63],q[63:1]};
2'b11: q <= {{8{q[63]}},q[63:8]};
endcase
end
else begin
q <= q;
end
end
end

endmodule

题目描述4:

构造线性移位寄存器,reset应当使LFSR归1。

1

Solution4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module top_module(
input clk,
input reset, // Active-high synchronous reset to 5'h1
output [4:0] q
);

always @(posedge clk)begin
if(reset)begin
q <= 5'h1;
end
else begin
q[4] <= 1'b0 ^ q[0];
q[3] <= q[4];
q[2] <= q[3] ^ q[0];
q[1] <= q[2];
q[0] <= q[1];
end
end

endmodule

题目描述5:

为这个序列电路编写Verilog代码。假设你要在DE1-SoC板上实现这个电路。将R输入连接到SW开关,将时钟连接到密钥[0],将L连接到密钥[1],将Q输出连接到红灯LEDR上。

2

Solution5:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
module top_module (
input [2:0] SW, // R
input [1:0] KEY, // L and clk
output [2:0] LEDR); // Q

wire clk;
assign clk = KEY[0];

always @(posedge clk)begin
if(KEY[1])begin
LEDR[0] <= SW[0];
LEDR[1] <= SW[1];
LEDR[2] <= SW[2];
end
else begin
LEDR[0] <= LEDR[2];
LEDR[1] <= LEDR[0];
LEDR[2] <= LEDR[2] ^ LEDR[1];
end
end

endmodule

题目描述6:

构建一个32位的Galois LFSR,其taps位置为32、22、2和1。

Solution6:

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
module top_module(
input clk,
input reset, // Active-high synchronous reset to 32'h1
output [31:0] q
);
integer i;
always @(posedge clk)begin
if(reset)begin
q <= 32'h1;
end
else begin
for(i=0;i<32;i++)begin
if((i==21)||(i==1)||(i==0))begin
q[i] <= q[i+1] ^ q[0];
end
else if(i==31)begin
q[31] <= 1'b0 ^ q[0];
end
else begin
q[i] <= q[i+1];
end
end
end
end

endmodule

题目描述7:实现如下电路图

3

Solution7:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module top_module (
input clk,
input resetn, // synchronous reset
input in,
output out);

reg [3:0] tmp;
assign out = tmp[3];

always @(posedge clk)begin
if(!resetn)begin
tmp <= 4'h0;
end
else begin
tmp <= {tmp[2:0],in};
end
end

endmodule

题目描述8:实现如下电路图

4

  • Connect the R inputs to the SW switches,
  • clk to KEY[0],
  • E to KEY[1],
  • L to KEY[2], and
  • w to KEY[3].
  • Connect the outputs to the red lights LEDR[3:0].

Solution8:

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
module top_module (
input [3:0] SW,
input [3:0] KEY,
output [3:0] LEDR
);

MUXDFF u1(.clk(KEY[0]),
.w(KEY[3]),
.R(SW[3]),
.E(KEY[1]),
.L(KEY[2]),
.Q(LEDR[3]));

MUXDFF u2(.clk(KEY[0]),
.w(LEDR[3]),
.R(SW[2]),
.E(KEY[1]),
.L(KEY[2]),
.Q(LEDR[2]));

MUXDFF u3(.clk(KEY[0]),
.w(LEDR[2]),
.R(SW[1]),
.E(KEY[1]),
.L(KEY[2]),
.Q(LEDR[1]));

MUXDFF u4(.clk(KEY[0]),
.w(LEDR[1]),
.R(SW[0]),
.E(KEY[1]),
.L(KEY[2]),
.Q(LEDR[0]));

endmodule

module MUXDFF (
input clk,
input w,R,E,L,
output Q
);
wire tmp;
assign tmp = E ? w : Q;
always @(posedge clk)begin
Q <= L ? R : tmp;
end

endmodule

题目描述9:

在这个问题中,你将为一个8x1存储器设计一个电路,在这个电路中,写入到存储器是通过移位来完成的,而读取是”随机访问”,就像在一个典型的RAM中一样。然后您将使用该电路实现一个3输入逻辑功能。

首先,用8个d类型触发器创建一个8位移位寄存器。标记为Q[0]到Q[7]。移位寄存器输入称为S,输入Q[0] (MSB先移位)。使能输入enable控制是否移位,扩展电路使其有3个额外的输入A,B,C和一个输出Z。电路的行为应该如下:当ABC为000时,Z=Q[0],当ABC为001时,Z=Q[1],以此类推。你的电路应该只包含8位移位寄存器和多路复用器。这个电路称为3输入查找表(LUT)。

Solution9:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
module top_module (
input clk,
input enable,
input S,
input A, B, C,
output Z );

reg [7:0] Q;
always @(posedge clk)begin
if(enable)begin
Q <= {Q[6:0],S};
end
else begin
Q <= Q;
end
end

assign Z = Q[{A,B,C}];

endmodule

小结

今天更新了移位寄存器部分的答案,注意最后一题用了一些技巧来简化代码书写,但实现时电路并无差异,体现了HDL中”Describe”的特性。