一、通用双向移位寄存器:

  • 功能描述:
      4位的双向移位寄存器,含控制输入端(ctrl)、串行输入端(Dsl、Dsr)、4个并行输入端和4个并行输出端,要求实现5种功能:异步置零同步置数左移右移保持原状态不变,功能如下:
ctrl action
00 保持
01 右移
10 左移
11 并行输入
  • 设计方案:
      ①异步置零:复位信号需放入敏感列表;
      ②优先级:reset>ctrl;
      ③ctrl的多方案可以用case来完成
  • 关键代码:
    always@(posedge clk or negedge reset)   begin
        if(~reset)
            Dout <= 4'b0000;                //Asynchronous zero setting
        else begin
            case(ctrl)
                2'b00: Dout <= Dout;                //remain
                2'b01: Dout <= {Dsr, Dout[3:1]};    //shift right
                2'b10: Dout <= {Dout[2:0], Dsl};    //Shift left
                2'b11: Dout <= Din;         //Parallel input
            endcase
        end
    end
  • 仿真验证:
      分别控制实现保持、右移、左移和并行输入。
    initial begin
        clk=0;
        forever #10 clk=~clk;
    end
    
    initial begin
        reset=0; Dsl=0; Dsr=1; ctrl=2'b00; Din=4'b1111;
        #100 reset=1; Dsl=0; Dsr=1; ctrl=2'b01; Din=4'b1111;
        #100 reset=1; Dsl=0; Dsr=0; ctrl=2'b10; Din=4'b1111;
        #100 reset=1; Dsl=0; Dsr=0; ctrl=2'b11; Din=4'b0001;
        #100 $stop;
    end

  输出波形如下图所示,符合题意。

  • 综合结果:

  • 总结反思
      看好模块名称!!!

二、带控制组的触发器组:

  • 功能描述:
      16位D触发器,以byteena来控制寄存器的高低字节是否被写入。byteena[1]控制高字节d[15:8],而byteena[0]控制低字节d[7:0].resetn是低电平有效的同步复位信号.所有DFF由时钟的上升沿触发.

  • 设计方案:
      ①普通的16位同步复位D触发器,resetn不在敏感列表中
      ②优先级:reset>byteena;
      ③byteena的多方案可以用case来完成

  • 关键代码:

    always @(posedge clk)   begin
        if(~resetn)
            q <= 16'b0000000000000000;
        else    begin
            case(byteena)
                2'b00: q <= q;
                2'b01: q <= {q[15:8],d[7:0]};
                2'b10: q <= {d[15:8],q[7:0]};
                2'b11: q <= d;
            endcase
        end
    end
  • 仿真验证:
      依次设计进行:不赋值、低位赋值、高位赋值、全部赋值
    initial begin
        clk=0;
        forever #10 clk=~clk;
    end

    initial        begin
        resetn=1; byteena=2'b00; d=16'b1010101111001101;
        #100 resetn=1; byteena=2'b01; d=16'b1010101111001101;
        #100 resetn=1; byteena=2'b10; d=16'b1010101111001101;
        #100 resetn=1; byteena=2'b11; d=16'b1010101111001101;
        #100 resetn=0; 
        #100 $stop;
    end

  如图,在第一个大周期内,输出悬空,未赋值;第二个时钟周期内,完成了低位赋值,高位悬空;三四周期依次完成了高位赋值和全赋值,符合题意。

  • 综合结果:

三、算数左右移:

  • 功能描述:
      64位算术移位寄存器,具有同步load数据功能,具体移位由ctrl控制,由下表所示:
