首页 > 其他分享 >牛客网手撕代码(31-58)

牛客网手撕代码(31-58)

时间:2023-03-17 15:12:30浏览次数:35  
标签:clk 31 牛客 rst input 网手 data reg 时钟

31. 数据累加输出

题目

实现串行输入数据累加输出,输入端输入 8 bit 数据,每当模块接收到4个输入数据后,输出端输出4个接收到数据的累加结果。输入端和输出端与上下游的交互采用valid-read 双向握手机制。要求上下游均能满速传输时,数据传输无气泡,不能由于本模块的设计原因产生额外的性能损失。

电路的接口如下图所示。valid_a 用来指示数据输入data_in 的有效性,valid_b 用来指示数据输出 data_out 的有效性;ready_a 用来指示本模块是否准备好接收上游数据,ready_b 表示下游是否准备好接收本模块的输出数据;clk 是时钟信号;rst_n 是异步复位信号。

img

输入描述:

input clk ,

input rst_n ,

input [7:0] data_in ,

input valid_a ,

input ready_b

输出描述:

output ready_a ,

output reg valid_b ,

output reg [9:0] data_out

解法

 input    clk  , 
 input    rst_n  ,
 input  [7:0] data_in ,
 input    valid_a ,
 input    ready_b ,

 output    		  ready_a ,
 output reg  valid_b ,
 output reg [9:0] data_out
reg [1:0] count;
assign ready_a = ~valid_b | ready_b;


