0%

Verilog之入门级刷题

本文主要对牛客网Verilog刷题中的入门习题进行记录,其中标题带*号的表示题目相对有学习价值。

四选一多路器

  • 制作一个四选一的多路选择器,要求输出定义上为线网类型

    image-20231110153403923
  • Mux41.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    module Mux41(
    input [1:0]d1,d2,d3,d0,
    input [1:0]sel,
    output[1:0]mux_out
    );

    reg [1:0] mux_out_reg;

    always @(*) begin
    case(sel)
    2'b00: mux_out_reg = d3;
    2'b01: mux_out_reg = d2;
    2'b10: mux_out_reg = d1;
    2'b11: mux_out_reg = d0;
    default: mux_out_reg = 2'b00;
    endcase
    end

    assign mux_out = mux_out_reg;

    endmodule
  • Mux41_tb.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    module Mux41_tb;
    reg [1:0]d1,d2,d3,d0;
    reg [1:0]sel;
    wire[1:0]mux_out;

    Mux41 u1(
    .d1(d1),
    .d2(d2),
    .d3(d3),
    .d0(d0),
    .sel(sel),
    .mux_out(mux_out)
    );

    initial begin
    d1 = 0;
    d2 = 1;
    d3 = 2;
    d0 = 3;
    #10 sel = 0;
    #10 sel = 1;
    #10 sel = 2;
    end

    endmodule
    • 结果如下:

      image-20231110153859533

      RTL原理图:

      image-20231110154235961

异步复位的串联T触发器

  • T触发器逻辑功能为:在脉冲有效边沿到来时,T=0,触发器状态不变Qn+1=Qn;T=1,Qn=~Qn

  • Tff_2.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    module Tff_2(
    input wire data, clk, rst,
    output reg q
    );

    reg data_reg;

    always @(posedge clk or negedge rst) begin
    if(!rst)begin
    data_reg <= 0;
    end
    else begin
    if(data == 0) begin
    data_reg <= data_reg;
    end
    else if (data == 1) begin
    data_reg <= ~data_reg;
    end
    end
    end

    always @(posedge clk or negedge rst) begin
    if(!rst)begin
    q <= 0;
    end
    else begin
    if(data_reg == 0) begin
    q <= q;
    end
    else if (data_reg == 1) begin
    q <= ~q;
    end
    end
    end

    endmodule
  • Tff_2_tb.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    `define CLK_PERIOD 10

    module Tff_2_tb;
    reg data, clk, rst;
    wire q;

    Tff_2 u1(
    .data(data),
    .clk(clk),
    .rst(rst),
    .q(q)
    );

    //时钟信号的产生
    initial clk = 0;
    always #(`CLK_PERIOD/2) clk = ~clk;

    //复位和结束信号的产生
    initial begin
    rst = 0;
    #11 rst = 1;
    #1000 $stop;
    end

    //其他激励信号
    initial begin
    data = 0;
    #21 data = 1;
    #20 data = 0;
    #40 data = 1;
    #20 data = 0;
    end
    endmodule
    • 结果如下:

      image-20231110164517235

      RTL原理图:

      image-20231110172248731


奇偶校验

  • 如果是奇数个1 则结果为1,为偶数则结果为0(通过sel选择是进行奇校验还是偶校验)

  • 运用单目运算符(|,^,&)可以进行如下检测:

    • e = &d;//检测是否全为1(若全为1,则e=1,若有0,则e=0)
    • f = ^d;//奇偶校验(检测1的个数是奇数还是偶数,若是奇数,则f=1,若是偶数,则f=0)
    • g = |d;//检测是否全为0(若全为0,则g=0,若有1,则g=1)
  • odd_sel.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    module odd_sel(
    input [31:0] bus,
    input sel,
    output check
    );

    //使用单目运算符^判断bus中1的个数,若奇数个1,则结果为1,若偶数个1,则结果为0
    wire odd_even;
    assign odd_even = ^bus;

    //sel == 1意味着进行奇校验,sel == 0意味着进行偶校验
    assign check = sel ? odd_even : ~odd_even;

    endmodule
    • RTL原理图:

      image-20231110175239174

