HDLBits——Shift Registers

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]

错过:

  1. 没有注意到 areset 是 async(异步)复位,敏感信号表没有加 areset。
  2. 排除了 areset 和 load 情况外,就直接右移了,但右移也需要使能信号,否则值不变。
  3. 右移写成了 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

错过:

  1. 列举 case 的情况时忘了加 2b’。
  2. 片选时只写了 [],忘了写是哪个信号。
  3. 算术移位寄存器右移补的不是 0 是符号位,左移都补 0。
  4. 移八位时没改 q<={{8{q[63]}},q[63:8]}; 还写的是 q<={{8{q[63]}},q[63:1]};

Timing Diagram:

image-20211213162556106

image-20211213162613967


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。

image-20211213163706596


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:

image-20211213164623742


Problem 110 3-bit LFSR

Requirement:

相比于上一题 5-bit 线性移位寄存器,本题原理上相同,但作者希望我们结合实际的板子(DE1-SoC,可能是一个教学板)和其外部接口(KEY&LED),实现一个线性移位寄存器电路。

image-20211219215836800

使用 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)

image-20211213163706596

例如 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:

实现下图中的电路:

image-20211220205121394


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 移位寄存器电路:

image-20211220212307870

为移位寄存器编写一个顶层 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。

改进:

  1. 可以使用 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
    
  2. 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:

image-20211220221912768



113 题改进思路来源于 LogicJitterGibbs 博主。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值