ctrl action
00 算术左移1位
01 算术左移8位
10 算术右移1位
11 算术右移8位
  • 设计方案:
      ①算术右移时,需要复制最高位,可以通过拼接运算符来完成;算术左移同逻辑左移
      ②优先级:load>ena>ctrl;
      ③ctrl的多方案可以用case来完成
  • 关键代码:
    always@(posedge clk)    begin
        if(load)
            q <= data;
        else if(ena)    begin
            case(ctrl)
                2'b00: q <= {q[62:0], 1'b0};
                2'b01: q <= {q[55:0], 8'b0};
                2'b10: q <= {1'b0, q[63:1]};
                2'b11: q <= {8'b0, q[63:8]}; //unsigned number
            endcase
        end
    end
  • 仿真验证:
      分别设计进行加载数据、算术左移1、算术左移8、算术右移1、算术右移8、不移位。
    initial begin
        clk=0;
        forever #10 clk=~clk;
    end
    
    initial begin
        load=1; ena=1; ctrl=2'b00; data=16'h1111111111111111;
        #20 load=0; ena=1; ctrl=2'b01; data=16'h1111111111111111;
        #20 load=0; ena=1; ctrl=2'b10; data=16'h1111111111111111;
        #20 load=0; ena=1; ctrl=2'b11; data=16'h1111111111111111;
        #20 load=0; ena=0; ctrl=2'b10; data=16'h1111111111111111;
    end

  得到对应波形如下图所示,符合预设。

  • 综合结果:

  • 总结分析:
      拼接运算符的正确应用:q <= {{8{q[63]}}, q[63:8]}