移位运算与乘法*

  • 已知d为一个8位数,请在每个时钟周期分别输出该数乘1/3/7/8,并输出一个信号通知此时刻输入的d有效(d给出的信号的上升沿表示写入有效)

    • 状态机与移位运算逻辑:

      状态 输出位运算
      1 d
      3 (din << 2) - din
      7 (din << 3) - din
      8 (din << 3)
    • 状态机不受输入信号控制,循环跳转就可以

    • 状态输出采用时序电路的方式,这样可以节省一个复位状态,在rst归0时输出为0

  • multi_sel.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    module multi_sel(
    input clk,
    input rst,
    input [7:0]d,
    output reg input_grant,
    output reg [10:0]out
    );

    //状态定义
    parameter S0 = 4'd1,
    S1 = 4'd3,
    S2 = 4'd7,
    S3 = 4'd8;

    reg [3:0] state;
    reg [3:0] next_state;

    reg [7:0] din;

    //状态跳转
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    state <= S0;
    end
    else begin
    state <= next_state;
    end
    end

    //下一状态的判断
    always @(*) begin
    case(state)
    S0: next_state <= S1;
    S1: next_state <= S2;
    S2: next_state <= S3;
    S3: next_state <= S0;
    default: next_state <= S0;
    endcase
    end

    //状态对应输出
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    out <= 11'd0;
    input_grant <= 0;
    din <= 8'd0;
    end
    else begin
    case(state)
    S0: begin
    din <= d; //这里很妙,能保证后面的都是与din相乘,而不是与新输入的d相乘
    out <= d;
    input_grant <= 1;
    end
    S1: begin
    out <= (din << 2) - din; //d*3 = d*4-d
    input_grant <= 0;
    end
    S2: begin
    out <= (din << 3) - din; //d*7 = d*8-d
    input_grant <= 0;
    end
    S3: begin
    out <= (din << 3);
    input_grant <= 0;
    end
    endcase
    end
    end

    endmodule
  • multi_sel_tb.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    `define CLK_PERIOD 10

    module multi_sel_tb;

    reg clk;
    reg rst;
    reg [7:0] d;
    wire input_grant;
    wire [10:0] out;

    multi_sel u1(
    .clk(clk),
    .rst(rst),
    .d(d),
    .input_grant(input_grant),
    .out(out)
    );

    //时钟信号的产生
    initial clk = 0;
    always #(`CLK_PERIOD/2) clk = ~clk;

    //复位和结束信号的产生
    initial begin
    rst = 0;
    #11 rst = 1;
    #1000 $stop;
    end

    //其他激励信号
    initial begin
    d = 143;
    #40 d = 7;
    #50 d = 6;
    #10 d = 128;
    #20 d = 129;
    end

    endmodule
    • 结果如下:

      image-20231110194006603