always @ (posedge clk or negedge rst_n)  begin
     if( ~rst_n ) begin
         count <= 2'b0;
     end
     else begin
         if(valid_a & ready_a) begin
             if( count == 2'd3)
                 count <= 2'd0;
             else
                 count <= count + 2'd1;
             end
         end
    end


    always @ (posedge clk or negedge rst_n)  begin
         if( ~rst_n ) begin
                 data_out <= 10'b0;
             end
         else begin
             if(valid_a && ready_a) begin
                 if(count == 2'd0) begin
                     data_out <= data_in;
             end
                 else begin
                     data_out <= data_out + data_in;
                 end
         end
     end
end


always @ (posedge clk or negedge rst_n)  begin
     if( ~rst_n ) begin
         valid_b <= 1'b0;
     end
     else begin
         if(count == 2'd3 && valid_a && ready_a) begin
             valid_b <= 1'b1;
         end
         else if(valid_b && ready_b)begin
             valid_b <= 1'b0;
         end
         else begin
             valid_b <= valid_b;
         end
     end
end

32. 非整数倍数据位宽转换 24 to 128

题目

实现数据位宽转换电路,实现 24 bit 数据输入转换为 128 bit 数据输出。其中,先到的数据应置于输出的高bit位。

电路的接口如下图所示。valid_in用来指示数据输入data_in的有效性,valid_out用来指示数据输出data_out的有效性;clk是时钟信号;rst_n是异步复位信号。

img

输入描述:

input            clk, 
input            rst_n,
input            valid_in,
input  [23:0]    data_in,    

输出描述:

output  reg         valid_out  ,
output reg [127:0]  data_out

解法

入数据是 24 bit ,输出数据是 128 bit 。因为 128×3=24×16,所以每输入 16 个有效数据,就可以产生三个完整的输出。因此设置一个仅在输入数据有效时工作的计数器 cnt,计数范围是 0-15。

设置一个数据暂存器 data_lock,每当输入有效时,将数据从低位移入。

每当计数器 cnt 计数到5、10、15时,data_out 要进行更新,并拉高 valid_out 一个周期。

    reg [3:0]   cnt;
    reg [127:0] data_lock;
     
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            cnt <= 0;
        else
            cnt <= ~valid_in? cnt:cnt+1;
    end
     
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            valid_out <= 0;
        else
            valid_out <= (cnt==5 || cnt==10 || cnt==15)&&valid_in;
    end
     
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            data_lock <= 0;
        else
            data_lock <= valid_in? {data_lock[103:0], data_in}: data_lock;
    end
     
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            data_out <= 0;
        else if(cnt==5)
            data_out <= valid_in? {data_lock[119:0], data_in[23:16]}: data_out;
        else if(cnt==10)
            data_out <= valid_in? {data_lock[111:0], data_in[23: 8]}: data_out;
        else if(cnt==15)
            data_out <= valid_in? {data_lock[103:0], data_in[23: 0]}: data_out;
        else
            data_out <= data_out;
    end

33. 非整数倍数据位宽转换8to12

题目

实现数据位宽转换电路,实现 8 bit 数据输入转换为 12 bit 数据输出。其中,先到的数据应置于输出的高 bi t位。

电路的接口如下图所示。valid_in 用来指示数据输入 data_in 的有效性, valid_out 用来指示数据输出 data_out 的有效性;clk 是时钟信号;rst_n 是异步复位信号。

img

解法

input       clk  , 
input       rst_n ,
input       valid_in ,
input [7:0] data_in ,

output reg        valid_out,
output reg [11:0] data_out

非整数倍的数据位宽转换 题型,第一,找最大倍数关系,比如 24->128,则中间缓存大小为 24*5=120 ,本题 8->12,中间缓存大小为 8。采用缓存 buff 保存data_in。第二,确定计数周期,在每次 valid 有效的时钟下, cnt 自增1,当cnt=1,2时,data_out都会输出一次,那么计数周期就为0-2。第三,在每次输出时,使用拼接运算符拼接buffdata_in来输出data_out,并将未输出的data_in保存在缓存buff(在 line 34 没有保存是因为此时data_in全部输出)。

	reg[7:0] buff;
	reg[1:0] cnt;
    always@(negedge rst_n or posedge clk)begin
        if(~rst_n)
            cnt <= 0;
        else if(valid_in)
            if(cnt == 2)
                cnt <= 0;
            else
                cnt <= cnt + 1;
    end
    always@(negedge rst_n or posedge clk)begin
        if(~rst_n)begin
            buff <= 0;
            data_out <= 0;
        end
        else if(valid_in)begin
            if(cnt == 1)begin
                data_out <= {buff, data_in[7:4]};
                buff[3:0] <= data_in[3:0];
            end
            else if(cnt == 2)begin
                data_out <= {buff[3:0], data_in};
            end
            else
                buff <= data_in;
            end
    end
    always@(negedge rst_n or posedge clk)begin
        if(~rst_n)
            valid_out <= 0;
        else if(valid_in && (cnt == 1 || cnt == 2))
            valid_out <= 1;
        else
            valid_out <= 0;
    end    

34. 整数倍数据位宽转换 8 to 16

题目

实现数据位宽转换电路,实现 8 bit 数据输入转换为 16 bit 数据输出。其中,先到的 8 bit 数据应置于输出 16 bit 的高 8 位。

电路的接口如下图所示。valid_in 用来指示数据输入 data_in 的有效性,valid_out 用来指示数据输出 data_out 的有效性;clk是时钟信号;rst_n 是异步复位信号。

img img

解法

 input          clk  , 
 input          rst_n  ,
 input          valid_in ,
 input  [7:0]   data_in ,

 output reg        valid_out,
 output reg [15:0] data_out
reg cnt;
always @ (posedge clk or negedge rst_n)  begin
     if(~rst_n) begin
         cnt <= 1'b0;
     end
     else begin
         if(valid_in) begin
         cnt <= cnt + 1'b1; // cnt = ~cnt;
         end
     end
end
reg [7:0] data_reg;


always @ (posedge clk or negedge rst_n)  begin
 if(~rst_n) begin
     data_out <= 16'b0;
     valid_out <= 1'b0;
 end
 else begin
     if(valid_in) begin
         if(cnt == 1'b0) begin
             data_reg <= data_in;
             valid_out <= 1'b0;
         end
         else begin
             data_out <= {data_reg, data_in};
             valid_out <= 1'b1;
         end
     end
     else begin
         valid_out <= 1'b0;
     end
 end
end

35.状态机-非重叠的序列检测

题目

设计一个状态机,用来检测序列 10111,要求:

1、进行非重叠检测 即 101110111 只会被检测通过一次

2、寄存器输出且同步输出结果

注意 rst 为低电平复位

img img

解法

 input wire clk ,
 input wire rst ,
 input wire data ,

 output reg flag
reg [1:0] cnt;
always @ (posedge clk or negedge rst_n) begin
     if(~rst_n) begin
         cnt <= 2'b0;
     end
     else begin
         if(valid_in) begin
             if(cnt == 2'd2) 
                 cnt <= 2'd0;
             else
                 cnt <= cnt + 2'd1;
         end
     end
 end
reg [7:0] data_reg;


always @ (posedge clk or negedge rst_n) begin
     if(~rst_n) begin
         data_out <= 12'b0;
         valid_out <= 1'b0;
         data_reg <= 8'b0;
     end
     else begin
         if(valid_in) begin
             case(cnt) 
                 2'd0 : begin
                 data_reg <= data_in;
                 valid_out <= 1'b0;
                 end
                 2'd1 : begin
                 data_out <= {data_reg[7:0], data_in[7:4]};
                 data_reg[3:0] <= data_in[3:0];
                 valid_out <= 1'b1;
                 end
                 2'd2 : begin
                 data_out <= {data_reg[3:0], data_in[7:0]};
                 valid_out <= 1'b1;
                 end
                 default : begin
                 data_reg <= 8'b0;
                 data_out <= 12'b0;
                 valid_out <= 1'b0;
             end
         endcase 
     end
         else begin
             valid_out <= 1'b0;
         end
     end
end

36. 状态机-重叠序列检测

题目

设计一个状态机,用来检测序列 1011,要求:

1、进行重叠检测 即 10110111 会被检测通过 2 次

2、寄存器输出,在序列检测完成下一拍输出检测有效

注意 rst 为低电平复位

img img

解法

 input wire clk ,
 input wire rst ,
 input wire data ,

 output reg flag
    parameter S0=0, S1=1, S2=2, S3=3, S4=4;
    reg [2:0] state, nstate;
     
    always@(posedge clk or negedge rst) begin
        if(~rst)
            state <= S0;
        else
            state <= nstate;
    end
     
    always@(*) begin
        if(~rst)
            nstate <= S0;
        else
            case(state)
                S0     : nstate <= data? S1: S0;
                S1     : nstate <= data? S1: S2;
                S2     : nstate <= data? S3: S0;
                S3     : nstate <= data? S4: S2;
                S4     : nstate <= data? S1: S2;
                default: nstate <= S0;
            endcase
    end
     
    always@(posedge clk or negedge rst) begin
        if(~rst)
            flag <= 0;
        else
            flag <= state==S4;
    end

37.时钟分频(偶数)

题目

请使用 D 触发器设计一个同时输出 2/4/8 分频的 50% 占空比的时钟分频器

注意 rst 为低电平复位

img img

解法

input wire rst ,
input wire clk_in,

output wire clk_out2,
output wire clk_out4,
output wire clk_out8
	reg clk_out2_r, clk_out4_r, clk_out8_r;
     
    always@(posedge clk_in or negedge rst) begin
        if(~rst)
            clk_out2_r <= 0;
        else
            clk_out2_r <= ~clk_out2_r;
    end
     
    always@(posedge clk_out2 or negedge rst) begin
        if(~rst)
            clk_out4_r <= 0;
        else
            clk_out4_r <= ~clk_out4_r;
    end
     
    always@(posedge clk_out4 or negedge rst) begin
        if(~rst)
            clk_out8_r <= 0;
        else
            clk_out8_r <= ~clk_out8_r;
    end
     
    assign clk_out2 = clk_out2_r;
    assign clk_out4 = clk_out4_r;
    assign clk_out8 = clk_out8_r;

38. 自动贩售机1

题目

设计一个自动贩售机,输入货币有三种,为 0.5/1/2 元,饮料价格是 1.5 元,要求进行找零,找零只会支付 0.5 元。

ps:

投入的货币会自动经过边沿检测并输出一个在时钟上升沿到 1,在下降沿到 0 的脉冲信号

注意 rst 为低电平复位

img img

解法

信号 含义
d1 0.5元
d2 1元
d3 2元
ou1 饮料
out2 零钱

输入描述:

输入信号 clk rst d1 d2 d3

类型 wire

输出描述:

输出信号 out1 , [1:0] out2

类型 reg

parameter S0 = 'd0, S1 = 'd1, S2 = 'd2, S3 = 'd3 , S4 = 'd4, S5 = 'd5 , S6 = 'd6;
    reg  [2:0]    current_state;
    reg  [2:0]    next_state;
    wire [2:0]   input_state;//将输入组合起来
    assign input_state = {d1,d2,d3};
     
    always@(posedge clk or negedge rst)begin
        if(rst == 1'b0)begin
            current_state <= S0;
        end
        else begin
            current_state <= next_state;
        end
    end   
     
    always@(*)begin
        case(current_state)
            S0:begin
                case(input_state)
                    3'b100:next_state = S1 ;
                    3'b010:next_state = S2 ;
                    3'b001:next_state = S4 ;
                    default:next_state = next_state;
                endcase
            end
            S1:begin
                case(input_state)
                    3'b100:next_state = S2 ;
                    3'b010:next_state = S3 ;
                    3'b001:next_state = S5 ;
                    default:next_state = next_state; 
                endcase
            end
            S2:begin
                case(input_state)
                    3'b100:next_state = S3 ;
                    3'b010:next_state = S4 ;
                    3'b001:next_state = S6 ;
                    default:next_state = next_state;
                endcase            
            end
             
            default:begin
                next_state = S0;
            end
        endcase
    end
     
    always@(posedge clk or negedge rst)begin
        if(rst == 1'b0)begin
            out1 <= 1'b0;
            out2 <= 2'b0;
        end
        else begin
            case(next_state)
                    S3:        begin out1 <= 1'b1;out2 <= 2'b0; end 
                    S4:        begin out1 <= 1'b1;out2 <= 2'b1; end 
                    S5:        begin out1 <= 1'b1;out2 <= 2'b10; end 
                    S6:        begin out1 <= 1'b1;out2 <= 2'b11; end 
                    default:   begin out1 <= 1'b0;out2 <= 2'b0; end 
            endcase
        end
    end

39.自动贩售机2

题目

设计一个自动贩售机,输入货币有两种,为 0.5/1 元,饮料价格是 1.5/2.5 元,要求进行找零,找零只会支付 0.5 元。

ps:

1、投入的货币会自动经过边沿检测并输出一个在时钟上升沿到1,在下降沿到0的脉冲信号

2、此题忽略出饮料后才能切换饮料的问题

注意 rst 为低电平复位

信号示意图:

img img

解法

信号 含义
d1 0.5元
d2 1元
sel 选择饮料
ou1 饮料1
out2 饮料2
out3 零钱
 input wire clk ,
 input wire rst ,
 input wire d1 ,
 input wire d2 ,
 input wire sel ,

 output reg out1,
 output reg out2,
 output reg out3
    parameter S0=0, S0_5=1, S1=2, S1_5=3, S2=4, S2_5=5, S3=6;
    reg[2:0] state, nstate;
     
    always@(posedge clk or negedge rst) begin
        if(~rst)
            state <= 0;
        else
            state <= nstate;
    end
     
    always@(*) begin
        case(state)
            S0     : nstate = d1? S0_5:
                              d2? S1:
                              nstate;
            S0_5   : nstate = d1? S1:
                              d2? S1_5:
                              nstate;
            S1     : nstate = d1? S1_5:
                              d2? S2:
                              nstate;
            S1_5   : nstate = ~sel? S0:
                              d1? S2:
                              d2? S2_5:
                              nstate;
            S2     : nstate = ~sel? S0:
                              d1? S2_5:
                              d2? S3:
                              nstate;
            default: nstate = S0;
        endcase
    end
     
    always@(*) begin
        if(~rst) begin
            {out1, out2, out3} = 3'b000;
        end
        else begin
            case(state)
                S0, S0_5, S1: {out1, out2, out3} = 0;
                S1_5        : {out1, out2, out3} = ~sel? 3'b100: 3'b000;
                S2          : {out1, out2, out3} = ~sel? 3'b101: 3'b000;
                S2_5        : {out1, out2, out3} = ~sel? 3'b101: 3'b010;
                S3          : {out1, out2, out3} = ~sel? 3'b101: 3'b011;
                default     : {out1, out2, out3} = 3'b000;
            endcase
        end
    end

40. 占空比50%的奇数分频

题目

设计一个同时输出 7 分频的时钟分频器,占空比要求为 50%

注意 rst 为低电平复位

img img

输入描述:

输入信号 clk_in rst

类型 wire

输出描述:

输出信号 clk_out7

类型 wire

解法

 input wire rst ,
 input wire clk_in,

 output wire clk_out7

对于奇数分频电路,主要难点在于 50% 占空比的实现。单触发沿在奇数分频中是没有办法实现 50% 占空比的,因此需要考虑使用双边沿加组合逻辑实现50%占空比

通过简单的状态转移表就能够得出,clkout7 的翻转第一次是在上升沿,第二次是在下降沿。

clkin clkout7 cnt
0 0 0
1 1 1
0 1 1
1 1 2
0 1 2
1 1 3
0 1 3
1 1 4
0 0 4
1 0 5
0 0 5
1 0 6
0 0 6
1 0 7
0 0 7
1 1 8

要让两个不同的时钟边沿触发的分频信号进行组合逻辑,分析应该有下面的时许构成:

很容易看出,做法应该是将两个信号进行或操作

img

img

   reg [3:0]            cnt ;
   always @(posedge clk_in or negedge rst) begin
      if (!rst) begin
         cnt    <= 'b0 ;
      end
      else if (cnt == N-1) begin
         cnt    <= 'b0 ;
      end
      else begin
         cnt    <= cnt + 1'b1 ;
      end
   end
 
   reg                  clkp ;
   always @(posedge clk_in or negedge rst) begin
      if (!rst) begin
         clkp <= 1'b0 ;
      end
      else if (cnt == (N>>1)) begin 
        clkp <= 1 ;
      end
      else if (cnt == N-1) begin 
        clkp <= 0 ;
      end
   end
   
 
   reg                  clkn ;
   always @(negedge clk_in or negedge rst) begin
      if (!rst) begin
         clkn <= 1'b0 ;
      end
      else if (cnt == (N>>1) ) begin 
        clkn <= 1 ;
      end
      else if (cnt == N-1) begin 
        clkn <= 0 ;
      end
   end
 
 
   assign clk_out7 = clkp | clkn ;

41.任意小数分频

题目

请设计一个可以实现任意小数分频的时钟分频器,比如说8.7分频的时钟信号

注意rst为低电平复位

提示:

其实本质上是一个简单的数学问题,即如何使用最小公倍数得到时钟周期的分别频比。

设小数为 n,此处以 8.7 倍分频的时钟周期为例。

首先,由于不能在硬件上进行小数的运算(比如 2.1 个时钟这种是不现实的,也不存在 3.3个寄存器),小数分频不能做到分频后每个时钟周期都是源时钟的 nn 倍,也无法实现占空比为 1/2,因此,考虑小数分频,其实现方式应当为 53 个 clkout 时钟周期是10个 clkin 时钟周期的8.7倍。

img img

解法

input wire clk_in,
input wire rst,

output wire clk_out

parameter M_N = 8'd87; 
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期

假设输出 clk_out 是输入clk_in N分频。首先要将分频系数N化为分数形式,比如 4.75→19/44 , 3.4→34/10。本题中,8.7 可以化为 87/10。这意味着在 87 个clk_in周期内输出 10 个clk_out周期就可以实现分频。

img

然后采用若干种(一般是两种)整数分频在87个原周期clk_in内产生10个新时钟周期clk_out。整数分频的分频系数有很多种选择,但要尽可能接近,提高clk_out的均匀度。一般推荐在小数分频系数N的附近选取。因为8<N<9,所以两个整数分频系数是8和9。接着要计算87个clk_out周期分别有多少个是8分频和9分频的。设每10个clk_out中有x个8分频输出和y个9分频输出,则可列出如下方程:

\[{x+y=10, 8x+9y=87} \]

可得 x=3,y=7。也就是 3 个 8 分频 和 7 个 9 分频一组,循环输出,就等效于 8.7 分频。 最后安排组内 8 分频和 9 分频的位置。这里的方法也不固定,不过本题要求 3 个8分频先输出,再输出 7 个 9 分频,如下图。

img

题目提供的

  • M_N=87。一组clk_out输出需要的clk_in时钟数量。
  • c89=24。切换分频系数的时间点。从这里可以看出,本题要求先输出3个8分频。
  • div_e=8。分频系数1。
  • div_o=9。分频系数2。

自定义的

  • cyc_cnt。对clk_in进行计数,达到M_N后清零。
  • div_flag。8/9分频标志。当div_flag==0时是8分频;当div_flag==1时是9分频。cyc_cnt==M_N-1或者cyc_cnt==c89-1时该标志位翻转。
  • clk_cnt。用于产生分频输出。当div_flag==0时,计数最大值是div_e-1;当div_flag==1时,计数最大值是div_o-1
  • clk_out_r。根据clk_cntdiv_flag产生分频输出。
    reg [3:0] clk_cnt;
    reg [6:0] cyc_cnt;
    reg div_flag;
    reg clk_out_r;
    assign clk_out = clk_out_r;


    always@(posedge clk_in or negedge rst) begin
        if(~rst)
            clk_cnt <= 0;
        else if(~div_flag)
            clk_cnt <= clk_cnt==(div_e-1)? 0: clk_cnt+1;
        else
            clk_cnt <= clk_cnt==(div_o-1)? 0: clk_cnt+1;
    end
     
    always@(posedge clk_in or negedge rst) begin
        if(~rst)
            cyc_cnt <= 0;
        else
            cyc_cnt <= cyc_cnt==(M_N-1)? 0: cyc_cnt+1;
    end
     
    always@(posedge clk_in or negedge rst) begin
        if(~rst)
            div_flag <= 0;
        else
            div_flag <= cyc_cnt==(M_N-1)||cyc_cnt==(c89-1)? ~div_flag: div_flag;
    end
     
    always@(posedge clk_in or negedge rst) begin
        if(~rst)
            clk_out_r <= 0;
        else if(~div_flag)
            clk_out_r <= clk_cnt<=((div_e>>2)+1);
        else
            clk_out_r <= clk_cnt<=((div_o>>2)+1);
    end
     

42.无占空比要去的奇数分频

题目

请设计一个同时输出 5 分频的时钟分频器,本题对占空比没有要求

注意 rst 为低电平复位

img img

解法

 input wire rst ,
 input wire clk_in,

 output wire clk_out5
    parameter q=6;
    reg[3:0] cnt;
    reg out;
    assign clk_out5 = out;
    always@(clk_in or rst)begin
        if(~rst) cnt <= 0;
        else cnt <= cnt==9? 0: cnt+1;
    end
     
    always@(*)begin
        if(~rst) out <= 0;
        else if(cnt==2 || cnt==q) out = ~out;
    end

43.根据状态转移写状态机-三段式

题目

如图所示为两种状态机中的一种,请根据状态转移图写出代码,状态转移线上的0/0 等表示的意思是过程中 data/flag 的值。

要求:

1、 必须使用对应类型的状态机

2、 使用三段式描述方法,输出判断要求要用到对现态的判断

img img

解法

 input wire clk ,
 input wire rst ,
 input wire data ,

 output reg flag

三段式包含三个进程:

第一个进程(同步时序always),描述次态到现态的转移

第二个进程(组合逻辑always),描述状态转移条件的判断

第三个进程(同步时序always),描述状态的寄存器输出

摩尔机和米勒机的区别:主要区别在于状态机的输出与当前的状态是否有关,下面用两段式来描述两者的区别。

需要注意的是,如果使用三段式描述法,两者的区别主要聚焦于第三段的判断是基于现态还是次态。

摩尔状态机要比米勒状态机少一个状态,摩尔状态机慢一个周期;米勒状态机使用当前输入和当前状态共同判断,摩尔状态机不需要当前输入。

    parameter S0 = 'd0, S1 = 'd1, S2 = 'd2, S3 = 'd3 ;
    reg [2:0]  current_state;
    reg [2:0]  next_state;
     
    always@(posedge clk or negedge rst)begin
        if(rst == 1'b0)begin
            current_state <= S0;
        end
        else begin
            current_state <= next_state;
        end
    end   
     
    always@(*)begin
        case(current_state)
            S0:begin
                next_state = data ? S1 : S0;
            end
            S1:begin
                next_state = data ? S2 : S1;
            end
            S2:begin
                next_state = data ? S3 : S2;
            end
            S3:begin
                next_state = data ? S0 : S3;
            end
            default:begin  next_state = S0; end
        endcase
    end
     
    always@(posedge clk or negedge rst)begin
        if(rst == 1'b0)begin
            flag <= 1'b0;
        end
        else begin
            if(current_state == S3)begin
                if (data) flag <= 1'b1;
                else flag <= 1'b0;
            end
            else begin
                flag <= 1'b0;
            end
        end
    end

44.根据状态转移写状态机-二段式

题目

img

如图所示为两种状态机中的一种,请根据状态转移图写出代码,状态转移线上的0/0等表示的意思是过程中data/flag的值。

要求:

1、 必须使用对应类型的状态机

2、 使用二段式描述方法

img img

解法

input wire clk ,
input wire rst ,
input wire data ,

output reg flag
    parameter S0 = 'd0, S1 = 'd1, S2 = 'd2, S3 = 'd3 ,S4 = 'd4 ;
    reg [2:0]  current_state;
    reg [2:0]  next_state;
     
    always@(posedge clk or negedge rst)begin
        if(rst == 1'b0)begin
            current_state <= S0;
        end
        else begin
            current_state <= next_state;
        end
    end   
     
    always@(*)begin
        case(current_state)
            S0:begin
                next_state = data ? S1 : S0;
                flag = 1'b0; 
            end
            S1:begin
                next_state = data ? S2 : S1;
                flag = 1'b0;
            end
            S2:begin
                next_state = data ? S3 : S2;
                flag = 1'b0;
            end
            S3:begin
                next_state = data ? S4 : S3;
                flag = 1'b0;
            end
            S4:begin
                next_state = data ? S1 : S0;
                flag = 1'b1;
            end
            default:begin  
                next_state = S0;
                flag = 1'b0;             
            end
        endcase
    end

45.异步FIFO

题目

请根据题目中给出的双口 RAM 代码和接口描述,实现异步 FIFO,要求 FIFO 位宽和深度参数化可配置。

img

双口 RAM 端口说明:

端口名 I/O 描述
wclk input 写数据时钟
wenc input 写使能
waddr input 写地址
wdata input 输入数据
rclk input 读数据时钟
renc input 读使能
raddr input 读地址
rdata output 输出数据

同步 FIFO 端口说明:

端口名 I/O 描述
wclk input 写时钟
rclk input 读时钟
wrstn input 写时钟域异步复位
rrstn input 读时钟域异步复位
winc input 写使能
rinc input 读使能
wdata input 写数据
wfull output 写满信号
rempty output 读空信号
rdata output 读数据

双口 RAM 代码如下,可在本题答案中添加并例化此代码。

module dual_port_RAM #(parameter DEPTH = 16,
   parameter WIDTH = 8)(
 input wclk
,input wenc
,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
,input [WIDTH-1:0] wdata      //数据写入
,input rclk
,input renc
,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
,output reg [WIDTH-1:0] rdata //数据输出
);


reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];


always @(posedge wclk) begin
if(wenc)
RAM_MEM[waddr] <= wdata;
end 


always @(posedge rclk) begin
if(renc)
rdata <= RAM_MEM[raddr];
end 


endmodule  

输入描述:

input           wclk  , 
input           rclk  , 
input           wrstn  ,
input          rrstn  ,
input           winc  ,
input           rinc  ,
input     [WIDTH-1:0]  wdata  

输出描述:

output wire        wfull  ,
output wire        rempty  ,
output wire [WIDTH-1:0]  rdata

解法

parameter ADDR_WIDTH = $clog2(DEPTH);
 
 
/**********************addr bin gen*************************/
 
reg [ADDR_WIDTH:0]  waddr_bin;
 
reg [ADDR_WIDTH:0]  raddr_bin;
 
 
always @(posedge wclk or negedge wrstn) begin
 
       if(~wrstn) begin
 
              waddr_bin <= 'd0;
 
       end
 
       else if(!wfull && winc)begin
 
              waddr_bin <= waddr_bin + 1'd1;
 
       end
 
end
 
always @(posedge rclk or negedge rrstn) begin
 
       if(~rrstn) begin
 
              raddr_bin <= 'd0;
 
       end
 
       else if(!rempty && rinc)begin
 
              raddr_bin <= raddr_bin + 1'd1;
 
       end
 
end
 
 
/**********************addr gray gen*************************/
 
wire       [ADDR_WIDTH:0]  waddr_gray;
 
wire       [ADDR_WIDTH:0]  raddr_gray;
 
reg [ADDR_WIDTH:0]  wptr;
 
reg [ADDR_WIDTH:0]  rptr;
 
assign waddr_gray = waddr_bin ^ (waddr_bin>>1);
 
assign raddr_gray = raddr_bin ^ (raddr_bin>>1);
 
always @(posedge wclk or negedge wrstn) begin
 
       if(~wrstn) begin
 
              wptr <= 'd0;
 
       end
 
       else begin
 
              wptr <= waddr_gray;
 
       end
 
end
 
always @(posedge rclk or negedge rrstn) begin
 
       if(~rrstn) begin
 
              rptr <= 'd0;
 
       end
 
       else begin
 
              rptr <= raddr_gray;
 
       end
 
end
 
/**********************syn addr gray*************************/
 
reg         [ADDR_WIDTH:0]  wptr_buff;
 
reg         [ADDR_WIDTH:0]  wptr_syn;
 
reg         [ADDR_WIDTH:0]  rptr_buff;
 
reg         [ADDR_WIDTH:0]  rptr_syn;
 
always @(posedge wclk or negedge wrstn) begin
 
       if(~wrstn) begin
 
              rptr_buff <= 'd0;
 
              rptr_syn <= 'd0;
 
       end
 
       else begin
 
              rptr_buff <= rptr;
 
              rptr_syn <= rptr_buff;
 
       end
 
end
 
always @(posedge rclk or negedge rrstn) begin
 
       if(~rrstn) begin
 
              wptr_buff <= 'd0;
 
              wptr_syn <= 'd0;
 
       end
 
       else begin
 
              wptr_buff <= wptr;
 
              wptr_syn <= wptr_buff;
 
       end
 
end
 
/**********************full empty gen*************************/
 
assign wfull = (wptr == {~rptr_syn[ADDR_WIDTH:ADDR_WIDTH-1],rptr_syn[ADDR_WIDTH-2:0]});
 
assign rempty = (rptr == wptr_syn);

46.同步FIFO

题目

根据题目提供的双口 RAM 代码和接口描述,实现同步 FIFO,要求 FIFO 位宽和深度参数化可配置。

img

输入描述:

input           clk    , 
input           rst_n  ,
input           winc  ,
input           rinc  ,
input     [WIDTH-1:0]  wdata  

输出描述:

output reg        wfull  ,
output reg        rempty  ,
output wire [WIDTH-1:0]  rdata

解法

关于读写地址以及 fifo_cnt 的产生,在 FIFO 里经典的做法读写地址均做循环累加:地址指针 waddrraddr 均比实际地址多一位,最高位用来指示套圈情况。当 waddrraddr 的最高位相同时,fifo_cnt = waddr-raddr;当 waddrraddr 的最高位相反时,fifo_cnt = DEPTH + waddr[ADDR_WIDTH-1:0] - raddr[ADDR_WIDTH-1:0] 。这种用最高位表示套圈的思路是没问题的,但是参考答案里的做法是有问题的,他的做法是直接定义 waddr[ADDR_WIDTH:0] 然后从 0开始加,加到高位自动翻转:

always @(posedge clk or negedge rst_n) begin 
    if(~rst_n) begin 
        waddr <= 'd0; 
    end 
    else if(!wfull && winc)begin 
        waddr <= waddr + 1'd1; 
    end
end


这样的做法只适用于深度为 2^N 的 fifo,一旦深度非 2^N 那么 addr 就乱了。如果还要使用最高位标志套圈的思路,那么需要做出修改如下:

reg [DP_WD :0]waddr;

wire    wenc;
wire    waddr_d_h;
wire [DP_WD -1:0]waddr_d_l;

assign wenc = winc & (!wfull);

assign waddr_d_h = (waddr[DP_WD-1:0] == DEPTH-1) ? ~waddr[DP_WD] : waddr[DP_WD];

assign waddr_d_l = (waddr[DP_WD-1:0] == DEPTH-1) ? 0 : waddr[DP_WD-1:0] + 1;

always @(posedge clk or negedge rst_n)begin
	if(~rst_n) waddr <= 0;
	else if(wenc) waddr <= {waddr_d_h, waddr_d_l};
end

最高位彻底的作为标志位,当低位计数到 DEPTH-1 时,高位翻转。如此一来仍旧可以用经典的方式计算 fifo_cnt

wire [DP_WD :0]fifo_cnt = (waddr[DP_WD] == raddr[DP_WD]) ? waddr[DP_WD-1:0] - raddr[DP_WD-1:0]: (waddr[DP_WD-1:0] + DEPTH - raddr[DP_WD-1:0]);

当高位值一致时就 waddr-raddr,当高位值不一样时就 waddr+DEPTH-raddr ,得到了目前的 fifo 内数据量,注意,此时计算的 fifo_cnt 在时序上处于 winc/rinc 的下一拍,也就是数据真正写入/读出的那一拍。

下一步空满信号,空满信号的产生原理也很简单,fifo_cnt==0时为空,fifo_cnt==DEPTH时为满。但是网站上给出的参考括对照波形,wfull/rempty 信号的输出都是在 fifo_cnt 的下一拍,即 winc/rinc 的延后两拍,这是有问题的,下面这段代码是按照对比波形写出来的,也就是对比 pass 的行为:

wire rempty_d = (fifo_cnt == 0);

always @(posedge clk or negedge rst_n)begin
  if(~rst_n) rempty <= 0;
  else   rempty <= rempty_d;
end

wire wfull_d = (fifo_cnt == DEPTH);

always @(posedge clk or negedge rst_n)begin
  if(~rst_n) wfull <= 0;
  else   wfull <= wfull_d;
end

而后ram的wenc/renc的产生与wfull/rempty相关:
assign wenc = winc & (!wfull);
assign renc = rinc & (!rempty);

wenc/renc的产生思路没有什么,当满时不再写入ram空时不再读取ram这个行为是合理的,当然fifo内部不看wfull/rempty也没有什么问题,毕竟 fifo 把wfull/rempty给到外面就是为了让控制器做处理的:wfull置起后,winc不能为高,否则写行为不可控可能数据覆盖;rempty置起后,rinc不能为高,否则读行为不可控读数据不准。

那么话题回到wfull/rempty,根据上面的分析,wfull/rempty这两个信号的反馈必须在winc/rinc的下一拍得到,否则控制器无法及时的调整winc/rinc逻辑。举个例子,当前深度16的FIFO内已有15个数,cyc0 winc起请求写下一个数,cyc1 数据写入,cys2 wfull信号起。那么在cyc1 winc仍然可以置起写数(因为没看到wfull),哪怕这个时候在 fifo 内做了ram保护 wenc = winc & (!wfull) 也没有用,因此cyc1在内部也没有看到wfull信号。

用上面那个通过了网站测评的代码做以下测试,向深度 16 的 fifo 里写了 18 个数,然后读数,可以看到第一个数据已经被覆盖:

img

同时后续的 wfull/rempty 信号也乱了,因为 fifo_cnt 连带着都跳乱了。

因此 wfull/rempty 的产生必须在 winc/rinc 的下一拍:

wire rempty = (fifo_cnt == 0);
wire wfull = (fifo_cnt == DEPTH);

当然了,这样也会导致 fifo 的 winc/rinc时序变差(要看wfull/rempty),因此可以考虑winc/rinc当拍产生wfull_d/rempty_d,然后打拍得到wfull/rempty,代价是wfull_d/rempty_d的产生逻辑比较深,而且做起来稍微复杂了一点点(其实不复杂,就是要做一些选择信号啥的,我懒得做了)看取舍吧。我就用上面那种了:

img

这才是一个合理的fifo波形。

/**********************************SFIFO************************************/
module sfifo#(
    parameter    WIDTH = 8,
    parameter     DEPTH = 16
)(
    input                     clk        , 
    input                     rst_n    ,
    input                     winc    ,
    input                      rinc    ,
    input         [WIDTH-1:0]    wdata    ,
 
    output reg                wfull    ,
    output reg                rempty    ,
    output wire [WIDTH-1:0]    rdata
);
 
localparam DP_WD = $clog2(DEPTH);
 
reg  [DP_WD   :0]waddr;
wire             wenc;
wire             waddr_d_h;
wire [DP_WD -1:0]waddr_d_l;
assign wenc = winc & (!wfull);
assign waddr_d_h = (waddr[DP_WD-1:0] == DEPTH-1) ? ~waddr[DP_WD] : waddr[DP_WD];
assign waddr_d_l = (waddr[DP_WD-1:0] == DEPTH-1) ? 0 : waddr[DP_WD-1:0] + 1;
always @(posedge clk or negedge rst_n)begin
    if(~rst_n)    waddr <= 0;
    else if(wenc) waddr <= {waddr_d_h, waddr_d_l};
end
 
reg  [DP_WD   :0]raddr;
wire             renc;
wire             raddr_d_h;
wire [DP_WD -1:0]raddr_d_l;
assign renc = rinc & (!rempty);
assign raddr_d_h = (raddr[DP_WD-1:0] == DEPTH-1) ? ~raddr[DP_WD] : raddr[DP_WD];
assign raddr_d_l = (raddr[DP_WD-1:0] == DEPTH-1) ? 0 : raddr[DP_WD-1:0] + 1;
always @(posedge clk or negedge rst_n)begin
    if(~rst_n)    raddr <= 0;
    else if(renc) raddr <= {raddr_d_h, raddr_d_l};
end
 
wire [DP_WD :0]fifo_cnt = (waddr[DP_WD] == raddr[DP_WD]) ? waddr[DP_WD-1:0] - raddr[DP_WD-1:0]:
                          (waddr[DP_WD-1:0] + DEPTH - raddr[DP_WD-1:0]);
 
wire rempty_d = (fifo_cnt == 0);
always @(posedge clk or negedge rst_n)begin
    if(~rst_n)    rempty <= 0;
    else          rempty <= rempty_d;
end
 
wire wfull_d = (fifo_cnt == DEPTH);
always @(posedge clk or negedge rst_n)begin
    if(~rst_n)    wfull <= 0;
    else          wfull <= wfull_d;
end
 
dual_port_RAM #(.DEPTH(DEPTH), .WIDTH(WIDTH))
u_ram (
    .wclk    (clk),
    .wenc    (wenc),
    .waddr    (waddr),
    .wdata    (wdata),
    .rclk    (clk),
    .renc    (renc),
    .raddr    (raddr),
    .rdata    (rdata)
);
 
endmodule

47.格雷码计数器

题目

实现 4 bit 位宽的格雷码计数器。

img

解法

 input clk,
 input rst_n,

 output reg [3:0] gray_out
    reg[4:0] count;
    reg rev;
    always@(posedge clk or negedge rst_n)
    if(!rst_n)
        count <= 5'b0;
    else
        count <= count + 1'b1;
     
    always@(*)
    if(!rst_n)
        gray_out = 4'b0;
    else
        gray_out = count[4:1] ^ (count[4:1] >> 1);

48.多 bit MUX 同步器

题目

data_en为高期间,data_in将保持不变,data_en为高至少保持 3 个 B 时钟周期。表明,当data_en为高时,可将数据进行同步。

本题中data_in端数据变化频率很低,相邻两个数据间的变化,至少间隔 10 个 B 时钟周期。

img
端口 I/O 描述
clk_a input A时钟域时钟
clk_b input B时钟域时钟
arstn input A时钟域异步复位
brstn input B时钟域异步复位
data_in input A时钟数据输入
data_en input A时钟数据有效信号,高电平有效
data_out output B时钟域数据输出

解法

 input    clk_a , 
 input    clk_b , 
 input    arstn ,
 input    brstn ,
 input  [3:0] data_in ,
 input    data_en ,

 output reg [3:0] dataout

输入数据暂存data_reg中,使能信号data_en用打两拍的方式跨时钟域传输到时钟域B,最后data_out根据使能信号更新数据。

data_en信号在A时钟域用一个D触发器暂存,然后打两拍传输到B时钟域。

根据同步到B时钟域的使能信号data_en_b1,更新输出。

    reg [3:0] data_reg;
    reg       data_en_a, data_en_b0, data_en_b1;
     
    always@(posedge clk_a or negedge arstn) begin
        if(~arstn)
            data_reg <= 0;
        else
            data_reg <= data_in;
    end
     
    always@(posedge clk_a or negedge arstn) begin
        if(~arstn)
            data_en_a <= 0;
        else
            data_en_a <= data_en;
    end
     
    always@(posedge clk_b or negedge brstn) begin
        if(~brstn) begin
            data_en_b0 <= 0;
            data_en_b1 <= 0;
        end
        else begin
            data_en_b0 <= data_en_a;
            data_en_b1 <= data_en_b0;
        end
    end
     
    always@(posedge clk_b or negedge brstn) begin
        if(~brstn)
            dataout <= 0;
        else
            dataout <= data_en_b1? data_reg: dataout;
    end

49.脉冲同步电路

题目

从 A 时钟域提取一个单时钟周期宽度脉冲,然后在新的时钟域 B 建立另一个单时钟宽度的脉冲。

A 时钟域的频率是 B 时钟域的 10 倍;A时钟域脉冲之间的间隔很大,无需考虑脉冲间隔太小的问题。

电路的接口如下图所示。data_in是脉冲输入信号,data_out是新的脉冲信号;clk_fast是 A 时钟域时钟信号,clk_slow是 B 时钟域时钟信号;rst_n 是异步复位信号。

img

解法

 input    clk_fast , 
 input    clk_slow , 
 input    rst_n  ,
 input    data_in ,

 output    dataout

总体思路是将 A 时钟域的脉冲信号转换为电平信号,打两拍后再转换为 B 时钟域的脉冲信号。

img
reg data_level, data_level1, data_level2, data_level3;
 assign dataout = data_level3^data_level2;    
    // 脉冲信号转电平信号
    always@(posedge clk_fast or negedge rst_n) begin
        if(~rst_n)
            data_level <= 0;
        else
            data_level <= data_in? ~data_level: data_level;
    end
     
    // 电平信号打两拍再转为脉冲信号
    always@(posedge clk_slow or negedge rst_n) begin
        if(~rst_n) begin
            data_level1 <= 0;
            data_level2 <= 0;
            data_level3 <= 0;
        end
        else begin
            data_level1 <= data_level;
            data_level2 <= data_level1;
            data_level3 <= data_level2;
        end
    end

50. 简易秒表

题目

请编写一个模块,实现简易秒表的功能:具有两个输出,当输出端口 second 从 1-60 循环计数,每当second计数到 60,输出端口minute加一,一直到minute=60,暂停计数。

img img

输入描述:

clk:系统时钟信号

rst_n:异步复位信号,低电平有效

输出描述:

second:6比特位宽,秒表的秒读数

minute:6比特位宽,秒表的分读数

解法

 input clk,
 input rst_n,

 output reg [5:0]second,
 output reg [5:0]minute

首先确定 second 的取值逻辑:当 minute=60 时停止计数,即保持 second 为0;当 second=60 时,下一个周期 second 置为1。其余情况 second 等于前一时刻的值加一。

然后明确 minute 的取值逻辑:当 second=60minute 等于前一时刻的值加一。其余情况,minute 保持不变。

    always @(posedge clk or negedge rst_n)
        if (!rst_n)
            begin 
                minute <= 6'd0;
            end
        else if (second == 6'd60)
            begin
                minute <= minute+1;
            end
        else 
            begin  
                minute <= minute;
            end
         
    always @(posedge clk or negedge rst_n)
        if (!rst_n)
            begin 
                second <= 6'd0;
            end
        else if(second == 6'd60)
            begin
                second <= 6'd1;
            end
        else if (minute == 60)
            second <= 0;      
        else
            second <= second+1'd1;

51.可置位计数器

题目

请编写一个十六进制计数器模块,计数器输出信号递增每次到达 0,给出指示信号zero,当置位信号 set 有效时,将当前输出置为输入的数值 set_num

img img

输入描述:

clk:时钟信号

rst_n:复位信号,低电平有效

set:置位指示信号,当该信号有效时,表示将输出信号强制置为 set_num

set_num:4比特信号,当set信号有效时,将该信号的数字赋予输出信号 number

输出描述:

zero:过零指示信号,当 number 计数到0时,该信号为1,其余时刻为0

number:4比特位宽,表示计数器的当前读数

解法

 input clk,
 input rst_n,
 input set,
 input [3:0] set_num,

 output reg [3:0]number,
 output reg zero

多了一个num中间计数器,使时序后移了一个周期。这里也加入了一个中间计数器。

 always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            num <= 0;
        else
            num <= set? set_num: num+1;
    end
     
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            number <= 0;
        else
            number <= num;
    end
     
    always@(*) begin
        if(~rst_n)
            zero <= 0;
        else
            zero <= number==0;
    end

52.加减计数器

题目

请编写一个十进制计数器模块,当 mode 信号为 1,计数器输出信号递增,当 mode 信号为 0,计数器输出信号递减。每次到达 0,给出指示信号 zero。

img img

输入描述:

clk:系统时钟信号

rst_n:复位信号,低电平有效

mode:模式选择信号,当该信号为 1,计数器每个时钟加一;为 0,则每个时钟减一。

输出描述:

number:4 比特位宽,计数器当前输出读数。

zero:过零指示信号,当 numbe r为 0 时,该信号为 1,其他时刻为0.

解法

 input clk,
 input rst_n,
 input mode,
 
 output reg [3:0]number,
 output reg zero

根据波形要求,要添加一个中间计数器num

    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            num <= 0;
        else if(mode)
            num <= num==9? 0: num+1;
        else
            num <= num==0? 9: num-1;
    end
     
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            number <= 0;
        else
            number <= num;
    end
     
    always@(posedge clk or negedge rst_n) begin
        if(~rst_n)
            zero <= 0;
        else
            zero <= num==0;
    end

53.单端口RAM

题目

设计一个单端口 RAM ,它有: 写接口,读接口,地址接口,时钟接口和复位;存储宽度是 4 位,深度 128。

注意 rst为低电平复位

img

解法

 input clk,
 input rst,
 input enb,
 input [6:0]addr,
 input [3:0]w_data,
 //在testbench中,clk为周期5ns的时钟,rst为低电平复位

 output wire [3:0]r_data
    reg [3:0] myRAM [127:0];
    reg [3:0] r_data_r;
     
    // 写入RAM
    genvar i;
    generate
        for(i=0;i<128;i=i+1)
            always@(posedge clk or negedge rst) begin
                if(~rst)
                    myRAM[i] <= 0;
                else
                    myRAM[addr] <= enb? w_data: myRAM[addr];
            end
                     
    endgenerate
             
    // 读取RAM
    always@(*) begin
        if(~rst)
            r_data_r <= 0;
        else
            r_data_r <= ~enb? myRAM[addr]: r_data_r;
    end
 
    assign r_data = r_data_r;

54. RAM的简单实现

题目

实现一个深度为 8,位宽为 4 bit 的双端口 RAM,数据全部初始化为 0000。具有两组端口,分别用于读数据和写数据,读写操作可以同时进行。当读数据指示信号read_en 有效时,通过读地址信号 read_addr 读取相应位置的数据 read_data ,并输出;当写数据指示信号 write_en 有效时,通过写地址信号 write_addr 和写数据 write-data,向对应位置写入相应的数据。

img img

输入描述:

clk:系统时钟信号

rst_n:异步复位信号,低电平有效

read_en,write_en:单比特信号,读/写使能信号,表示进行读/写操作

read_addr,write_addr:8比特位宽的信号,表示读/写操作对应的地址

write_data:4比特位宽的信号,在执行写操作时写入RAM的数据

输出描述:

read_data:4比特位宽的信号,在执行读操作时从RAM中读出的数据

解法

 input clk,
 input rst_n,
 input write_en,
 input [7:0]write_addr,
 input [3:0]write_data,
 input read_en,
 input [7:0]read_addr,

 output reg [3:0]read_data
    //prameter
    parameter WIDTH = 4;
    parameter DEPTH = 8;
     
    //defination
    reg [WIDTH - 1 : 0] RAM [0 : DEPTH - 1];
 
    //output
    integer i;
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n) begin
               for(i = 0; i < DEPTH; i = i + 1) begin
                   RAM[i] <= 'd0;
               end
        end
        else if(write_en) RAM[write_addr] <= write_data;
    end
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n) read_data <= 'd0;
        else if(read_en) read_data <= RAM[read_addr];
        else read_data <= 'd0;
    end

55. Johnson Counter

题目

请用 Verilog 实现 4 位约翰逊计数器(扭环形计数器),计数器的循环状态如下。

imgimg

解法

 input    clk ,
 input    rst_n,

 output reg [3:0] Q 
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n) Q <= 'd0;
        else Q <= {~Q[0], Q[3 : 1]};
    end

56.流水线乘法器

题目

实现 4 bit 无符号数流水线乘法器设计。

img

解法

 input      clk  , 
 input      rst_n  ,
 input [size-1:0]   mul_a  ,
 input [size-1:0]   mul_b  ,

 output reg [size*2-1:0] mul_out 
    //parameter
    parameter N = size * 2;
    //defination
    wire [N - 1 : 0] temp [0 : 3];
     
    reg [N - 1 : 0] adder_0;
    reg [N - 1 : 0] adder_1;
     
    //output
    genvar i;
    generate
        for(i = 0; i < 4; i = i + 1)begin : loop
            assign temp[i] = mul_b[i] ? mul_a << i : 'd0;
        end
    endgenerate
     
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n) adder_0 <= 'd0;
        else adder_0 <= temp[0] + temp[1];
    end
     
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n) adder_1 <= 'd0;
        else adder_1 <= temp[2] + temp[3];
    end
     
    always@(posedge clk or negedge rst_n)begin
        if(!rst_n) mul_out <= 'd0;
        else mul_out <= adder_0 + adder_1;
    end

57.交通灯

题目

要求实现一个交通红绿灯,具有红黄绿三个小指示灯和一个行人按钮,正常情况下,机动车道指示灯按照60时钟周期绿灯,5个时钟周期黄灯,10个时钟周期红灯循环。当行人按钮按下,如果剩余绿灯时间大于10个时钟,则缩短为10个时钟,小于10个时钟则保持不变。

注:机动车道的指示灯和人行道指示灯应该是配对的,当机动车道的灯为绿或者黄时,人行道的灯为红;当机动车道的灯为红时,人行道的灯为绿,为简便起见,只考虑机动车道的指示灯。

img

输入描述:

clk:系统时钟信号

rst_n:复位信号,低电平有效

pass_request:行人按钮信号,当该信号为 1,表示按钮按下,如果剩余绿灯时间大于 10 个时钟,则缩短为 10 个时钟,小于 10 个时钟则保持不变。

输出描述:

clock:交通灯倒计时读数

red:该信号为1,表示红灯亮,为0表示红灯不亮

yellow:该信号为1,表示黄灯亮,为0表示黄灯不亮

green:该信号为1,表示黄灯亮,为0表示黄灯不亮

解法

该题为交通灯切换,但题目描述错误,该题的实际信号流程为红灯 10 个周期=>黄灯 5 个周期 => 绿灯 60 周期,题解需要再 rst 状态等待两个时钟周期再跳转到第一状态,故需要在复位后等待两个时钟再进入工作状态,且等待过程中时钟计数不停止

  input rst_n, //异位复位信号,低电平有效
  input clk, //时钟信号
  input pass_request,

  output wire[7:0]clock,
  output reg red,
  output reg yellow,
  output reg green
    parameter RST=3'd0,GREEN=3'd1,YELLOW=3'd2,RED=3'd3;
     
    reg [1:0] state,next_state;
    reg [7:0] clock_1;
     
    always @(*)
        case(state)
            RST    :next_state = clock_1==8'd8?RED   :   RST;
            GREEN  :next_state = clock_1==8'd1?RED   : GREEN;
            YELLOW :next_state = clock_1==8'd1? GREEN:YELLOW;
            RED    :next_state = clock_1==8'd1?YELLOW:   RED;
            default:next_state = RST;
        endcase
     
    always @(posedge clk or negedge rst_n)
        if(!rst_n)
            state <= RST;
        else
            state <= next_state;
           
    always @(posedge clk or negedge rst_n)
        if(!rst_n)
            clock_1 <= 8'd10;
        else begin
            if(clock_1==8'd1)
                case(state) 
                    GREEN,RST:clock_1 <= 8'd10;
                    YELLOW   :clock_1 <= 8'd60;
                    RED      :clock_1 <= 8'd5 ;
                    default  :clock_1 <= 8'd10;
                endcase
            else if(clock_1==8'd8&&state==RST)
                clock_1 <= 8'd10;
            else if(pass_request&&clock_1>8'd10&&state==GREEN)
                clock_1 <= 8'd10;
            else
                clock_1 <= clock_1 - 1'b1;
        end
     
    always @(posedge clk or negedge rst_n)
        if(!rst_n)begin
            red    <= 1'd0;
            yellow <= 1'd0;
            green  <= 1'd0;
        end else begin
            case(next_state)
                RST    :begin 
                            red    <= 1'd0;
                            yellow <= 1'd0;
                            green  <= 1'd0;
                        end
                GREEN  :begin 
                            red    <= 1'd0;
                            yellow <= 1'd0;
                            green  <= 1'd1;
                        end
                YELLOW :begin 
                            red    <= 1'd0;
                            yellow <= 1'd1;
                            green  <= 1'd0;
                        end
                RED    :begin 
                            red    <= 1'd1;
                            yellow <= 1'd0;
                            green  <= 1'd0;
                        end
                default:begin 
                            red    <= 1'd0;
                            yellow <= 1'd0;
                            green  <= 1'd0;
                        end
            endcase
        end
     
    assign clock = clock_1;

58.游戏机计费程序

题目

要求实现一个游戏机计费模块,某游戏机具有多个模式,价格不同:普通模式每分钟 1 元,畅玩模式每分钟收费 2 元,默认情况下为普通模式,在 boost 按键按下之后进入畅玩模式。

游戏机采用预付费模式,输入端口 money 的数值为预付费用,在 set 信号有效时,将 money 的数值读入。输出端口 remain 的数值为剩余费用,当费用小于 10元时,黄色信号灯 yellow 亮起。当费用不足时,红色信号灯 red 亮起,同时关闭电脑。在游戏过程中可以通过 set 端口续费。每次 set 信号有效将此时刻 money 的数值加到 remain 之中。

注:在程序中以每个时钟周期代表一分钟,每个单位大小表示1元。

img

输入描述:

clk:系统时钟信号

rst_n:复位信号,低电平有效

money:10bit位宽的数据,表示充值数额,当set信号有效时,将该信号的数值加到游戏余额remain中

set:充值信号,当信号等于1,表示用户充值。

boost:游戏机模式切换信号,为 1 时,表示进入畅玩模式,每个时钟扣费 2,即 remain 减二,为 0 时,表示普通模式,remain 每个时钟减一。

输出描述:

remain:10 bit 位宽的数据,表示余额,根据充值数额和游戏模式变化

yellow:指示灯,当 remain 大于 0 且小于 10 时,为1。

red:指示灯,当余额不足时为 1,其余时刻为 0。

解法

input rst_n, //异位复位信号,低电平有效
input clk, //时钟信号
input [9:0]money,
input set,
input boost,

output reg[9:0]remain,
output reg yellow,
output reg red

set==1时,进行投币,money会添加到余额remain上。boost==0是普通模式,每个时钟周期消耗1元;boost==0是畅玩模式,每个时钟周期消耗2元。当余额不足时停止计费(余额小于对应模式的要求)。

指示灯

黄灯yellow在余额小于10元时亮起,余额归零时灭掉;红灯red在余额不足时亮起。

always@(posedge clk or negedge rst_n) begin
	if(~rst_n) begin
		yellow <= 0;
		red    <= 0;
	end
	else begin
		yellow <= remain<10&&remain;
		red <= boost? remain<2: remain<1;
	end
end
     
always@(posedge clk or negedge rst_n) begin
	if(~rst_n)
		remain <= 0;
	else if(boost)
		remain <= set     ? remain+money:
                      		remain<2? remain:
                      		remain-2;
	else
	remain <= set     ? remain+money:
                      	remain<1? remain:
                      	remain-1;
end

标签:clk,31,牛客,rst,input,网手,data,reg,时钟
From: https://www.cnblogs.com/accumulagain/p/17226849.html

相关文章

  • 2203031124-罗浩
    项目内容课程班级名称链接填写课程班级博客链接作业链接填写作业要求链接博客名称2203031124-罗浩-第一次c语言作业-作业1要求每道题要求有题目,代码(使......
  • 牛客网手撕代码(1-30)
    1.四选一多路选择器题目制作一个四选一的多路选择器,要求输出定义上为线网类型状态转换:状态序号d011d110d201d300解法input[1:0]d1,d2,d......
  • PAT Basic 1031. 查验身份证
    PATBasic1031.查验身份证1.题目描述:一个合法的身份证号码由17位地区、日期编号和顺序编号加1位校验码组成。校验码的计算规则如下:首先对前17位数字加权求和,权重分配......
  • 滴水 316 复习 结构体
    结构体1.为什么需要结构体需要一个东西来存储非常多的东西。比如生命蓝量坐标2.结构体定义赋值structMyStruct{inta;intb;intc;};intmain(){MyStruct......
  • 2203031146
    2203031146-张贤举c语言第一周作业项目内容课程班级博客链接https://edu.cnblogs.com/campus/pexy/22sj作业要求博客链接https://edu.cnblogs.com/campus/p......
  • 31-40-阻抗-拉线
    1mm板厚,1.4mil铜箔,1080pp片3mil,绿油0.5mil阻抗匹配:Diff---100欧-3.93mil-6mil间距  差分线间距(线对之间,线内)线内:  线对:  框选区域删除(右键)按照设定......
  • 江南信息学2023第四周练习20230317 题解
    首先,通报批评上周抄袭题解的同学有:黄耿益,黄远鸿,博客提供题解不是让大家直接复制粘贴抄袭的,而是在大家不会做时提供思路和解决方案,可以抄写,但不允许直接复制粘贴抄袭,请养成......
  • 20230314-python-字典与json
    1.字典的定义                      ......
  • 20212323严文霞--数据库读书笔记一(P3-P18,P31-P33)
    1.1数据库系统概述1.1.1数据库的4个基本概念数据(data)定义:描述事物的符号记录称为数据。数据有多种表现形式,例如数字、文字、图形、图像、音视频等;数据需要进行解......
  • 0307-0314模块与包
    0307-03141.模块的简介#1.定义:一系列功能的集合#2.作用:拿来主义,极大提高开发效率#3.来源: 1.内置:#python解释器自带的,直接拿来使用的 2.第三方:#别人写的,如果想......