HDLBits-Circuits学习小结(六)移位寄存器(shift registers)

1 简单的移位寄存器

1.1 构建一个4位移位寄存器

构建一个4位移位寄存器(右移位),具有异步置位、同步置数和使能。
areset:将移位寄存器置零。
load:用data[3:0]载入移位寄存器而不是移位。
ena:向右移位(q[3]变为零,q[0]被移出去,消失)。
q:移位寄存器的输出。
如果load和ena同时输入为1,则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)
            q <= 0;
        else if (load)
            q <= data;
        else if (ena)
            q <= {1'b0,q[3:1]};
        else
            q <= q;       
    end

endmodule

official solution:

module top_module(
	input clk,
	input areset,
	input load,
	input ena,
	input [3:0] data,
	output reg [3:0] q);
	
	// Asynchronous reset: Notice the sensitivity list.
	// The shift register has four modes:
	//   reset
	//   load
	//   enable shift
	//   idle -- preserve q (i.e., DFFs)
	always @(posedge clk, posedge areset) begin
		if (areset)		// reset
			q <= 0;
		else if (load)	// load
			q <= data;
		else if (ena)	// shift is enabled
			q <= q[3:1];	// Use vector part select to express a shift.
	end
	
endmodule

1.2 构建一个100位的左/右旋转器

构建一个具有同步置数和左/右使能功能的100位左/右旋转器。旋转器从寄存器的一端移入另一端移出的位,这与移位器丢弃移出的位并移入零不同。 如果使能端有效,旋转器将旋转这些位,并且不会修改/丢弃这些位。

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)
            q <= data;
        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

1.3 建立一个64位算术移位寄存器

建立一个具有同步负载的64位算术移位寄存器。移位器可以向左和向右移位1或8位的位置(按数量选择)。

算术右移将移位寄存器中数字的符号位(在这种情况下为q [63])移位,而不是通过逻辑右移完成的零移位。
关于算术右移的另一种思考方式是,它假定要移位的数字是带符号的并保留符号,以便算术右移将带符号的数除以2的幂。

逻辑左移和算术左移之间没有区别。

load:向移位寄存器加载数据[63:0],而不是进行移位。
ena:选择是否移动。
amount:选择要移动的方向和移动量:

  • 2’b00:向左移1位。
  • 2’b01:向左移8位。
  • 2’b10:右移1位。
  • 2’b11:向右移8位。

提示:5位数字11000算术右移1是11100,而逻辑右移将产生01100。
类似地,算术右移1的5位数字01000算术右移为00100,并且逻辑右移将产生相同的结果,因为原始数字为非负数。

关于算术移位和逻辑移位,可以看这篇文章,注意:

  • 左移的规则:丢弃最高位,0补最低位;
  • 算术右移的规则:按二进制形式把所有的数字向右移动对应的位数,低位移出(舍弃),高位的空位补符号位;
  • 逻辑右移的规则:忽略了符号位扩展,0补最高位,填充时不管左边的数字是正是负都用0来填充。

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)
            q <= data;
        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]};
                	default: q <= q;
            	endcase
            end
            else
                q <= q;
        end
    end

endmodule

2 线性反馈移位寄存器(重要)

2.1 构建5位最大长度的Galois LFSR

线性反馈移位寄存器,即linear feedback shift register(LFSR),是通常带有几个XOR门的移位寄存器,以产生移位寄存器的下一个状态。 Galois LFSR是一种特殊的设置,其中带有“抽头tap”的位与输出位进行异或运算以产生其下一个值,而没有移动“tap”位位置。如果仔细选择tap位置,LFSR可以设为“最大长度”。n位的最大长度LFSR在重复之前循环经过2n-1个状态(永远不会达到全零状态)。

下图显示了一个5位最大长度的Galois LFSR,在位置5和3处有抽头,即tap(抽头“tap”位置通常从1开始编号)。
请注意,为保持一致性,我在位置5处绘制了XOR门,但XOR门输入之一为0。

5-L
提示:从1开始的前几个状态是00001、10100、01010、00101,… LFSR在返回00001之前应循环经过31个状态。

这道题目关键在于理解这句话:Galois LFSR是一种特殊的设置,其中带有“抽头tap”的位与输出位进行异或运算以产生其下一个值,而没有移动“tap”位位置。

solution:

module top_module(
    input clk,
    input reset,    // Active-high synchronous reset to 5'h1
    output [4:0] q
); 
 
    integer i;
    always @(posedge clk) begin
        if (reset)
            q <= 5'b00001;
        else begin
            q <= {q[0],q[4],q[3]^q[0],q[2],q[1]};
        end
    end
endmodule

official solution(⭐️):

module top_module(
	input clk,
	input reset,
	output reg [4:0] q);
	
	reg [4:0] q_next;		// q_next is not a register

	// Convenience: Create a combinational block of logic that computes
	// what the next value should be. For shorter code, I first shift
	// all of the values and then override the two bit positions that have taps.
	// A logic synthesizer creates a circuit that behaves as if the code were
	// executed sequentially, so later assignments override earlier ones.
	// Combinational always block: Use blocking assignments.
	always @(*) begin
		q_next = q[4:1];	// Shift all the bits. This is incorrect for q_next[4] and q_next[2]
		q_next[4] = q[0];	// Give q_next[4] and q_next[2] their correct assignments
		q_next[2] = q[3] ^ q[0];
	end
	
	
	// This is just a set of DFFs. I chose to compute the connections between the
	// DFFs above in its own combinational always block, but you can combine them if you wish.
	// You'll get the same circuit either way.
	// Edge-triggered always block: Use non-blocking assignments.
	always @(posedge clk) begin
		if (reset)
			q <= 5'h1;
		else
			q <= q_next;
	end
	