位拆分与运算*

  • 现在输入了一个压缩的16位数据,按照sel选择输出四个数据的相加结果,并输出valid_out信号(在不输出时候拉低)

    • 0: 不输出且只有此时的输入有效
    • 1:输出[3:0]+[7:4]
    • 2:输出[3:0]+[11:8]
    • 3:输出[3:0]+[15:12]
    image-20231112014304793
  • 这题的关键其实在于:只有当sel信号为0时,输入数据才被锁存,后续执行的都是锁存的这个数据,只有当sel信号再次为0时,输入数据才会被再次锁存,而在这期间输入信号的改变,对输出是不起作用的。这一部分的代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    reg [15:0] data_lock;  
    //sel==0时将输入数据锁住,只有当sel再次为零的时候,输入的数据才有效
    always@(posedge clk or negedge rst) begin
    if (!rst)
    data_lock <= 0;
    else if(!sel)
    data_lock <= d; //后续sel不等于0时都是对这个data_lock操作
    end
  • data_cal.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    module data_cal(
    input clk,
    input rst,
    input [15:0] d,
    input [1:0] sel,

    output [4:0] out,
    output validout
    );

    reg [4:0] out_reg;
    reg validout_reg;

    reg [15:0] data_lock;
    //sel==0时将输入数据锁住,只有当sel再次为零的时候,输入的数据才有效
    always@(posedge clk or negedge rst) begin
    if (!rst)
    data_lock <= 0;
    else if(!sel)
    data_lock <= d;
    end

    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    out_reg <= 5'b0;
    validout_reg <= 1'b0;
    end
    else begin
    case(sel)
    2'b00: begin
    out_reg <= 5'b0;
    validout_reg <= 1'b0;
    end
    2'b01: begin
    out_reg <= data_lock[3:0] + data_lock[7:4];
    validout_reg <= 1'b1;
    end
    2'b10: begin
    out_reg <= data_lock[3:0] + data_lock[11:8];
    validout_reg <= 1'b1;
    end
    2'b11: begin
    out_reg <= data_lock[3:0] + data_lock[15:12];
    validout_reg <= 1'b1;
    end
    default: begin
    out_reg <= 5'b0;
    validout_reg <= 1'b0;
    end
    endcase
    end
    end

    assign out = out_reg;
    assign validout = validout_reg;

    endmodule
    • RTL原理图

      image-20231112015014386


多功能数据处理器*

  • 根据指示信号select的不同,对输入信号a,b实现不同的运算。输入信号a,b为8bit有符号数,当select信号为0,输出a;当select信号为1,输出b;当select信号为2,输出a+b;当select信号为3,输出a-b

    image-20231112143533256
  • 此题提到了一个有符号数,我的理解是,在FPGA的加减法运算中,如果没有bit的溢出,那么其实你定义是无符号还是有符号,计算的结果都是一样的,那么为了防止溢出,在进行有符号加减法运算时,我们对输入的数据通常扩展一个符号位。对于加法运算通常将结果扩展为length(max(a,b)+1)

  • 对于有符号数+无符号数的情况,有符号位前面扩展一个符号位,无符号数前面扩展0,结果用有符号数显示

  • 对于无符号数+无符号数的情况,可以不扩展,因为要扩展也是扩展0,所有没必要

  • 所以我的建议是,在进行加减运算时,其实不需要指定输入数据是否signed,运算时进行相应符号位扩展就可

  • data_select.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    module data_select(
    input clk,
    input rst_n,
    input signed[7:0]a,
    input signed[7:0]b,
    input [1:0]select,
    output reg signed [8:0]c
    );

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n)begin
    c <= 9'b0;
    end
    else begin
    case(select)
    2'b00: c <= {a[7], a};
    2'b01: c <= {b[7], b};
    2'b10: c <= {a[7], a} + {b[7], b};
    2'b11: c <= {a[7], a} - {b[7], b};
    default: c <= 9'b0;
    endcase
    end
    end

    endmodule
  • data_select_tb.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    `define CLK_PERIOD 10

    module data_select_tb;
    reg clk;
    reg rst_n;
    reg signed[7:0]a;
    reg signed[7:0]b;
    reg [1:0]select;
    wire signed [8:0]c;

    data_select u1(
    .clk(clk),
    .rst_n(rst_n),
    .a(a),
    .b(b),
    .select(select),
    .c(c)
    );

    //时钟信号的产生
    initial clk = 0;
    always #(`CLK_PERIOD/2) clk = ~clk;

    //复位和结束信号的产生
    initial begin
    rst_n = 0;
    #11 rst_n = 1;
    #1000 $stop;
    end

    initial begin
    a = 8'b0;
    b = 8'b0;
    select = 2'b0;
    #12 a = 8'd127;
    b = -8'd100;
    select = 2'd2;
    #20 a = -8'd125;
    b = -8'd128;
    select = 2'd3;
    end

    endmodule
    • 结果如下:

      image-20231112144734119


