0%

Verilog之进阶级刷题

本文主要对牛客网Verilog刷题中的进阶习题进行记录。

输入序列连续的序列检测

  • 请编写一个序列检测模块,检测输入信号a是否满足01110001序列,当信号满足该序列,给出指示信号match。

    image-20240113174445243

  • sequence_detect_easy.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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    module sequence_detect_easy(
    input clk,
    input rst_n,
    input a,
    output reg match
    );

    //状态定义
    parameter IDLE = 4'd0, //初始状态
    S1 = 4'd1, //"0"
    S2 = 4'd2, //"01"
    S3 = 4'd3, //"011"
    S4 = 4'd4, //"0111"
    S5 = 4'd5, //"01110"
    S6 = 4'd6, //"011100"
    S7 = 4'd7, //"0111000"
    S8 = 4'd8; //"01110001"

    reg [3:0] state,next_state;

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

    //次态判断
    always @(*) begin
    case (state)
    IDLE: next_state = a ? IDLE : S1;
    S1: next_state = a ? S2 : S1;
    S2: next_state = a ? S3 : S1;
    S3: next_state = a ? S4 : S1;
    S4: next_state = a ? IDLE : S5;
    S5: next_state = a ? S2 : S6;
    S6: next_state = a ? S2 : S7;
    S7: next_state = a ? S8 : S1;
    S8: next_state = a ? S3 : S1; //这里实际上重复检测了,重复的是“011”
    default: next_state = IDLE; //本来想省略一个状态的,但若省略S8,那么输出会提前一个clk输出,且不能重复检测
    endcase
    end

    //输出时序
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    match <= 1'b0;
    end
    else begin
    match <= (state == S8) ? 1 : 0;
    end
    end

    endmodule

    /*使用移位寄存器实现序列检测器会更简单
    `timescale 1ns/1ns
    module sequence_detect(
    input clk,
    input rst_n,
    input a,
    output reg match
    );

    reg [7:0] a_tem;

    always @(posedge clk or negedge rst_n)
    if (!rst_n)
    begin
    match <= 1'b0;
    end
    else if (a_tem == 8'b0111_0001)
    begin
    match <= 1'b1;
    end
    else
    begin
    match <= 1'b0;
    end

    always @(posedge clk or negedge rst_n)
    if (!rst_n)
    begin
    a_tem <= 8'b0;
    end
    else
    begin
    a_tem <= {a_tem[6:0],a};
    end
    endmodule
    */

含有无关项的序列检测*

  • 请编写一个序列检测模块,检测输入信号a是否满足011XXX110序列(长度为9位数据,前三位是011,后三位是110,中间三位不做要求),当信号满足该序列,给出指示信号match。

    image-20240916123454507
  • 该题使用移位寄存器可以很好的实现

  • 源代码

    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
    module sequence_detect(
    input clk,
    input rst_n,
    input a,
    output reg match
    );

    reg [8 : 0] a_shift_reg;

    //移位寄存器
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    a_shift_reg <= 9'd0;
    end
    else begin
    a_shift_reg <= {a_shift_reg[7 : 0], a};
    end
    end

    //match判断
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    match <= 1'b0;
    end
    else begin
    if(a_shift_reg[8 -: 3] == 3'b011 && a_shift_reg[0 +: 3] == 3'b110) begin
    match <= 1'b1;
    end
    else begin
    match <= 1'b0;
    end
    end
    end

    endmodule