endmodule

2.2 LFSR的示例

为下面这一时序电路(该电路在这里出现过)编写Verilog代码(可以使用子模块,但顶层必须命名为top_module)。 假设将在DE1-SoC板上实现该电路, 将R输入连接到SW开关,将Clock连接到KEY [0],将L输入到KEY [1],将Q输出连接到红灯LEDR。

在这里插入图片描述
提示:该电路是线性反馈移位寄存器(LFSR)的示例。最大周期LFSR可用于生成伪随机数,因为它在重复之前会循环2n-1个组合。全零组合不会出现在此序列中。

solution:

module top_module (
	input [2:0] SW,      // R
	input [1:0] KEY,     // L and clk
	output [2:0] LEDR);  // Q
	
    wire [2:0] temp;
    assign temp = KEY[1] ? SW :  {LEDR[2]^LEDR[1],LEDR[0],LEDR[2]};
    
    always @(posedge KEY[0]) begin
        LEDR <= temp;
    end

endmodule

2.3 构建32位Galois LFSR

在位32、22、2和1处用抽头构建32位Galois LFSR。

提示:这足够长,以至于您要使用向量,而不是32个DFF实例化。

module top_module(
    input clk,
    input reset,    // Active-high synchronous reset to 32'h1
    output [31:0] q
); 
    reg [31:0] q_next;
    always @(*) begin
        q_next <= q[31:1];
        q_next[31] <= q[0];
        q_next[21] <= q[22]^q[0];
        q_next[1] <= q[2]^q[0];
        q_next[0] <= q[1]^q[0];
    end
    
    always @(posedge clk) begin
        if (reset)
            q <= 32'h1;
        else
            q <= q_next;
    end

endmodule

2.4 移位寄存器电路的实现

实现下面的电路:

m2014
这里我采用的是例化D触发器的方式,官方给的参考利用的是移位寄存器的方法,更加简洁。

solution:

module top_module(
    input clk,
    input resetn,   // synchronous reset
    input in,
    output out);
    
    reg q1,q2,q3,q4;
    DFF d1 (clk,resetn,in,q1);
    DFF d2 (clk,resetn,q1,q2);
    DFF d3 (clk,resetn,q2,q3);
    DFF d4 (clk,resetn,q3,q4);
    
    always @(posedge clk) begin
        if (resetn)
            out <= 0;
        else
            out <= q4;
    end     

endmodule
        
module DFF(
    input clk,
    input resetn,
    input in,
    output q);
    
    always @(posedge clk) begin
        if (resetn)
            q <= 0;
        else
            q <= in;
    end
    
endmodule

official solution(⭐️):

module top_module (
	input clk,
	input resetn,
	input in,
	output out
);

	reg [3:0] sr;
	
	// Create a shift register named sr. It shifts in "in".
	always @(posedge clk) begin
		if (~resetn)		// Synchronous active-low reset
			sr <= 0;
		else 
			sr <= {sr[2:0], in};
	end
	
	assign out = sr[3];		// Output the final bit (sr[3])

endmodule

考虑如下所示的n位移位寄存器电路:

2
假设n = 4,则为移位寄存器编写一个顶级Verilog模块(名为top_module)。在该顶级模块中实例化MUXDFF子电路的四个副本。假设我们要在DE2板上实现该电路。

  • 将R输入连接到SW开关,
  • clk连接到KEY [0],
  • E连接到KEY [1],
  • L连接到KEY [2],
  • w连接到KEY [3],
  • 将输出连接到红灯LEDR [3:0]

solution:

module top_module(
    input [3:0] SW,
    input [3:0] KEY,
    output [3:0] LEDR
); //
    MUXDFF m0 (KEY[0],KEY[1],KEY[2],KEY[3],SW[3],LEDR[3]);
    MUXDFF m1 (KEY[0],KEY[1],KEY[2],LEDR[3],SW[2],LEDR[2]);
    MUXDFF m2 (KEY[0],KEY[1],KEY[2],LEDR[2],SW[1],LEDR[1]);
    MUXDFF m3 (KEY[0],KEY[1],KEY[2],LEDR[1],SW[0],LEDR[0]);

endmodule

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

2.5 设计一个用于8x1存储器的电路

⭐️

在这个问题中,您将设计一个用于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))。

这道题突破口在于:该存储器的写入是通过移入位完成的,并且是先从低位输入的。

module top_module (
    input clk,
    input enable,
    input S,
    input A, B, C,
    output Z); 
    
    wire [2:0] abc;
    reg [7:0] temp;
    
    assign abc = {A,B,C};
    assign Z = temp[abc];
    
    always @(posedge clk) begin
        if (enable)
            temp <= {temp[6:0],S};
        else
            temp <= temp;
    end
    
endmodule

official solution(⭐️):

module top_module (
	input clk,
	input enable,
	input S,
	
	input A, B, C,
	output reg Z
);

	reg [7:0] q;
	
	// The final circuit is a shift register attached to a 8-to-1 mux.
	
	// Create a 8-to-1 mux that chooses one of the bits of q based on the three-bit number {A,B,C}:
	// There are many other ways you could write a 8-to-1 mux
	// (e.g., combinational always block -> case statement with 8 cases).
	assign Z = q[ {A, B, C} ];

	// Edge-triggered always block: This is a standard shift register (named q) with enable.
	// When enabled, shift to the left by 1 (discarding q[7] and and shifting in S).
	always @(posedge clk) begin
		if (enable)
			q <= {q[6:0], S};	
	end
	
endmodule
  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值