HDLbits day8

2、counters

2.1、Four-bit binary counter

构建一个从 0 到 15(含)计数的 4 位二进制计数器,周期为 16。复位输入是同步的,应将计数器复位为 0。

module top_module (
    input clk,
    input reset,      // Synchronous active-high reset
    output [3:0] q);
    
    //always@(posedge clk or posedge reset)//异步
    always@(posedge clk)//同步
        begin
            if(reset)
                q<=4'b0000;
            else
                q<=q+4'b0001;
        end

endmodule

2.2、Decade counter

构建一个从 0 到 9(含)计数的十进制计数器,周期为 10。复位输入是同步的,应将计数器复位为 0。

module top_module (
    input clk,
    input reset,        // Synchronous active-high reset
    output [3:0] q);

    always@(posedge clk)
        begin
            //if(reset or q>=4'b1001)错误写法
            if(reset || q>=4'b1001)
                q<=1'b0;
            else
                q<=q+1'b1;
        end

endmodule

2.3、Decade counter again

制作一个从 1 到 10 的十年计数器,包括 1 到 10。复位输入是同步的,应将计数器复位为 1。

module top_module (
    input clk,
    input reset,
    output [3:0] q);

    //assign q=1'b1;错误写法
    initial
        q=1'b1;
    always@(posedge clk)
        begin
            if(reset || q>=4'd10)
                q<=1'b1;
            else
                q<=q+1'b1;
        end
endmodule

2.4、Slow decade counter

构建一个从 0 到 9 计数的十进制计数器,周期为 10。复位输入是同步的,应该将计数器复位为 0。我们希望能够暂停计数器,而不是总是在每个时钟周期递增,所以slowena输入指示计数器何时应该增加。

1)这是一个带有启用控制信号的常规十进制计数器

2)由图可以看出sslowena信号为高电平时,q+1;sslowena信号为低电平时,q保持

module top_module (
    input clk,
    input slowena,
    input reset,
    output [3:0] q);
    
    always@(posedge clk)
        begin
            if(reset)
                q<=1'b0;
            else if(slowena)
                begin
                    if(q>=4'd9)
                        q<=1'b0;
                    else
                        q<=q+1'b1;
                end
            else
                q<=q;
        end
    
endmodule

2.5、Counter 1-12

设计一个具有以下输入和输出的 1-12 计数器:

  • 复位同步高电平有效复位,强制计数器为 1
  • 启用设置高以使计数器运行
  • Clk正边沿触发时钟输入
  • Q[3:0]计数器的输出
  • c_enable, c_load, c_d[3:0]控制信号进入提供的 4 位计数器,因此可以验证正确的操作。

您有以下可用组件:

  • 下面的 4 位二进制计数器 ( count4 ),它具有启用和同步并行加载输入(加载的优先级高于启用)。count4模块提供给您在你的电路中实例化它。
  • 逻辑门
module count4(
	input clk,        //时钟
	input enable,   //启用
	input load,      //同步并行加载输入
	input [3:0] d,    //
	output reg [3:0] Q
);
c_enable 、c_loadc_d输出是分别进入内部计数器的enableloadd输入的信号。它们的目的是允许检查这些信号的正确性。
module top_module (
    input clk,//时钟
    input reset,//复位
    input enable,//启用
    output [3:0] Q,//计数
    output c_enable,
    output c_load,
    output [3:0] c_d
); //

    //count4 the_counter (clk, c_enable, c_load, c_d /*, ... */ );
    assign c_enable=enable;//检测使能端
    assign c_load= (Q>=4'd12 && enable==1'b1)|reset;
    //load为检测是否要加载数据,只有当复位,或者计数到最大值且使能端为1,才可以加载数据
    assign c_d=c_load?4'd1:4'd0;//判断有没有加载数据
    count4 the_counter (clk, c_enable, c_load, c_d , Q);
        

endmodule

2.6、Counter 1000

从 1000 Hz 时钟导出一个称为OneHertz的 1 Hz 信号,该信号可用于驱动一组小时/分钟/秒计数器的启用信号,以创建数字挂钟。由于我们希望时钟每秒计数一次,因此OneHertz信号必须每秒准确地断言一个周期。使用模 10 (BCD) 计数器和尽可能少的其他门构建分频器。还要从您使用的每个 BCD 计数器输出使能信号(c_enable[0] 为最快的计数器,c_enable[2] 为最慢的)。

为您提供以下 BCD 计数器Enable必须为高电平才能使计数器运行。复位是同步的并设置为高以强制计数器为零。电路中的所有计数器必须直接使用相同的 1000 Hz 信号。

module bcdcount (
	input clk,
	input reset,
	input enable,
	output reg [3:0] Q
);

题目使用3个0-9的计数器来实现时钟分频功能。若仅使用一个bcdcount分频,每计数10次,OneHertz输出为高,那么可以实现10分频,即100Hz。如果counter0每计数10次,counter1计数1次,那么counter1计数10次时OneHertz为高,则可以实现100分频,即10Hz。同理,三个bcdcount可以实现1000分频,即1Hz。

1)采用组合逻辑电路使能三个bcdcount,容易理解,reset不为1时,counter0始终在计数,c_enable[0]=1’b1;
2)仅当counter0计数10次(q0==4’d9)时,counter1计数1次,也就是counter0计数10个时钟周期后,下一时钟周期counter1计数,c_enable[1]=1’b1;
3)当counter1计数10次时(q1==4’d9),counter1会保存q1==4’d9十个时钟周期才会变回0,即需要counter0==4’d9才会再次变化。所以counter2有效的时钟周期为counter1==4’d9的情况下,counted0==4’d9的下一时钟周期。所以q1==4’d9 && q0==4’d9时c_enable[2]=1’b1。
4)根据最终得到的波形图可以看到,要求的OneHertz输出并不是频率为1Hz、占空比为50%的时钟,而是频率为1Hz、宽度为一个clk时钟周期的的脉冲。所以,OneHertz为高的条件为q2==4’d9 && q1==4’d9 && q0==4’d9。