四、8位同步二进制加减法计数器:

  • 功能描述:
      8位同步二进制加减法计数器,输入为时钟端clk(下降沿有效)和异步清除端rstn(低电平有效),加减控制端updown,当updown为1时执行加法计数,为0时执行减法计数;输出为进位端C和8位计数输出端Q

  • 设计方案:
      ①异步置零:复位信号需放入敏感列表;
      ②优先级:rstn>updown;
      ③updown的多方案可以用case来完成.
      ④进位端C只有在从255->0时才为高电平,其余为低电平,可以将当前值是否为255作为一级判断。

  • 关键代码:

    always @(negedge clk)   begin
        if(~rstn)   begin   Q <= 8'b0;C=0;  end
        else if(Q == 8'b11111111)   begin
            if(updown == 1) begin   C=1; Q=8'b00000000; end
            else        begin   C=0; Q=8'b11111110; end
        end
        else    begin
            case(updown)
                1'b1:   begin   Q = Q + 1; C=0;     end
                1'b0:   begin   Q = Q - 1; C=0;     end
            endcase
        end
    end
  • 仿真验证:
      设计含加减法和255->0的testbench
    initial begin
        clk=0;
        forever #10 clk=~clk;
    end

    initial begin 
        rstn=0; updown=1;
        #20 rstn=1; updown=1;
        #20 rstn=1; updown=0;
        #40 rstn=1; updown=1;
        #100 $stop;
    end 

  相应的波形图如下,加减法合题意,且由255->0时,相应的进位输出为1.

  • 综合结果:

五、分频器:

  • 功能描述:
      N分频时钟,50%占空比上升沿采样,异步低电平复位不可采用电平触发同一敏感列表不可同时出现同一个信号的上升沿和下降沿

  • 设计方案:
      ①异步复位:rstn信号出现在敏感列表中;
      ②因为对于奇偶性不同的N值,涉及到分别在上升沿和下降沿进行翻转,故需对N值得奇偶进行讨论:
      N为偶数:

  为实现能够对时钟频率进行分频,则需要对时钟的翻转次数进行计数,这里采用对时钟上升沿进行计数,cnt每加一,则说明经过了一个时钟周期,如按图中需分四倍频,则需经过两个时钟周期,进行一次反转,四倍频即为\({cnt==1}\),则进行一次翻转,推广到偶数的N,则为每逢\({cnt==(N>>1)-1}\),进行一次翻转。

  N为奇数:

  而奇数的特殊点在于涉及到在时钟的上升沿和下降沿分别翻转,则可将其等效为两个偶倍频的并.
  如图中\({odd\_clk\_out\_1}\),\({odd\_clk\_out\_2}\),其中前者在时钟上升沿进行翻转;后者在时钟下降沿进行翻转。
  同时依旧在上升沿进行计数(图上cnt有一定误差,画不到中间的部分????),对于图中五倍频来说,前者在\({cnt==0|cnt==2}\)进行翻转,后者在\({cnt==1|cnt==3}\)进行翻转,
  推广到奇数的N,则有前者在\({cnt==0|cnt==N>>1}\)进行翻转,后者在\({cnt==1|cnt==N>>1+1}\)进行翻转。

  • 关键代码:
    //even
    always@(posedge clk, negedge rstn)  begin
        if(!rstn)
            even_cnt <= 1'b0;
        else if(even_cnt == (N>>1'b1)-1'b1)
            even_cnt <= 1'b0;
        else
            even_cnt <= even_cnt + 1'b1;
    end

    always@(posedge clk, negedge rstn)  begin
        if(!rstn)
            even_clk_out <= 1'b0;
        else if(even_cnt == (N>>1'b1)-1'b1)
            even_clk_out <= ~even_clk_out;
        else
            even_clk_out <= even_clk_out;
    end
    
    //odd
    always@(posedge clk, negedge rstn)  begin
        if(!rstn)
            odd_cnt <= 1'b0;
        else if(odd_cnt == N-1'b1)
            odd_cnt <= 1'b0;
        else
            odd_cnt <= odd_cnt + 1'b1;
    end

    always@(posedge clk, negedge rstn)  begin
        if(!rstn)
            odd_clk_out1 <= 1'b0;
        else if(odd_cnt == N>>1'b1 | odd_cnt == 1'b0)
            odd_clk_out1 <= ~odd_clk_out1;
        else
            odd_clk_out1 <= odd_clk_out1;
    end

    always@(negedge clk, negedge rstn)  begin
        if(!rstn)
            odd_clk_out2 <= 1'b0;
        else if(odd_cnt == (N>>1'b1)+1'b1 | odd_cnt == 1'b1)
            odd_clk_out2 <= ~odd_clk_out2;
        else
            odd_clk_out2 <= odd_clk_out2;
    end
    
    assign odd_clk_out = odd_clk_out1 | odd_clk_out2;
    assign clk_out = (N%2==0) ? even_clk_out : odd_clk_out;
  • 仿真验证:
      由代码,我们只需要控制生成时钟信号clk和复位信号rstn即可。
    initial begin
        clk = 0;
        forever #10 clk = ~clk;
    end

    initial begin
        rstn=0;
        #20 rstn=1;
    end

  相应地,五倍频的仿真波形结果如下,符合要求。

  • 综合结果:

  • 总结分析:
      ①:总结规律->设计实现;
      ②:优先级:算术运算符>移位运算符,想要先进行移位时,应加括号。

六、异步四位二进制加计数器:

  • 功能描述:

  每个D触发器的Q非端连接到D端,实现翻转功能。计数时钟clk加至触发器FF0的时钟脉冲输入端,每输入一个计数脉冲,FF0翻转一次。

  FF1-FF3都以前一级触发器的Q端作为触发信号,当Q0由1变0时,FF1翻转,其余类推。

  • 设计方案:
      ①设计基本的D触发器作为FF,例化4次得到功能实现;
      ②据图连接例化端口。

  • 关键代码:

module FF(
    input clk,
    input rstn,
    input d,
    output reg q);
    
    always @(negedge clk)   begin
        if(!rstn)   q <= 0;
        else    q <= d;
    end
endmodule

module asyn_counter(
    input clk,
    input rstn,
    output q0,
    output q1,
    output q2,
    output q3);

    FF FF0(
        .clk(clk),
        .rstn(rstn),
        .d(~q0),
        .q(q0));
    FF FF1(
        .clk(q0),
        .rstn(rstn),
        .d(~q1),
        .q(q1));
    FF FF2(
        .clk(q1),
        .rstn(rstn),
        .d(~q2),
        .q(q2));
    FF FF3(
        .clk(q2),
        .rstn(rstn),
        .d(~q3),
        .q(q3));

endmodule
  • 仿真验证:
      只需控制生成时钟信号和复位信号
    initial begin
        clk = 0;
        forever #10 clk=~clk;
    end

    initial begin 
        rstn=0; 
        #20 rstn=1;
        #100 $stop;
    end

  对应波形图如下,正确完成从零开始的下降沿计数。

  • 综合结果: