HDLBits——Shift Registers
Problem 106 4-bit shift register
Requirement:
设计一个 4bit 异步复位,拥有同步置位(load)和使能的右移移位寄存器。
- areset : 寄存器复位为 0。
- load : 将 data[3:0] 输入至移位寄存器中而不是移位。
- ena : 使能信号,控制向右移动(q[0] 移出消失,q[3] 变为0)。
- q : 移位寄存器中的数据。
如果 load 和 ena 输入同时为高,则 load 输入具有更高的优先级。
Solution:
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
PS:本质就是 q[3]q[2]q[1]q[0] —> 0q[3]q[2]q[1]
错过:
- 没有注意到 areset 是 async(异步)复位,敏感信号表没有加 areset。
- 排除了 areset 和 load 情况外,就直接右移了,但右移也需要使能信号,否则值不变。
- 右移写成了 q<={1’b0,q[2:0]}; 应该是 q[3:1]。
Problem 107 Left/right rotator(循环移位)
Requirement:
设计一个 100bit 的可左移或右移的移位寄存器,附带同步置位和左移或右移的使能信号。本题中,移位寄存器在左移或右移时,不同于 Problem106 的补 0 和直接舍弃某一 bit 位,本题是要求在 100bit 内循环移动,不舍弃某一 bit 位同时也不补 0。
比如说右移 1bit,在 Problem106 就是补 0 和丢弃 q[0],而在本题中左移 1bit 为 {q[0], q[99:1]}。
- load:load 信号将 data[99:0] 输入至寄存器内。
- ena[1:0]:信号选择是否移位和移位的具体方向。
- 2’b01 循环右移一位。
- 2’b10 循环左移一位。
- 2’b00 和 2’b11 不移动。
- q:移位后寄存器内的数据。
Solution:
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 if (ena==2'b01) begin
q<={q[0],q[99:1]};
end
else if (ena==2'b10) begin
q<={q[98:0],q[99]};
end
else begin
q<=q;
end
end
endmodule
Problem 108 Left/right arithmetic shift by 1 or 8
Requirement:
设计一个 64-bit 带同步置位的算术移位寄存器,该寄存器可以由 amount 控制来移动方向和每次移动的次数。算术右移移位寄存器中的符号位 q [63] 移位,不像是逻辑右移中进行补零的操作。而是保留符号位后再进行移位。
例如:
一个 5-bit 值为 11000 的寄存器算术右移一位后为 11100, 而逻辑右移后为 01100。
而一个 5-bit 值为 01000 的寄存器算术右移一位后为 00100,逻辑右移会产生同样的结果,逻辑移位寄存器和算术左移移位寄存器没有区别。
- load :置位信号。
- ena :使能信号,来选择是否移位。
- amount :选择移位的方向和移位的个数。
- 2’b00 : 左移1bit。
- 2’b01 : 左移8bit。
- 2’b10 : 右移1bit。
- 2’b11 : 右移8bit。
- q : 寄存器中的数据。
Solution:
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 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]};
default: q<=q;
endcase
end
else q<=q;
end
endmodule
错过:
- 列举 case 的情况时忘了加 2b’。
- 片选时只写了 [],忘了写是哪个信号。
- 算术移位寄存器右移补的不是 0 是符号位,左移都补 0。
- 移八位时没改 q<={{8{q[63]}},q[63:8]}; 还写的是 q<={{8{q[63]}},q[63:1]};
Timing Diagram:
Problem 109 5-bit LFSR
Requirement:
线性反馈移位寄存器(LFSR)是通常带有几个 XOR 门来产生下一状态的移位寄存器,Galois LFSR(伽罗瓦线性反馈移位寄存器) 是一个特殊的移位寄存器。其中带有"tap"位的位置与输出位 XOR 产生下一个值,该值没有"tap"位标志的正常移位。"tap"位置经过仔细选择后,可使 LFSR 为最大长度,n 位的最大长度 LFSR在重复之前循环通过 2^n -1 个状态(永远不会达到全零状态)。
下图显示了一个 5 位最大长度 Galois LFSR,其 tap 位位于位置 5 和 3(tap 位置通常从 1 开始编号)。为了一致性,在位置 5 绘制了 XOR 门,但 XOR 门输入之一是 0,复位信号使 LFSR 复位为 1。
Solution:
module top_module(
input clk,
input reset, // Active-high synchronous reset to 5'h1
output reg [4:0] q
);
always @(posedge clk) begin
if(reset) begin
q<=5'h1;
end
else begin
q<={q[0]^0,q[4],q[3]^q[0],q[2],q[1]};
end
end
endmodule
Timing Diagram:
Problem 110 3-bit LFSR
Requirement:
相比于上一题 5-bit 线性移位寄存器,本题原理上相同,但作者希望我们结合实际的板子(DE1-SoC,可能是一个教学板)和其外部接口(KEY&LED),实现一个线性移位寄存器电路。
使用 Verilog 实现上图中的时序电路,可以使用子模块进行构建,但顶层要命名为 top_module。假设要在 DE1-SoC 教学板上实现这个电路,将输入端口 r 连接到板子上的拨动开关,clock 端口接到按键 KEY[0],端口 L 接到按键 KEY[1],输出端口 Q 连接到板子上的红色 LED。
Solution:
module top_module (
input [2:0] SW, // R
input [1:0] KEY, // L and clk
output reg [2:0] LEDR); // Q
wire l,clk;
assign clk = KEY[0];
assign l = KEY[1];
always @(posedge clk) begin
LEDR[0] <= l?SW[0]:LEDR[2];
LEDR[1] <= l?SW[1]:LEDR[0];
LEDR[2] <= l?SW[2]:LEDR[1]^LEDR[2];
end
endmodule
也可:
wire [2:0] d = l?SW:{LEDR[1]^LEDR[2],LEDR[0],LEDR[2]};
always @(posedge clk)begin
LEDR <= d;
end
注意 [2:0] 的顺序是从左到右是 2 到 0。
Problem 111 32-bit LFSR
Requirement:
参考 109 题中的 5bit LFSR,实现一个 32bit Galois LFSR,抽头点为 32,22,2,1。
提示:32bit 的 LFSR 最好使用向量实现,而不是例化 32 个触发器。
Solution:
module top_module(
input clk,
input reset, // Active-high synchronous reset to 32'h1
output reg [31:0] q
);
always @(posedge clk) begin
if (reset) begin
q<=32'h1;
end
else begin// 32,22,2,1
q<={q[0]^1'b0,q[31:23],q[22]^q[0],q[21:3],q[2]^q[0],q[1]^q[0]};
end
end
endmodule
PS:抽头点(tap point)
例如 5bit Galois LFSR 图中有两个触发器的输入为前级输出与 q[0] 的异或,这些位置被称为 tap position,109 题的抽头点为 5,3。也就是抽头点指的就是第5个,第3个寄存器的输入经过了异或逻辑。
转化到本题就是
- q[21] <= q[22]^q[0]
- q[1] <= q[2]^q[0]
- q[0] <= q[1]^q[0]
Galois LFSR 即在普通的循环左移的基础上(q <= {q[0],q[31:1]};)把 tap 点的输出替换成异或后的结果。
Problem 112 Shift Register(Exams/m2014 q4k)
Requirement:
实现下图中的电路:
Solution:
module top_module (
input clk,
input resetn, // synchronous reset
input in,
output out);
reg [3:0] q;
assign out = q[0];
always @(posedge clk) begin
if(~resetn) begin
q<=4'h0;
end
else begin
q<={in,q[3:1]};
end
end
endmodule
PS:resetn 表示低位触发,从图也能看出来。
Problem 113 Shift Register(Exams/m2014 q4b)
Requirement:
实现下图中的 n-bit 移位寄存器电路:
为移位寄存器编写一个顶层 Verilog 模块(名为 top_module),假设 n = 4,在顶层模块中实例化 MUXDFF(多路复用器) 子电路的四个副本。
- 将 R 输入连接到 SW 开关;
- clk 到 KEY[0];
- E 到 KEY[1];
- L 到 KEY[2];
- w 到 KEY[3];
- output 到红灯 LEDR[3:0]。
Solution:
module top_module (
input [3:0] SW,
input [3:0] KEY,
output [3:0] LEDR
);
MUXDFF M0(KEY[0],LEDR[1],SW[0],KEY[1],KEY[2],LEDR[0]);
MUXDFF M1(KEY[0],LEDR[2],SW[1],KEY[1],KEY[2],LEDR[1]);
MUXDFF M2(KEY[0],LEDR[3],SW[2],KEY[1],KEY[2],LEDR[2]);
MUXDFF M3(KEY[0],KEY[3],SW[3],KEY[1],KEY[2],LEDR[3]);
endmodule
module MUXDFF (
input clk,
input w, R, E, L,
output reg Q
);
always @(posedge clk) begin
Q <= L?(R):(E?w:Q);
end
endmodule
错过:4 个 MUXDFF 的 w 端口输入是不一样的,我都写成了 w。
改进:
-
可以使用 generate 实例化子模块,便于用于子模块数量较多的情况,修改 i 的上限即可,但输入向量 w_input 还是需要手动构建。
wire [3:0] w_input = {KEY[3],LEDR[3],LEDR[2],LEDR[1]}; generate genvar i; for(i=0;i<4;i=i+1) begin:muxdff MUXDFF ( .clk(KEY[0]), .w(w_input[i]), .R(SW[i]), .E(KEY[1]), .L(KEY[2]), .Q(LEDR[i]) ); end endgenerate
-
MUXDFF 子模块最好把组合逻辑和时序逻辑分开实现。
module MUXDFF ( input clk, input w, R, E, L, output Q ); reg Q_r; wire d_in = (L)? R: (E)? w: Q_r; always@(posedge clk)begin Q_r <= d_in; end assign Q = Q_r; endmodule
如果没有需要例化子模块的要求的话:
always @(posedge KEY[0])
LEDR <= KEY[2] ? SW : (KEY[1] ? {KEY[3], LEDR[3:1]} : LEDR);
endmodule
Problem 114 Shift Register(Exams/m2014 q4k)
Requirement:
本题中实现的是一个和 8x1 结构的存储体相关的电路。存储的输入通过移入比特进行,存储的读取类似于传统 RAM 中的随机读取,即可以通过 3 个输入端口指定读出比特的位置。
首先通过 8 个触发器实现一个 8bit 深的移位寄存器,8个寄存器的输出依次为 Q[0]…Q[7]。移位寄存器的输入为 S,输入首先会填充到 MSB(最高位)Q[0]。enable 信号控制移位,当其有效时输入数据并移位。此外,该电路还有三个输入端口 A,B,C 以及一个输出端口 Z。电路的功能如下:当 ABC = 000 时,Z = Q[0],当 ABC = 001 时,Z = Q[1],以此类推。你的电路中只能包括一个 8bit 移位寄存器以及一个多路选择器。(这就是一个三输入查找表 LUT 电路)
Solution:
module top_module (
input clk,
input enable,
input S,
input A, B, C,
output reg Z );
reg[7:0] shift_Register;
always @(posedge clk) begin
if(enable) begin
shift_Register <= {shift_Register[6:0],S};
end
end
always @(*) begin
case({A,B,C})
3'b000:Z = shift_Register[0];
3'b001:Z = shift_Register[1];
3'b010:Z = shift_Register[2];
3'b011:Z = shift_Register[3];
3'b100:Z = shift_Register[4];
3'b101:Z = shift_Register[5];
3'b110:Z = shift_Register[6];
3'b111:Z = shift_Register[7];
default:Z = 1'b0;
endcase
end
endmodule
PS:我的理解,输入 S 首先会填充到 MSB Q[0],暗示是左移的移位寄存器,且低位反而是代表高值(小端存储)。
大佬理解:
本题中右移和左移都可以,右移相当于 shift_reg[7] 为最高位,在输出时和题目的定义有些不同,ABC = 0 时,Z=shift_reg[7]。如果左移,shift_reg[0] 为最高位,此时输出比特位的顺序和 ABC 的值相同。
{S,shift_reg[7:1]} 或者 {shift_reg[6:0],S} 都是 Q1<=Q0 移动。
shift_reg={Q0,Q1,Q7} 右移,shift_reg={S,Q0,Q6}={S,shift_reg[7:1]} MSB在左侧,000 对应 [7]。
shift_reg={Q7,Q6,Q0} 左移,shift_reg={Q6,Q5,S}={shift_reg[6:0],S} MSB在右侧,000 对应[0]。
本质都是 Q1<=Q0 移动。
改进:
不用 case 语法,直接 assign Z = q[{A,B,C}];
Timing Diagram:
113 题改进思路来源于 LogicJitterGibbs 博主。