module top_module (
    input clk,
    input reset,
    output OneHertz,
    output [2:0] c_enable
); //

    reg[3:0] q1,q2,q3;
    assign c_enable[0]=1'b1;
    assign c_enable[1]= (q1==4'd9)?1'b1:1'b0;
    assign c_enable[2]= (q1==4'd9 && q2==4'd9)?1'b1:1'b0;
    assign OneHertz= (q1==4'd9 && q2==4'd9 && q3==4'd9)?1'b1:1'b0;
    bcdcount counter0 (clk, reset, c_enable[0],q1);
    bcdcount counter1 (clk, reset, c_enable[1],q2);
    bcdcount counter2 (clk, reset, c_enable[2],q3);

endmodule

2.7、4-digit decimal counter

构建一个4位数的BCD(二进制编码的十进制)计数器。每个十进制数字使用4位进行编码:q[3:0]是个位,q[7:4]是十位,以此类推。对ena[3:1],要输出一个使能信号,指示个十、百、千位什么时候进位,即十、百、千位什么时候加一。

您可能需要实例化一些十进制计数器

1)q[3:0]从0计数到9,当其为4'd9时,下一个时钟周期,q[7:4]加1;

2)遇到reset=1时,q复位为0

module top_module (
    input clk,
    input reset,   // Synchronous active-high reset
    output [3:1] ena,
    output [15:0] q);
    
    assign ena[1] = (q[3:0]==4'd9)?1'b1:1'b0;
    assign ena[2] = (q[3:0]==4'd9 && q[7:4]==4'd9)?1'b1:1'b0;
    assign ena[3] = (q[3:0]==4'd9 && q[7:4]==4'd9 && q[11:8]==4'd9)?1'b1:1'b0;
    bcd_counter U1(clk,reset,1'b1,q[3:0]);
    bcd_counter U2(clk,reset,ena[1],q[7:4]);
    bcd_counter U3(clk,reset,ena[2],q[11:8]);
    bcd_counter U4(clk,reset,ena[3],q[15:12]);

endmodule

module bcd_counter(
    input clk,
    input reset,
    input in,
    output [3:0] counter);
    
    always@(posedge clk)
        begin
            if(reset)
                counter<=4'd0;
            else
                begin
                    if(counter==4'd9 & in)
                        counter<=4'd0;
                    else
                        counter<=counter+in;
                end
        end
    
endmodule

2.8、12-hour clock

设计一个满足12小时计时的时钟系列计数器(包括am/pm指示)。该计数器由一个fast-running clk驱动,每当时钟需要增加计时时,ena信号则会发生脉冲信号(如:每秒一次)。 reset信号复位时钟到12:00 AM。pm信号为0时指示AM,为1时指示PM。hh、mm、ss都为两个BCD digits(8 bits),分别表示小时(01-12)、分钟(00-59)、秒(00-59)。reset 比 enable信号优先级更高,即使未使能,也要执行复位操作。 下面的时序图展示了时钟从11:59:59 AM 到12:00:00 PM的信号变化,以及同步复位和使能操作结果。

注意11:59:59 PM 在下一个时钟周期应为12:00:00 AM ;12:59:59 PM 在下一时钟周期应为 01:00:00 PM 。没有00:00:00这种形式。

module top_module(
    input clk,
    input reset,
    input ena,
    output pm,
    output [7:0] hh,
    output [7:0] mm,
    output [7:0] ss); 
    
    reg pm_state;
    wire pm_ena;
    reg [3:0] ss_one;
    reg [3:0] ss_ten;
    reg [3:0] mm_one;
    reg [3:0] mm_ten;
    reg [3:0] hh_one;
    reg [3:0] hh_ten;
    
    always@(posedge clk)
        begin
            if(reset)
               ss_one<=4'd0;
            else if(ena)
                begin
                    if(ss_one==4'd9)
                        ss_one<=4'd0;
                    else
                        ss_one<=ss_one+4'd1;
                end
        end
    
    always@(posedge clk)
        begin
            if(reset)
               ss_ten<=4'd0;
            else if(ena  && (ss_one==4'd9))
                begin
                    if(ss_ten==4'd5)
                        ss_ten<=4'd0;
                    else
                        ss_ten<=ss_ten+4'd1;
                end
        end
    
    always@(posedge clk)
        begin
            if(reset)
               mm_one<=4'd0;
            else if(ena  && (ss_one==4'd9) && (ss_ten==4'd5))
                begin
                    if(mm_one==4'd9)
                        mm_one<=4'd0;
                    else
                        mm_one<=mm_one+4'd1;
                end
        end
    
    always@(posedge clk)
        begin
            if(reset)
               mm_ten<=4'd0;
            else if(ena  && (ss_one==4'd9) && (ss_ten==4'd5) && (mm_one==4'd9))
                begin
                    if(mm_ten==4'd5)
                        mm_ten<=4'd0;
                    else
                        mm_ten<=mm_ten+4'd1;
                end
        end
    
    always@(posedge clk)
        begin
            if(reset)
               hh_one<=4'd2;
            else if(ena  && (ss_one==4'd9) && (ss_ten==4'd5) && (mm_one==4'd9) && (mm_ten==4'd5))
                begin
                    if(hh_one==4'd9)
                        hh_one<=4'd0;
                    else if(hh_one==4'd2 && hh_ten==4'd1)//注意12:59:59跳到01:00:00
                        hh_one<=4'd1;
                    else
                        hh_one<=hh_one+4'd1;
                end
        end
    
    always@(posedge clk)
        begin
            if(reset)
               hh_ten<=4'd1;
            else if(ena  && (ss_one==4'd9) && (ss_ten==4'd5) && (mm_one==4'd9) && (mm_ten==4'd5))
                begin
                    if(hh_one==4'd2 && hh_ten==4'd1)//注意12:59:59跳到01:00:00
                        hh_ten<=4'd0;
                    else if(hh_one==4'd9)
                        hh_ten<=hh_ten+4'd1;
                end
        end
    
    always@(posedge clk)
        begin
            if(reset)
                pm_state<=1'b0;
            else if(pm_ena)
                pm_state=~pm_state;
        end
    
    assign pm_ena=(ena  && (ss_one==4'd9) && (ss_ten==4'd5) && (mm_one==4'd9) && (mm_ten==4'd5) && (hh_one==4'd1) && (hh_ten==4'd1));
    assign hh={hh_ten,hh_one};
    assign mm={mm_ten,mm_one};
    assign ss={ss_ten,ss_one};
    assign pm=pm_state;

endmodule

 

 3、shift registers

3.1、4-bit shift register

构建一个 4 位移位寄存器(右移),具有异步复位、同步加载和启用。

  • areset:将移位寄存器重置为零。
  • load :用data[3:0]加载移位寄存器而不是移位。
  • ena:右移(q[3]变为零,q[0]移出并消失)。
  • q:移位寄存器的内容。

如果loadena输入都被置位 (1),则load输入具有更高的优先级。

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<=4'd0;
            else if(load)
               q<=data; 
            else if(ena)
                q<={1'b0,q[3],q[2],q[1]};
            else
                q<=q;
        end

endmodule

 3.2、Left/right rotator

构建一个 100 位左/右旋转器,具有同步加载左/右使能。与丢弃移出位并移入零的移位器不同,旋转器从寄存器的另一端移入移出的位。如果启用,旋转器会旋转位并且不会修改/丢弃它们。

  • load :用data[99:0]加载移位寄存器而不是旋转。
  • ena[1:0]:选择是否旋转以及旋转的方向。
    • 2'b01向右旋转一位
    • 2'b10向左旋转一位
    • 2'b002'b11不旋转。
  • q : 旋转器的内容。
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 if(ena==2'b01)  //向右旋转一位
                q<={q[0],q[99:1]};
            else if(ena==2'b10)//向左旋转一位
                q<={q[98:0],q[99]};
            else
                q<=q;
        end

endmodule

3.3、Left/right arithmetic  shift by 1 or 8

构建一个 64 位算术移位寄存器,同步加载。移位器可以向左和向右移动 1 位或 8 位位置,由数量选择。

算术右移移位寄存器中数字的符号位(在这种情况下为q[63] ),而不是逻辑右移所完成的零。考虑算术右移的另一种方法是,它假设被移动的数字是有符号的并保留符号,因此算术右移将带符号的数字除以 2 的幂。

逻辑左移和算术左移没有区别

  • load :用data[63:0]加载移位寄存器而不是移位。
  • ena : 选择是否换档。
  • amount:选择要移动的方向和量。
    • 2'b00:左移 1 位。
    • 2'b01:左移 8 位。
    • 2'b10:右移 1 位。
    • 2'b11:右移 8 位。
  • q:移位器的内容。
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 if(ena)
                begin
                    if(amount==2'b00)//左移一位
                        q<={q[62:0],1'b0};
                    else if(amount==2'b01)//左移8位
                        q<={q[55:0],8'b0};
                    else if(amount==2'b10)//右移1位
                        q<={q[63],q[63:1]};
                    else
                        q<={{8{q[63]}},q[63:8]};
                end
            else
                q<=q;
        end

endmodule

3.4、5-bit LFSR

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

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

构建这个 LFSR。复位应将 LFSR 复位为 1 

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

endmodule

3.5、3-bit LFSR

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

mt2015 muxdff.png

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

module top_module (
    input [2:0] SW,      // R
    input [1:0] KEY,     // L and clk
    output [2:0] LEDR);  // Q

    always@(posedge KEY[0])
        begin
            if(KEY[1]==1'b1)
                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[1]^LEDR[2];
                end
        end

endmodule

 3.6、32-bit 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
); 
    always@(posedge clk)
        begin
            if(reset)
                q<=32'h1;
            else
                begin
                    q<={1'b0^q[0],q[31:23],q[22]^q[0],q[21:3],q[2]^q[0],q[1]^q[0]};
                end
        end

endmodule

3.7、Shift register

实现以下电路:

考试 m2014q4k.png

 

module top_module (
    input clk,
    input resetn,   // synchronous reset同步复位
    input in,
    output out);
    
    reg [2:0] q;
    always@(posedge clk)
        begin
            if(!resetn)
                {out,q[2:0]}<=4'b0;
            else
                begin
                    {out,q[2:0]}<={q[2:0],in};
                end
        end

endmodule

3.8、shift register

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

考试 2014q4.png

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

  • 将R输入连接到SW开关,
  • clkKEY[0] ,
  • EKEY[1] ,
  • LKEY[2],并且
  • wKEY[3]
  • 将输出连接到红灯LEDR[3:0]
module top_module (
    input [3:0] SW,
    input [3:0] KEY,
    output [3:0] LEDR
); //
    
    MUXDFF U1(.clk(KEY[0]),.W(LEDR[1]),.R(SW[0]),.E(KEY[1]),.L(KEY[2]),.Q(LEDR[0]));
    MUXDFF U2(.clk(KEY[0]),.W(LEDR[2]),.R(SW[1]),.E(KEY[1]),.L(KEY[2]),.Q(LEDR[1]));
    MUXDFF U3(.clk(KEY[0]),.W(LEDR[3]),.R(SW[2]),.E(KEY[1]),.L(KEY[2]),.Q(LEDR[2]));
    MUXDFF U4(.clk(KEY[0]),.W(KEY[3]),.R(SW[3]),.E(KEY[1]),.L(KEY[2]),.Q(LEDR[3]));

endmodule

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

endmodule

3.9、3-input LUT

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

首先,创建一个带有 8 个 D 型触发器的 8 位移位寄存器。标记来自 Q[0]...Q[7] 的触发器输出。移位寄存器输入应称为S,它馈入 Q[0] 的输入(首先移入 MSB)。使输入控制是否移位。然后,将电路扩展为具有 3 个附加输入ABC和一个输出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 ); 

    reg [7:0] q;
    always@(posedge clk)
        begin
            if(enable)//移位
                q<={q[6:0],S};
            else
                q<=q;
        end
    
    assign Z=q[{A,B,C}];
    
endmodule

 

posted @ 2022-04-22 21:38  super_sweet  阅读(118)  评论(0编辑  收藏  举报