不重复序列检测*

  • 请编写一个序列检测模块,检测输入信号(a)是否满足011100序列, 要求以每六个输入为一组,不检测重复序列,例如第一位数据不符合,则不考虑后五位。一直到第七位数据即下一组信号的第一位开始检测。当信号满足该序列,给出指示信号match。当不满足时给出指示信号not_match。

    模块的接口信号图如下:(要求必须用状态机实现)

    image-20240916134936207
  • 添加一个ERROR状态,当出现某位出现错误是,不再检测后面的位,只是等待这组数据的结束

  • 源代码:

    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    module Sequence_detect_no_repeat(
    input clk,
    input rst_n,
    input data,
    output reg match,
    output reg not_match
    );

    //===============================参数定义=========================
    //状态定义
    localparam IDLE = 4'd0,
    S1 = 4'd1,
    S2 = 4'd2,
    S3 = 4'd3,
    S4 = 4'd4,
    S5 = 4'd5,
    ERROR = 4'd7;

    //现态与次态的定义
    reg [3 : 0] cur_state, next_state;

    //计数器
    reg [2 : 0] counter;

    //===============================状态机逻辑=========================
    //计数器逻辑
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    counter <= 'd0;
    end
    else begin
    if(counter == 5) begin
    counter <= 'd0;
    end
    else begin
    counter <= counter + 1;
    end
    end
    end

    //1.状态转移
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    cur_state <= IDLE;
    end
    else begin
    cur_state <= next_state;
    end
    end

    //2.次态判断
    always @(*) begin
    next_state = IDLE;
    case(cur_state)
    IDLE: begin
    if(data == 1'b0) begin
    next_state = S1;
    end
    else begin
    next_state = ERROR;
    end
    end
    S1: begin
    if(data == 1'b1) begin
    next_state = S2;
    end
    else begin
    next_state = ERROR;
    end
    end
    S2: begin
    if(data == 1'b1) begin
    next_state = S3;
    end
    else begin
    next_state = ERROR;
    end
    end
    S3: begin
    if(data == 1'b1) begin
    next_state = S4;
    end
    else begin
    next_state = ERROR;
    end
    end
    S4: begin
    if(data == 1'b0) begin
    next_state = S5;
    end
    else begin
    next_state = ERROR;
    end
    end
    S5: begin
    if(data == 1'b0) begin
    next_state = IDLE;
    end
    else begin
    next_state = ERROR;
    end
    end
    ERROR: begin
    if(counter == 5) begin
    next_state = IDLE;
    end
    else begin
    next_state = ERROR;
    end
    end
    endcase
    end

    //3.输出逻辑
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    match <= 1'b0;
    end
    else begin
    if(cur_state == S5 && data == 1'b0) begin
    match <= 1'b1;
    end
    else begin
    match <= 1'b0;
    end
    end
    end

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    not_match <= 1'b0;
    end
    else begin
    if(cur_state == ERROR && counter == 'd5) begin
    not_match <= 1'b1;
    end
    else begin
    not_match <= 1'b0;
    end
    end
    end

    endmodule

输入序列不连续的序列检测*

  • 请编写一个序列检测模块,输入信号端口为data,表示数据有效的指示信号端口为data_valid。当data_valid信号为高时,表示此刻的输入信号data有效,参与序列检测;当data_valid为低时,data无效,抛弃该时刻的输入。当输入序列的有效信号满足0110时,拉高序列匹配信号match。(要求用状态机实现)

    image-20240916142134289
  • 注意判断输出时,由于S3只是011,所以还需对此时输入data(且有效)判断cur_state == S3 && data == 1'b0 && data_valid == 1'b1

  • 源代码:

    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    module Sequence_detect_Discontinuous(
    input clk,
    input rst_n,
    input data,
    input data_valid,
    output reg match
    );

    //===============================参数定义=========================
    //状态定义
    localparam IDLE = 2'd0,
    S1 = 2'd1,
    S2 = 2'd2,
    S3 = 2'd3;

    //现态与次态定义
    reg [1 : 0] cur_state, next_state;

    //===============================状态机逻辑=========================
    //1.状态转移
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    cur_state <= IDLE;
    end
    else begin
    cur_state <= next_state;
    end
    end

    //2.次态判断
    always @(*) begin
    next_state = IDLE;
    case(cur_state)
    IDLE: begin
    if(data_valid && data == 1'b0) begin
    next_state = S1;
    end
    else begin
    next_state = IDLE;
    end
    end
    S1: begin
    if(data_valid && data == 1'b1) begin
    next_state = S2;
    end
    else begin
    next_state = S1;
    end
    end
    S2: begin
    if(data_valid && data == 1'b1) begin
    next_state = S3;
    end
    else begin
    next_state = S2;
    end
    end
    S3: begin
    if(data_valid && data == 1'b0) begin
    next_state = IDLE;
    end
    else begin
    next_state = S3;
    end
    end
    endcase
    end

    //3.输出逻辑
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    match <= 1'b0;
    end
    else begin
    if(cur_state == S3 && data == 1'b0 && data_valid == 1'b1) begin
    match <= 1'b1;
    end
    else begin
    match <= 1'b0;
    end
    end
    end

    endmodule

信号发生器*

  • 请编写一个信号发生器模块,根据波形选择信号wave_choise发出相应的波形:wave_choice=0时,发出方波信号;wave_choice=1时,发出锯齿波信号;wave_choice=2时,发出三角波信号。

    image-20240917163116780
  • 注意square_wave_cnt < 'd101~10、11~19~0)与square_wave_cnt < 'd9 || square_wave_cnt == 'd190~9、9~19)的区别。正常来说都是50%占空比的方波,不知为何不加就会牛客网编译器就会报错

    • square_wave_cnt < ‘d10:

      image-20240917163750125

    • square_wave_cnt < ‘d9 || square_wave_cnt == ‘d19

      image-20240917163520227

  • 源代码

    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    `timescale 1ns / 1ps

    module signal_generator(
    input clk,
    input rst_n,
    input [1:0] wave_choise,
    output reg [4:0]wave
    );

    //方波计数器
    reg [4 : 0] square_wave_cnt;
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    square_wave_cnt <= 'd0;
    end
    else begin
    if(wave_choise == 'd0) begin
    if(square_wave_cnt == 'd19) begin
    square_wave_cnt <= 'd0;
    end
    else begin
    square_wave_cnt <= square_wave_cnt + 1;
    end
    end
    else begin
    square_wave_cnt <= 'd0;
    end
    end
    end

    //三角波控制器
    reg triangle_wave_flag; //其中flag=0代表下降,=1代表上升
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    triangle_wave_flag <= 1'b0;
    end
    else begin
    if(wave_choise == 'd2) begin
    if(wave == 'd1) begin
    triangle_wave_flag <= 1'b1;
    end
    else begin
    if(wave == 'd19) begin //其实此时是1~20的wave为上升,21~0的wave为下降
    triangle_wave_flag <= 1'b0;
    end
    end
    end
    else begin
    triangle_wave_flag <= 1'b0;
    end
    end
    end

    //输出选择控制
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    wave <= 'd0;
    end
    else begin
    case(wave_choise)
    2'd0: begin
    if(square_wave_cnt < 'd9 || square_wave_cnt == 'd19) begin
    wave <= 'd0;
    end
    else begin
    wave <= 'd20;
    end
    end
    2'd1: begin
    if(wave == 'd20) begin
    wave <= 'd0;
    end
    else begin
    wave <= wave + 1;
    end
    end
    2'd2: begin
    if(triangle_wave_flag == 1'b0) begin
    wave <= wave - 1;
    end
    else begin
    wave <= wave + 1;
    end
    end
    default: wave <= 'd0;
    endcase
    end
    end

    endmodule

数据串转并电路

  • 实现串并转换电路,输入端输入单bit数据,每当本模块接收到6个输入数据后,输出端输出拼接后的6bit数据。本模块输入端与上游的采用valid-ready双向握手机制,输出端与下游采用valid-only握手机制。数据拼接时先接收到的数据放到data_b的低位。电路的接口如下图所示。valid_a用来指示数据输入data_a的有效性,valid_b用来指示数据输出data_b的有效性;ready_a用来指示本模块是否准备好接收上游数据,本模块中一直拉高;clk是时钟信号;rst_n是异步复位信号。

    image-20240917172628400
  • 源代码:

    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
    72
    73
    74
    75
    76
    module s_to_p(
    input clk ,
    input rst_n ,
    input valid_a ,
    input data_a ,

    output reg ready_a ,
    output reg valid_b ,
    output reg [5:0] data_b
    );

    reg [5 : 0] data_out_r;

    reg [2 : 0] cnt;
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    cnt <= 'd0;
    end
    else begin
    if(valid_a && ready_a) begin
    if(cnt == 'd5) begin
    cnt <= 'd0;
    end
    else begin
    cnt <= cnt + 1;
    end
    end
    end
    end

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    data_out_r <= 'd0;
    end
    else begin
    if(valid_a) begin
    data_out_r <= {data_a, data_out_r[5 : 1]};
    end
    end
    end

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    data_b <= 'd0;
    end
    else begin
    if(cnt == 'd5) begin
    data_b <= {data_a, data_out_r[5 : 1]};
    end
    end
    end

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    ready_a <= 1'b0;
    end
    else begin
    ready_a <= 1'b1;
    end
    end

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    valid_b <= 1'b0;
    end
    else begin
    if(cnt == 'd5) begin
    valid_b <= 1'b1;
    end
    else begin
    valid_b <= 1'b0;
    end
    end
    end

    endmodule

数据累加输出***

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

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

    image-20240917220618909
  • 好好理解ready_a和ready_b信号对正常只有valid逻辑的影响,重点看代码里面的注释!

  • 源代码:

    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
    module valid_ready(
    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] cnt;
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    cnt <= 'd0;
    end
    else begin
    if(valid_a && ready_a) begin //与输出相关的信号时,都带上ready_a就行,因为当本级准备好时,才能正常工作
    if(cnt == 'd3) begin
    cnt <= 'd0;
    end
    else begin
    cnt <= cnt + 1;
    end
    end
    end
    end

    //数据输出
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    data_out <= 'd0;
    end
    else begin
    if(cnt == 'd0 && valid_a && ready_a && ready_b) begin //这里其实不太好理解,就是当cnt == 0的时候是要开启下一次计算(正常来说),重新加载第一个数据
    data_out <= data_in; //如果下一级准备好了,就正常加载,如果没有准备好,数据应该保持,
    end
    else if(valid_a && ready_a) begin //其实~ready_b成立时,ready_a == 0(根据ready_a的组合逻辑),那么就肯定是data_out保持了
    data_out <= data_out + data_in; //所以这里写法可以这样简化,没有单独写出~ready_b的情况
    end
    end
    end

    //数据有效信号
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    valid_b <= 1'b0;
    end
    else begin
    if(cnt == 'd3 && valid_a && ready_a) begin //这里很容易出错,注意当cnt=='d3时,可不一定是valid_a==1
    valid_b <= 1'b1;
    end
    else begin
    if(~ready_b) begin //可以等效成代码:else if(valid_b && ready_b)
    valid_b <= valid_b; // valid_b <= 1'd0;
    end //该等效代码的理解是:运算完成后需置高1clk,在下一个时钟周期就要归0了
    else begin //如果此时下一级准备好了,说明下一级已经读到数据运算有效信号了,就拉低valid_b(就跟没有这个ready信号的正常操作一样)
    valid_b <= 1'b0; //如果此时下一级还没有准备好,那么就要继续保持这个valid_b信号,直到下一级在准备好的时候能读到这个信号,知道数据已经运算有效了
    end
    end //这个我自己写的分支的理解是:运算有效后如果下一级模块还没有准备好(~ready),那么该valid_b信号就要继续保持(因为如果没有准备好,所有输出数据都是要保持的)
    end //如果准备好了,那就正常归0(因为正常情况下valid就只会拉高1clk)
    end

    //输出给上一级的ready
    assign ready_a = (valid_b && ~ready_b) ? 0 : 1; //数据有效且下一级未准备好时 输出给上一级的ready拉高

    endmodule

非整数倍数据位宽转换24to128*

  • 实现数据位宽转换电路,实现24bit数据输入转换为128bit数据输出。其中,先到的数据应置于输出的高bit位。电路的接口如下图所示。valid_in用来指示数据输入data_in的有效性,valid_out用来指示数据输出data_out的有效性;clk是时钟信号;rst_n是异步复位信号。

    image-20240918145814104
  • 这里的解题思路很巧妙,输入数据是24bit,输出数据是128bit。因为128×3=24×16128×3=24×16,所以每输入16个有效数据,就可以产生三个完整的输出因此设置一个仅在输入数据有效时工作的计数器cnt,计数范围是0-15。

    image-20240919150515791
  • 设置一个数据暂存器data_lock(128bit),每当输入有效时,将数据从低位移入。

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

  • 源代码:

    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
    72
    73
    74
    75
    module width_24to128(
    input clk ,
    input rst_n ,
    input valid_in ,
    input [23:0] data_in ,

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

    reg [3 : 0] cnt;
    reg [127 : 0] data_lock;

    //计数器逻辑
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    cnt <= 'd0;
    end
    else begin
    if(valid_in) begin
    if(cnt == 'd15) begin
    cnt <= 'd0;
    end
    else begin
    cnt <= cnt + 1;
    end
    end
    end
    end

    //移位拼接
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    data_lock <= 'd0;
    end
    else begin
    if(valid_in) begin
    data_lock <= {data_lock[103 : 0], data_in};
    end
    end
    end

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    data_out <= 'd0;
    end
    else begin
    if(cnt == 'd5 && valid_in) begin
    data_out <= {data_lock[119 : 0], data_in[23 : 16]}; //在输出这组数据最后一个数的同时,这个数将被存到data_lock中,以便下次使用
    end
    else if(cnt == 'd10 && valid_in) begin
    data_out <= {data_lock[111 : 0], data_in[23 : 8]};
    end
    else if(cnt == 'd15 && valid_in) begin
    data_out <= {data_lock[103 : 0], data_in[23 : 0]};
    end
    end
    end

    //输出有效信号
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    valid_out <= 1'b0;
    end
    else begin
    if(valid_in && (cnt == 'd5 || cnt == 'd10 || cnt == 'd15)) begin
    valid_out <= 1'b1;
    end
    else begin
    valid_out <= 1'b0;
    end
    end
    end

    endmodule

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

  • 实现数据位宽转换电路,实现8bit数据输入转换为12bit数据输出。其中,先到的数据应置于输出的高bit位。电路的接口如下图所示。valid_in用来指示数据输入data_in的有效性,valid_out用来指示数据输出data_out的有效性;clk是时钟信号;rst_n是异步复位信号。

    image-20240919151229197
  • 本题解法与上题一致

  • 源代码:

    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
    72
    73
    module width_8to12(
    input clk ,
    input rst_n ,
    input valid_in ,
    input [7:0] data_in ,

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

    //8 * 3 = 12 * 2
    reg [1 : 0] cnt;
    reg [11 : 0] data_lock;

    //计数器逻辑
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    cnt <= 'd0;
    end
    else begin
    if(valid_in) begin
    if(cnt == 'd2) begin
    cnt <= 0;
    end
    else begin
    cnt <= cnt + 1;
    end
    end
    end
    end

    //移位锁存
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    data_lock <= 'd0;
    end
    else begin
    if(valid_in) begin
    data_lock <= {data_lock[3 : 0], data_in};
    end
    end
    end

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    data_out <= 'd0;
    end
    else begin
    if(valid_in && cnt == 'd1) begin
    data_out <= {data_lock[7 : 0], data_in[7 : 4]};
    end
    else if(valid_in && cnt == 'd2) begin
    data_out <= {data_lock[3 : 0], data_in};
    end
    end
    end

    //数据有效信号
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    valid_out <= 1'b0;
    end
    else begin
    if(valid_in && (cnt == 'd1 || cnt == 'd2)) begin
    valid_out <= 1'b1;
    end
    else begin
    valid_out <= 1'b0;
    end
    end
    end

    endmodule

整数倍数据位宽转换8to16

  • 实现数据位宽转换电路,实现8bit数据输入转换为16bit数据输出。其中,先到的8bit数据应置于输出16bit的高8位。电路的接口如下图所示。valid_in用来指示数据输入data_in的有效性,valid_out用来指示数据输出data_out的有效性;clk是时钟信号;rst_n是异步复位信号。

    image-20240919154353458
  • 源代码:

    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
    module width_8to16_integer(
    input clk ,
    input rst_n ,
    input valid_in ,
    input [7:0] data_in ,

    output reg valid_out ,
    output reg [15:0] data_out
    );

    reg [15 : 0] data_lock;

    //8 * 2 = 16
    reg cnt;
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    cnt <= 'd0;
    end
    else begin
    if(valid_in) begin
    cnt <= ~cnt;
    end
    end
    end

    //移位输出
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    data_lock <='d0;
    end
    else begin
    if(valid_in) begin
    data_lock <= {data_lock[7 : 0], data_in};
    end
    end
    end

    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    data_out <= 'd0;
    end
    else begin
    if(valid_in && cnt == 'd1) begin
    data_out <= {data_lock[7 : 0], data_in};
    end
    end
    end

    //数据输出有效
    always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
    valid_out <= 1'b0;
    end
    else begin
    if(valid_in && cnt == 'd1) begin
    valid_out <= 1'b1;
    end
    else begin
    valid_out <= 1'b0;
    end
    end
    end

    endmodule

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

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

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

    2、寄存器输出且同步输出结果(注意rst为低电平复位

    image-20240920140736362
  • 源代码:

    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
    module sequence_test1(
    input wire clk ,
    input wire rst ,
    input wire data ,
    output reg flag
    );

    //状态定义
    localparam IDLE = 3'd0,
    S1 = 3'd1,
    S2 = 3'd2,
    S3 = 3'd3,
    S4 = 3'd4;

    //现态与次态定义
    reg [2 : 0] cur_state, next_state;

    //1.状态跳转
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    cur_state <= IDLE;
    end
    else begin
    cur_state <= next_state;
    end
    end

    //2.次态判断
    always @(*) begin
    case(cur_state)
    IDLE: next_state = data == 1 ? S1 : IDLE;
    S1: next_state = data == 0 ? S2 : S1;
    S2: next_state = data == 1 ? S3 : S2;
    S3: next_state = data == 1 ? S4 : S3;
    S4: next_state = data == 1 ? IDLE : S4;
    default: next_state = IDLE;
    endcase
    end

    //3.输出判断
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    flag <= 1'b0;
    end
    else begin
    if(cur_state == S4 && data == 1) begin
    flag <= 1'b1;
    end
    else begin
    flag <= 1'b0;
    end
    end
    end

    endmodule

状态机-重叠序列检测

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

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

    2、寄存器输出,在序列检测完成下一拍输出检测有效(注意rst为低电平复位

    image-20240920160548636
  • 源文件:

    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
    module sequence_test2(
    input wire clk ,
    input wire rst ,
    input wire data ,
    output reg flag
    );
    //*************code***********//
    //状态定义
    localparam IDLE = 3'd0,
    S1 = 3'd1,
    S2 = 3'd2,
    S3 = 3'd3,
    S4 = 3'd4;

    //现态与次态定义
    reg [2 : 0] cur_state, next_state;

    //1.状态跳转
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    cur_state <= IDLE;
    end
    else begin
    cur_state <= next_state;
    end
    end

    //2.次态判断
    always @(*) begin
    case(cur_state)
    IDLE: next_state = data == 1 ? S1 : IDLE;
    S1: next_state = data == 0 ? S2 : S1;
    S2: next_state = data == 1 ? S3 : S2;
    S3: next_state = data == 1 ? S4 : S3;
    S4: next_state = data == 1 ? S1 : S2;
    default: next_state = IDLE;
    endcase
    end

    //3.输出判断
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    flag <= 1'b0;
    end
    else begin
    if(cur_state == S4) begin
    flag <= 1'b1;
    end
    else begin
    flag <= 1'b0;
    end
    end
    end

    //*************code***********//
    endmodule

时钟分频(偶数)*

  • 请使用D触发器设计一个同时输出2/4/8分频的50%占空比的时钟分频。注意rst为低电平复位

    image-20240920162131517
  • 其实偶数分频是有些小技巧的,即利用计数器,不过之前已经会啦

  • 源代码:

    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 even_div
    (
    input wire rst ,
    input wire clk_in,
    output wire clk_out2,
    output wire clk_out4,
    output wire clk_out8
    );
    //*************code***********//
    reg [2 : 0] clk_reg;

    reg clk_out2_r;
    reg clk_out4_r;
    reg clk_out8_r;

    always @(posedge clk_in or negedge rst) begin
    if(~rst) begin
    clk_reg <= 'd0;
    end
    else begin
    clk_reg <= clk_reg + 1;
    end
    end

    always @(posedge clk_in or negedge rst) begin
    if(~rst) begin
    clk_out2_r <= 1'b1;
    clk_out4_r <= 1'b1;
    clk_out8_r <= 1'b1;
    end
    else begin
    clk_out2_r <= clk_reg[0];
    clk_out4_r <= clk_reg[1];
    clk_out8_r <= clk_reg[2];
    end
    end

    assign clk_out2 = ~clk_out2_r;
    assign clk_out4 = ~clk_out4_r;
    assign clk_out8 = ~clk_out8_r;
    //*************code***********//
    endmodule

自动贩售机1**

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

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

    (d1 0.5元;d2 1元;d3 2元;out1 饮料;out2 零钱)

    image-20240923142613137
  • 可以借助该题好好理解米粒(输出取决于当前状态+输入)和摩尔型(输出只取决于当前状态)的区别

    • 米粒型由于当前状态与输入有关,可以放在输出逻辑里面判断输出,从而减小状态机数量,但增加输出逻辑的复杂度
    • 摩尔型由于当前状态与输出无关,那么其实有很多状态只是为了指示输出,这无形中增加了状态机的数量
  • 米粒型版本:(状态仅表示空闲、累计0.5元、累计1元)

    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    module seller1(
    input wire clk ,
    input wire rst ,
    input wire d1 ,
    input wire d2 ,
    input wire d3 ,

    output reg out1,
    output reg [1:0]out2
    );

    //状态定义
    parameter IDLE = 'd0,
    S1 = 'd1, //0.5
    S2 = 'd2; //1

    //现态与次态定义
    reg [2 : 0] cur_state, next_state;

    //1.状态跳转
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    cur_state <= IDLE;
    end
    else begin
    cur_state <= next_state;
    end
    end

    //2.次态判断
    always @(*) begin
    //next_state = IDLE; 因为次态判断中存在next_state = next_state;这样的写法,那么就不能把该语句放在前面,不然每次判断后next_state都只会等于IDLE
    case(cur_state) //主要原因在于d1,d2,d3都只拉高半个时钟周期
    IDLE: begin
    case({d1,d2,d3})
    3'b100: next_state = S1;
    3'b010: next_state = S2;
    3'b001: next_state = IDLE;
    default: next_state = next_state;
    endcase
    end
    S1: begin
    case({d1,d2,d3})
    3'b100: next_state = S2;
    3'b010: next_state = IDLE;
    3'b001: next_state = IDLE;
    default: next_state = next_state;
    endcase
    end
    S2: begin
    case({d1,d2,d3})
    3'b100: next_state = IDLE;
    3'b010: next_state = IDLE;
    3'b001: next_state = IDLE;
    default: next_state = next_state;
    endcase
    end
    default: next_state = IDLE;
    endcase
    end

    //3.输出逻辑
    reg out1_r;
    reg [1 : 0] out2_r;

    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    out1_r <= 'd0;
    out2_r <= 'd0;
    end
    else begin
    case(cur_state)
    IDLE: begin
    if({d1,d2,d3} == 3'b001) begin //这里{d1,d2,d3}能被捕获到也是挺神奇的
    out1_r <= 1'b1;
    out2_r <= 'd1;
    end
    else begin
    out1_r <= 1'b0;
    out2_r <= 'd0;
    end
    end
    S1: begin
    case({d1,d2,d3})
    3'b010: begin
    out1_r <= 1'b1;
    out2_r <= 'd0;
    end
    3'b001: begin
    out1_r <= 1'b1;
    out2_r <= 'd2;
    end
    default: begin
    out1_r <= 1'b0;
    out2_r <= 'd0;
    end
    endcase
    end
    S2: begin
    case({d1,d2,d3})
    3'b100: begin
    out1_r <= 1'b1;
    out2_r <= 'd0;
    end
    3'b010: begin
    out1_r <= 1'b1;
    out2_r <= 'd1;
    end
    3'b001: begin
    out1_r <= 1'b1;
    out2_r <= 'd3;
    end
    default: begin
    out1_r <= 1'b0;
    out2_r <= 'd0;
    end
    endcase
    end
    default: begin
    out1_r <= 1'b0;
    out2_r <= 'd0;
    end
    endcase
    end
    end

    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    out1 <= 'd0;
    out2 <= 'd0;
    end
    else begin
    out1 <= out1_r;
    out2 <= out2_r;
    end
    end

    endmodule
  • 摩尔型版本(状态表示可能出现的所有累积钱数):

    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    module seller1(
    input wire clk ,
    input wire rst ,
    input wire d1 ,
    input wire d2 ,
    input wire d3 ,

    output reg out1,
    output reg [1:0]out2
    );

    //状态定义
    parameter IDLE = 'd0,
    S1 = 'd1, //0.5
    S2 = 'd2, //1
    S3 = 'd3, //1.5
    S4 = 'd4, //2
    S5 = 'd5, //2.5
    S6 = 'd6; //3

    //现态与次态定义
    reg [2 : 0] cur_state, next_state;

    //1.状态跳转
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    cur_state <= IDLE;
    end
    else begin
    cur_state <= next_state;
    end
    end

    //2.次态判断
    always @(*) begin
    //next_state = IDLE; 因为次态判断中存在next_state = next_state;这样的写法,那么就不能把该语句放在前面,不然每次判断后next_state都只会等于IDLE
    case(cur_state) //主要原因在于d1,d2,d3都只拉高半个时钟周期
    IDLE: begin
    case({d1,d2,d3})
    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({d1,d2,d3})
    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({d1,d2,d3})
    3'b100: next_state = S3;
    3'b010: next_state = S4;
    3'b001: next_state = S6;
    default: next_state = next_state;
    endcase
    end
    default: next_state = IDLE;
    endcase
    end

    //3.输出逻辑
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    out1 <= 'd0;
    end
    else begin
    if(next_state == S3 || next_state == S4 || next_state == S5 || next_state == S6) begin
    out1 <= 'd1;
    end
    else begin
    out1 <= 'd0;
    end
    end
    end
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    out2 <= 'd0;
    end
    else begin
    case(next_state)
    S4: out2 <= 'd1;
    S5: out2 <= 'd2;
    S6: out2 <= 'd3;
    default: out2 <= 'd0;
    endcase
    end
    end

    endmodule

自动贩售机2*

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

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

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

    (d1 0.5 d2 1 sel 选择饮料 out1 饮料1 out2 饮料2 out3 零钱)

    image-20240923162758494
  • 与上一题类似,次态判断时加上sel判断就行

  • 源代码:

    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    module seller2(
    input wire clk ,
    input wire rst ,
    input wire d1 ,
    input wire d2 ,
    input wire sel ,

    output reg out1,
    output reg out2,
    output reg out3
    );

    //状态定义
    localparam IDLE = 'd0,
    S0_5 = 'd1,
    S1 = 'd2,
    S1_5 = 'd3,
    S2 = 'd4;

    //现态与次态定义
    reg [2 : 0] cur_state, next_state;

    //1.状态跳转
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    cur_state <= IDLE;
    end
    else begin
    cur_state <= next_state;
    end
    end

    //2.次态判断
    always @(*) begin
    case(cur_state)
    IDLE: begin
    case({d1,d2})
    2'b10: next_state = S0_5;
    2'b01: next_state = S1;
    default: next_state = next_state;
    endcase
    end
    S0_5: begin
    case({d1,d2})
    2'b10: next_state = S1;
    2'b01: next_state = ~sel ? IDLE : S1_5;
    default: next_state = next_state;
    endcase
    end
    S1: begin
    case({d1,d2})
    2'b10: next_state = ~sel ? IDLE : S1_5;
    2'b01: next_state = ~sel ? IDLE : S2;
    default: next_state = next_state;
    endcase
    end
    S1_5: begin
    case({d1,d2})
    2'b10: next_state = S2;
    2'b01: next_state = IDLE;
    default: next_state = next_state;
    endcase
    end
    S2: begin
    case({d1,d2})
    2'b10: next_state = IDLE;
    2'b01: next_state = IDLE;
    default: next_state = next_state;
    endcase
    end
    default: begin
    next_state = IDLE;
    end
    endcase
    end

    //3.输出逻辑
    reg out1_r;
    reg out2_r;
    reg out3_r;
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    out1_r <= 'd0;
    out2_r <= 'd0;
    out3_r <= 'd0;
    end
    else begin
    case(cur_state)
    S0_5: begin
    case({d1,d2})
    2'b10: begin
    out1_r <= 'd0;
    out2_r <= 'd0;
    out3_r <= 'd0;
    end
    3'b01: begin
    out1_r <= ~sel ? 'd1 : 'd0;
    out2_r <= 'd0;
    out3_r <= 'd0;
    end
    default: begin
    out1_r <= 'd0;
    out2_r <= 'd0;
    out3_r <= 'd0;
    end
    endcase
    end
    S1: begin
    case({d1,d2})
    2'b10: begin
    out1_r <= ~sel ? 'd1 : 'd0;
    out2_r <= 'd0;
    out3_r <= 'd0;
    end
    3'b01: begin
    out1_r <= ~sel ? 'd1 : 'd0;
    out2_r <= 'd0;
    out3_r <= ~sel ? 'd1 : 'd0;
    end
    default: begin
    out1_r <= 'd0;
    out2_r <= 'd0;
    out3_r <= 'd0;
    end
    endcase
    end
    S1_5: begin
    case({d1,d2})
    2'b10: begin
    out1_r <= 'd0;
    out2_r <= 'd0;
    out3_r <= 'd0;
    end
    3'b01: begin
    out1_r <= 'd0;
    out2_r <= sel ? 'd1 : 'd0;
    out3_r <= 'd0;
    end
    default: begin
    out1_r <= 'd0;
    out2_r <= 'd0;
    out3_r <= 'd0;
    end
    endcase
    end
    S2: begin
    case({d1,d2})
    2'b10: begin
    out1_r <= 'd0;
    out2_r <= sel ? 'd1 : 'd0;
    out3_r <= 'd0;
    end
    3'b01: begin
    out1_r <= 'd0;
    out2_r <= sel ? 'd1 : 'd0;
    out3_r <= 'd1;
    end
    default: begin
    out1_r <= 'd0;
    out2_r <= 'd0;
    out3_r <= 'd0;
    end
    endcase
    end
    default: begin
    out1_r <= 'd0;
    out2_r <= 'd0;
    out3_r <= 'd0;
    end
    endcase
    end
    end

    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    out1 <= 'd0;
    out2 <= 'd0;
    out3 <= 'd0;
    end
    else begin
    out1 <= out1_r;
    out2 <= out2_r;
    out3 <= out3_r;
    end
    end

    endmodule

占空比50%的奇数分频*

  • 设计一个同时输出7分频的时钟分频器,占空比要求为50%。注意rst为低电平复位

    image-20240925142052058

  • 分别利用上升沿和下降沿对计数器进行判断,最后对结果取或

    image-20240925142232868

  • 源代码:

    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
    module odo_div_or(
    input wire rst ,
    input wire clk_in,
    output wire clk_out7
    );

    reg [2 : 0] cnt;

    always @(posedge clk_in or negedge rst) begin
    if(~rst) begin
    cnt <= 'd0;
    end
    else begin
    if(cnt == 7 - 1) begin
    cnt <= 'd0;
    end
    else begin
    cnt <= cnt + 1;
    end
    end
    end

    //上升沿判断
    reg clk_up;
    always @(posedge clk_in or negedge rst) begin
    if(~rst) begin
    clk_up <= 1'b0;
    end
    else begin
    if(cnt == 7 / 2) begin
    clk_up <= 1'b1;
    end
    else begin
    if(cnt == 6) begin
    clk_up <= 1'b0;
    end
    else begin
    clk_up <= clk_up;
    end
    end
    end
    end

    //下升沿判断
    reg clk_down;
    always @(negedge clk_in or negedge rst) begin
    if(~rst) begin
    clk_down <= 1'b0;
    end
    else begin
    if(cnt == 7 / 2) begin
    clk_down <= 1'b1;
    end
    else begin
    if(cnt == 6) begin
    clk_down <= 1'b0;
    end
    else begin
    clk_down <= clk_down;
    end
    end
    end
    end

    assign clk_out7 = clk_up | clk_down;

    endmodule

任意小数分频*

  • 请设计一个可以实现任意小数分频的时钟分频器,比如说8.7分频的时钟信号,注意rst为低电平复位

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

    image-20240925154025505
  • 然后采用若干种(一般是两种)整数分频在87个原周期clk_in内产生10个新时钟周期clk_out。整数分频的分频系数有很多种选择,但要尽可能接近,提高clk_out的均匀度。一般推荐在小数分频系数NN的附近选取。因为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分频,如下图。

    image-20240925154456206

  • 源代码:

    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
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    module div_M_N(
    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; //奇数周期

    //分频器内部计数器
    reg [3 : 0] cnt_in;

    //分频转换标志
    reg div_flag; //0表示偶数分配,1表示奇数分频

    //外部总计数器
    reg [6 : 0] cnt_out;

    //1.分频器内部计数器逻辑
    always @(posedge clk_in or negedge rst) begin
    if(~rst) begin
    cnt_in <= 'd0;
    end
    else begin
    if(~div_flag) begin
    if(cnt_in == div_e - 1) begin
    cnt_in <= 'd0;
    end
    else begin
    cnt_in <= cnt_in + 1;
    end
    end
    else begin
    if(cnt_in == div_o - 1) begin
    cnt_in <= 'd0;
    end
    else begin
    cnt_in <= cnt_in + 1;
    end
    end
    end
    end

    //2.外部计数器逻辑
    always @(posedge clk_in or negedge rst) begin
    if(~rst) begin
    cnt_out <= 'd0;
    end
    else begin
    if(cnt_out == M_N - 1) begin
    cnt_out <= 'd0;
    end
    else begin
    cnt_out <= cnt_out + 1;
    end
    end
    end

    //3.分频转化器逻辑
    always @(posedge clk_in or negedge rst) begin
    if(~rst) begin
    div_flag <= 'd0;
    end
    else begin
    if(cnt_out == M_N - 1 || cnt_out == c89 - 1) begin
    div_flag <= ~div_flag;
    end
    else begin
    div_flag <= div_flag;
    end
    end
    end

    //4.时钟输出
    reg clk_out_r;
    always @(posedge clk_in or negedge rst) begin
    if(~rst) begin
    clk_out_r <= 1'b0;
    end
    else begin
    // if(~div_flag) begin //这种是在先低后高的情况下
    // if(cnt_in == (div_e / 2 - 1 ) || cnt_in == div_e - 1) begin
    // clk_out_r <= ~clk_out_r;
    // end
    // end
    // else begin
    // if(cnt_in == div_o / 2 || cnt_in == div_o - 1) begin
    // clk_out_r <= ~clk_out_r;
    // end
    // end

    if(~div_flag) begin //题目要求是先高后低
    if(cnt_in <= (div_e / 2 - 1 )) begin
    clk_out_r <= 1'b1;
    end
    else begin
    clk_out_r <= 1'b0;
    end
    end
    else begin
    if(cnt_in <= div_o /4 + 1) begin
    clk_out_r <= 1'b1;
    end
    else begin
    clk_out_r <= 1'b0;
    end
    end
    end
    end
    assign clk_out = clk_out_r;

    endmodule

无占空比要去的奇数分频

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

  • 对占空比没有要求的话,仅在上升沿计数就好

  • 源代码:

    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
    module odd_div(
    input wire rst ,
    input wire clk_in,
    output wire clk_out5
    );

    //计数器
    reg [2 : 0] cnt;
    always @(posedge clk_in or negedge rst) begin
    if(~rst) begin
    cnt <= 'd0;
    end
    else begin
    if(cnt == 'd4) begin
    cnt <= 'd0;
    end
    else begin
    cnt <= cnt + 1;
    end
    end
    end

    //上升沿判断
    reg clk_up;
    always @(posedge clk_in or negedge rst) begin
    if(~rst) begin
    clk_up <= 1'b0;
    end
    else begin
    if(cnt == 2) begin
    clk_up <= 1'b0;
    end
    else if(cnt == 0) begin
    clk_up <= 1'b1;
    end
    end
    end

    assign clk_out5 = clk_up;

    endmodule

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

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

    要求:

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

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

    image-20240926132356818 image-20240926132428378
  • 源代码:

    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 fsm1(
    input wire clk ,
    input wire rst ,
    input wire data ,
    output reg flag
    );

    //状态定义
    parameter S0 = 2'd0,
    S1 = 2'd1,
    S2 = 2'd2,
    S3 = 2'd3;

    //现态与次态定义
    reg [1 : 0] cur_state, next_state;

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

    //2.次态判断
    always @(*) begin
    next_state = cur_state;
    case(cur_state)
    S0: next_state = data ? S1 : S0;
    S1: next_state = data ? S2 : S1;
    S2: next_state = data ? S3 : S2;
    S3: next_state = data ? S0 : S3;
    endcase
    end

    //3.输出判断
    always @(posedge clk or negedge rst) begin
    if(~rst) begin
    flag <= 1'b0;
    end
    else begin
    if(cur_state == S3 && data == 1'b1) begin
    flag <= 1'b1;
    end
    else begin
    flag <= 1'b0;
    end
    end
    end

    endmodule

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

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

    要求:

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

    2、 使用二段式描述方法

    image-20240926133500919 image-20240926133518169
  • 源代码:

    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
    module fsm2(
    input wire clk ,
    input wire rst ,
    input wire data ,
    output reg flag
    );

    //状态定义
    parameter S0 = 3'd0,
    S1 = 3'd1,
    S2 = 3'd2,
    S3 = 3'd3,
    S4 = 3'd4;

    //现态与次态定义
    reg [2 : 0] cur_state, next_state;

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

    //2.次态判断与输出判断
    always @(*) begin
    next_state = cur_state;
    flag = 1'b0;
    case(cur_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
    endcase
    end

    endmodule

欢迎来到ssy的世界