求两个数的差值

  • 根据输入信号a,b的大小关系,求解两个数的差值:输入信号a,b为8bit位宽的无符号数。如果a>b,则输出a-b,如果a≤b,则输出b-a。

    image-20231112152523054
  • data_minus.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    module data_minus(
    input clk,
    input rst_n,
    input [7:0]a,
    input [7:0]b,

    output reg [8:0]c
    );

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    c <= 9'b0;
    end
    if(a > b) begin
    c <= a - b;
    end
    else begin
    c <= b - a;
    end
    end

    endmodule
  • 这里有个点是:在Verilog HDL中,仍然有必要根据设计需要采用关键字signed对信号进行声明。 例如,在进行比较运算时,对于无符号数据,1000大于0100;对于有 符号数据,1000小于0100。(虽然在此题中都是无符号,所以不用考虑)


使用generate…for语句简化代码*

  • 在某个module中包含了很多相似的连续赋值语句,请使用generata…for语句编写代码,替代该语句,要求不能改变原module的功能。使用Verilog HDL实现以上功能并编写testbench验证。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    module template_module( 
    input [7:0] data_in,
    output [7:0] data_out
    );
    assign data_out [0] = data_in [7];
    assign data_out [1] = data_in [6];
    assign data_out [2] = data_in [5];
    assign data_out [3] = data_in [4];
    assign data_out [4] = data_in [3];
    assign data_out [5] = data_in [2];
    assign data_out [6] = data_in [1];
    assign data_out [7] = data_in [0];

    endmodule
  • gen_for_module.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    module gen_for_module(
    input [7:0] data_in,
    output [7:0] data_out
    );

    genvar i;
    generate for(i = 0; i < 8; i = i + 1) begin: bit_block_name
    assign data_out[i] = data_in[7 - i];
    end
    endgenerate

    endmodule
  • 在begin之后的bit_block_name,表示该generate…for语句块的名称,可以根据需要修改。特别需要注意的是,即使只有一个语句,也需要使用begin…end。同时需要使用endgenerate表示结束

    image-20231112154435953

使用子模块实现三输入数的大小比较

  • 请编写一个子模块,将输入两个8bit位宽的变量data_a,data_b,并输出data_a,data_b之中较小的数。并在主模块中例化,实现输出三个8bit输入信号的最小值的功能。

  • main_mod.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    module main_mod(
    input clk,
    input rst_n,
    input [7:0]a,
    input [7:0]b,
    input [7:0]c,

    output [7:0]d
    );

    wire [7:0] temp1;
    wire [7:0] temp2;

    child_mod u1(
    .clk(clk),
    .rst_n(rst_n),
    .a(a),
    .b(b),
    .d(temp1)
    );

    child_mod u2(
    .clk(clk),
    .rst_n(rst_n),
    .a(a),
    .b(c),
    .d(temp2)
    );

    child_mod u3(
    .clk(clk),
    .rst_n(rst_n),
    .a(temp1),
    .b(temp2),
    .d(d)
    );

    endmodule
  • child_mod.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    module child_mod(
    input clk,
    input rst_n,
    input [7:0]a,
    input [7:0]b,
    output [7:0]d
    );

    reg [7:0] d_reg;

    always @ (posedge clk or negedge rst_n) begin
    if( ~rst_n ) begin
    d_reg <= 8'b0;
    end
    else begin
    if( a > b )
    d_reg <= b;
    else
    d_reg <= a;
    end
    end

    assign d = d_reg;

    endmodule
    • RTL原理图
    image-20231112161742832

使用函数实现数据大小端转换

  • 请用函数实现一个4bit数据大小端(高位bit与低位bit对应互换)转换的功能。实现对两个不同的输入分别转换并输出。

    image-20231112163254237
  • function_mod.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    module function_mod(
    input [3:0]a,
    input [3:0]b,

    output [3:0]c,
    output [3:0]d
    );

    assign c = data_bit_converse(a, 4);
    assign d = data_bit_converse(b, 4);

    function [3:0] data_bit_converse;
    input [3:0] data; //输入变量
    input [2:0] data_bit;

    integer i; //函数内的变量
    begin
    for(i = 0; i < data_bit; i = i + 1) begin
    data_bit_converse[i] = data[data_bit - i - 1];
    end
    end
    endfunction

    endmodule

4位数值比较器电路

  • 某4位数值比较器的功能表如下。请用Verilog语言采用门级描述方式,实现此4位数值比较器

    image-20231116195138065
  • 先用门级电路描述1bit比较器

    A B F(A>B) F(A<B) F(A=B)
    0 0 0 0 1
    0 1 0 1 0
    1 0 1 0 0
    1 1 0 0 1
    • 则有:
      $$
      F(A>B) = A\bar B\
      F(A<B) = \bar A B\
      F(A=B) = \bar A\bar B + AB =\overline{A\bar B+\bar AB}
      $$
  • 最后再调用四个1bit比较器,根据题目所述表格将其串起来

  • comparator_4.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    module comparator_4(
    input [3:0] A,
    input [3:0] B,
    output wire Y2, //A>B
    output wire Y1, //A=B
    output wire Y0 //A<B
    );

    wire W_y2[3:0];
    wire W_y1[3:0];
    wire W_y0[3:0];

    genvar gen_i;
    for (gen_i = 0; gen_i < 4; gen_i = gen_i + 1) begin
    compare_1 compare_1_u(
    .A (A[gen_i] ),
    .B (B[gen_i] ),
    .Y2(W_y2[gen_i]),//A>B
    .Y1(W_y1[gen_i]),//A=B
    .Y0(W_y0[gen_i]) //A<B
    );
    end

    assign Y2 = W_y2[3] | ((W_y1[3]) & (W_y2[2])) | ((W_y1[3]) & (W_y1[2]) & (W_y2[1])) | ((W_y1[3]) & (W_y1[2]) & (W_y1[1]) & (W_y2[0]));
    assign Y0 = W_y0[3] | ((W_y1[3]) & (W_y0[2])) | ((W_y1[3]) & (W_y1[2]) & (W_y0[1])) | ((W_y1[3]) & (W_y1[2]) & (W_y1[1]) & (W_y0[0]));
    assign Y1 = W_y1[3] & W_y1[2] & W_y1[1] & W_y1[0];

    endmodule

    module compare_1(
    input A,
    input B,
    output Y2,//A>B
    output Y1,//A=B
    output Y0 //A<B
    );

    assign Y2 = A & (!B);
    assign Y0 = (!A) & B;
    assign Y1 = !(Y2 | Y0);

    endmodule

4bit超前进位加法器

  • 请用Verilog语言采用门级描述方式,实现此4bit超前进位加法器,其逻辑表达式如下:

    • 中间变量:$G_i = A_i B_i,P_i = A_i \bigoplus B_i$
    • 和:$S_i = P_i\bigoplus C_{i-1}$
    • 进位:$C_i = G_i + P_i C_{i-1}$
  • lca_4.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    module lca_4(
    input [3:0] A_in,
    input [3:0] B_in,
    input C_1,
    output wire CO,
    output wire [3:0] S
    );

    wire [3:0] G;
    wire [3:0] P;
    wire [3:0] C;

    assign G = A_in & B_in; //按位与就ok
    assign P = A_in ^ B_in;

    assign C[0] = G[0] | P[0] & C_1;
    assign C[1] = G[1] | P[1] & C[0];
    assign C[2] = G[2] | P[2] & C[1];
    assign C[3] = G[3] | P[3] & C[2];

    assign S[0] = P[0] ^ C_1;
    assign S[1] = P[1] ^ C[0];
    assign S[2] = P[2] ^ C[1];
    assign S[3] = P[3] ^ C[2];

    assign CO = C[3];

    endmodule
    • RTL原理图

      image-20231116203246152


根据状态转移图或表写状态机

  • 某同步时序电路转换表如下,请使用D触发器和必要的逻辑门实现此同步时序电路,用Verilog语言描述。

    image-20240110012824743
    • 以上状态转移表对应的状态转移图如下:

      image-20240110012938022
  • Machine_state_design1.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    module Machine_state_design1(
    input A ,
    input clk ,
    input rst_n,

    output wire Y
    );

    //状态定义
    parameter S0 = 2'b00,
    S1 = 2'b01,
    S2 = 2'b10,
    S3 = 2'b11;

    //状态的中间变量
    reg [1:0] state, next_state;

    //状态跳转
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    state <= S0;
    end
    else
    state <= next_state;
    end

    //次态判断
    always @(*) begin
    case(state)
    S0: begin
    if(A) next_state = S3;
    else next_state = S1;
    end
    S1: begin
    if(A) next_state = S0;
    else next_state = S2;
    end
    S2: begin
    if(A) next_state = S1;
    else next_state = S3;
    end
    S3: begin
    if(A) next_state = S2;
    else next_state = S0;
    end
    default: next_state = S0;
    endcase
    end

    //输出
    assign Y = (state == S3) ? 1'b1: 1'b0;

    endmodule

ROM的简单实现

  • 实现一个深度为8,位宽为4bit的ROM,数据初始化为0,2,4,6,8,10,12,14。可以通过输入地址addr,输出相应的数据data。接口信号图如下:

    image-20240110193442403
  • ROM_easy_design.v

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    module ROM_easy_design(
    input clk,
    input rst_n,
    input [7:0]addr,

    output [3:0]data
    );

    reg [3:0] value [7:0];
    reg [3:0] data_r;

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    data_r <= 4'b0;
    value[0] <= 4'd0;
    value[1] <= 4'd2;
    value[2] <= 4'd4;
    value[3] <= 4'd6;
    value[4] <= 4'd8;
    value[5] <= 4'd10;
    value[6] <= 4'd12;
    value[7] <= 4'd14;
    end
    else begin
    data_r <= value[addr];
    end
    end

    assign data = data_r;

    endmodule

Reference

  • 移位运算和乘法这一题有人(移位运算与乘法_牛客题霸_牛客网 (nowcoder.com))提供了更加简单的写法(它这相当于将cnt当成4个状态了,并用一段式状态机描述):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    `timescale 1ns/1ns
    module multi_sel(
    input [7:0]d ,
    input clk,
    input rst,
    output reg input_grant,
    output reg [10:0]out
    );

    reg [1:0]cnt;
    reg [7:0]din;

    always@(posedge clk or negedge rst) begin
    if(!rst) begin
    cnt <= 0;
    out <= 0;
    input_grant <= 0;
    din <= 0;
    end
    else begin
    cnt <= cnt+1;
    case (cnt)
    0: begin
    din <= d;
    input_grant <= 1;
    out <= d;
    end
    1: begin
    input_grant <= 0;
    out <= (din<<2)-din;
    end
    2: begin
    input_grant <= 0;
    out <= (din<<3)-din;
    end
    3: begin
    input_grant <= 0;
    out <= (din<<3);
    end
    endcase
    end
    end

    endmodule
  • 关于子模块实现三输入数的大小比较时,需要调用3个模块,这里很多同学可能疑惑为什么用3个而不是2个。参考:FPGA数字IC牛客网Verilog刷题09-子模块例化_哔哩哔哩_bilibili,解释如下:

    • 第一个模块:比较 T 时刻的 a 和 b,T+1 时刻出来 tmp1; 第二个模块:比较 T 时刻的 a 和 c,T+1 时刻 出来 tmp2; 第三个模块:比较 T+1 时刻的 tmp1 和 tmp2,T+2 时刻出来 d;
    • 如果只用 2 个子模块,那么 T时刻比较 a和 b得到 tmp1 ,再比较 tmp1和 c的时候是 T+1时刻的 c 和 T+1时刻的 tmp1 ,而 tmp1代表的是 T时刻 a和 b的较小值,所以这时候比较的 T时刻的 a 、 b 和 T+1时刻的 c ,显然不符合要求。
欢迎来到ssy的世界