Verilog经典题

本文探讨了时钟域间的信号同步,包括从快时钟到慢时钟的单脉冲检测,使用握手信号的脉冲同步以避免丢失,以及异步复位同步释放技术。此外,还介绍了小数分频的实现方法,并通过一个售货机状态机的例子展示了状态机在实际应用中的设计。这些内容涵盖了数字系统设计中的关键概念和技术。
摘要由CSDN通过智能技术生成


1.快时钟到慢时钟 检测单时钟脉冲信号

sig_a 是 clka(300M)时钟域的一个单时钟脉冲信号(高电平持续一个时钟clka周期),请设计脉冲同步电路,将sig_a信号同步到时钟域 clkb(100M)中,产生sig_b单时钟脉冲信号(高电平持续一个时钟clkb周期)输出。请用 Verilog 代码描述。
clka时钟域脉冲之间的间隔很大,无需考虑脉冲间隔太小的问题
在这里插入图片描述
不考虑间隔的问题,将源时钟域的脉冲信号转换成电平信号。对同步的信号判断,上升沿。

  1 //
  2 module pulse_detect(
  3   output sig_b,
  4   input  clka, //fast 300M
  5   input  clkb, //slow 100M
  6   input  rst_n,
  7   input  sig_a);
  8 
  9 reg [2:0] sig_b_reg;
 10 reg sig_a_reg;
 11 
 12 always@(posedge clka or negedge rst_n) begin
 13   if(!rst_n)
 14     sig_a_reg <= 0;
 15   else if(sig_a)
 16     sig_a_reg <= ~sig_a_reg;
 17 end
 18 
 19 always@(posedge clkb or negedge rst_n) begin
 20   if(!rst_n)
 21     sig_b_reg <= 3'b000;
 22   else
 23     sig_b_reg <= {sig_b_reg[1:0],sig_a_reg};
 24 end
 25 
 26 assign sig_b = ^sig_b_reg[2:1];
 27 endmodule

tb:

  1 //`timescale 100ps/100ps
  2 module tb;
  3 
  4 reg clka,clkb,rst_n,sig_a;
  5 wire sig_b;
  6 
  7 pulse_detect dut(.clka(clka),
  8                  .clkb(clkb),
  9                  .rst_n(rst_n),
 10                  .sig_a(sig_a),
 11                  .sig_b(sig_b));
 12 
 13 initial begin
 14   clka = 0;
 15   forever begin
 16     #1.6ns clka = ~clka;
 17   end
 18 end
 19 initial begin
 20   clkb = 0;
 21   forever begin
 22     #5ns clkb = ~clkb;
 23   end
 24 end
 25 
 26 initial begin
 27   rst_n = 0;
 28   sig_a = 0;
 29 
 30   #10ns
 31   rst_n = 1;
 32   sig_a = 1;
 33   #3.2ns
 34   sig_a = 0;
 35   #200ns
 36   $stop();
 37 end
 38 
 39 initial begin
 40  // $fsdbDumpfile("pulse_detect.fsdb");
 41  // $fsdbDumpvars;
 42  $vcdpluson;
 43 end
 44 
 45 
 46 
 47 
 48 endmodule : tb
~                                                                                                                                                                                                                                                             
~                             

波形
在这里插入图片描述

1.2 带有握手信号的脉冲同步

如果两个脉冲的间隔短,会丢失信号。需要使用握手信号确保。[refer]
在这里插入图片描述

module HANDSHAKE_PULSE_SYNC
 (
 src_clk , //source clock 
 src_rst_n , //source clock reset (0: reset)
 src_pulse , //source clock pulse in
 src_sync_fail , //source clock sync state: 1 clock pulse if sync fail.
 dst_clk , //destination clock 
 dst_rst_n , //destination clock reset (0:reset)
 dst_pulse //destination pulse out
 );
//PARA DECLARATION
//INPUT DECLARATION
input src_clk ; //source clock 
input src_rst_n ; //source clock reset (0: reset)
input src_pulse ; //source clock pulse in
input dst_clk ; //destination clock 
input dst_rst_n ; //destination clock reset (0:reset)
//OUTPUT DECLARATION
output src_sync_fail ; //source clock sync state: 1 clock pulse if sync fail.
output dst_pulse ; //destination pulse out
//INTER DECLARATION
wire dst_pulse ;
wire src_sync_idle ;
reg src_sync_fail ;
reg src_sync_req ;
reg src_sync_ack ;
reg ack_state_dly1 ;
reg ack_state_dly2 ;
reg req_state_dly1 ;
reg req_state_dly2 ;
reg dst_req_state ;
reg dst_sync_ack ;
//--========================MODULE SOURCE CODE==========================--
//--=========================================--
// DST Clock :
// 1. generate src_sync_fail; 
// 2. generate sync req 
// 3. sync dst_sync_ack
//--=========================================--
assign src_sync_idle = ~(src_sync_req | src_sync_ack );
//report an error if src_pulse when sync busy ;
always @(posedge src_clk or negedge src_rst_n)
begin
 if(src_rst_n == 1'b0)
 src_sync_fail <= 1'b0 ;
 else if (src_pulse & (~src_sync_idle)) 
 src_sync_fail <= 1'b1 ;
 else
 src_sync_fail <= 1'b0 ;
end
//set sync req if src_pulse when sync idle ;
always @(posedge src_clk or negedge src_rst_n)
begin
 if(src_rst_n == 1'b0)
 src_sync_req <= 1'b0 ;
 else if (src_pulse & src_sync_idle) 
 src_sync_req <= 1'b1 ;
 else if (src_sync_ack)
 src_sync_req <= 1'b0 ;
end
always @(posedge src_clk or negedge src_rst_n)
begin
 if(src_rst_n == 1'b0)
 begin
 ack_state_dly1 <= 1'b0 ;
 ack_state_dly2 <= 1'b0 ;
 src_sync_ack <= 1'b0 ; 
 end
 else
 begin
 ack_state_dly1 <= dst_sync_ack ;
 ack_state_dly2 <= ack_state_dly1 ;
 src_sync_ack <= ack_state_dly2 ; 
 end 
end
//--=========================================--
// DST Clock :
// 1. sync src sync req 
// 2. generate dst pulse
// 3. generate sync ack
//--=========================================--
always @(posedge dst_clk or negedge dst_rst_n)
begin
 if(dst_rst_n == 1'b0)
 begin
 req_state_dly1 <= 1'b0 ;
 req_state_dly2 <= 1'b0 ;
 dst_req_state <= 1'b0 ;
 end
 else
 begin
 req_state_dly1 <= src_sync_req ;
 req_state_dly2 <= req_state_dly1 ;
 dst_req_state <= req_state_dly2 ;
 end
end
//Rising Edge of dst_state generate a dst_pulse;
assign dst_pulse = (~dst_req_state) & req_state_dly2 ; 
//set sync ack when src_req = 1 , clear it when src_req = 0 ;
always @(posedge dst_clk or negedge dst_rst_n)
begin
 if(dst_rst_n == 1'b0)
 dst_sync_ack <= 1'b0;
 else if (req_state_dly2) 
 dst_sync_ack <= 1'b1;
 else 
 dst_sync_ack <= 1'b0;
end
endmodule

在这里插入图片描述

2. 异步复位同步释放

使用异步复位同步释放来将输入数据a存储到寄存器中
在这里插入图片描述

module reset_release(
input clk,
input rst_n,
input d,
output reg dout
 );
  reg [1:0] rst_reg;
  //two level reg to syn rst_n=1
  always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
      rst_reg <= 2'b0;
    else
      rst_reg <= {rst_reg[0],1'b1};
  end
   
  always@(posedge clk or negedge rst_reg[1]) begin
    if(!rst_reg[1])
      dout <= 1'b0;
    else
      dout <= d;
  end
   
endmodule
module tb;
reg  clk,rst_n,d;
wire dout;
 	 reset_release dut(.clk(clk),
         .rst_n(rst_n),
            .d(d),
            .dout(dout));
   
     initial begin
      clk = 0;
      forever begin
        #5
        clk = ~clk;
      end
    end
  
    initial begin
      rst_n = 0;
      #8
      rst_n = 1;
      #200
      $stop();
    end
  
    initial begin
      d = 0;
      #10 d = 1;
      #10 d = 0;
      #15 d = 1;
    end
  endmodule : tb

在这里插入图片描述

3.握手信号跨时钟域

在这里插入图片描述

module data_driver(
    input clk_a,
    input rst_n,
    input data_ack,
    output reg [3:0]data,
    output reg data_req
    );
    reg [2:0] ack_reg;
    reg [2:0] count;
    //跨时钟域,此处打三拍避免亚稳态
    always@(posedge clk_a or negedge rst_n) begin
        if(!rst_n)
          ack_reg <= 3'b0;
        else
          ack_reg <= {ack_reg[1:0],data_ack};
        end
    //ack上升沿
    assign ack_posedge = !ack_reg[2] && ack_reg[1];
 
    always@(posedge clk_a or negedge rst_n) begin
        if(!rst_n)
            count <= 3'b0;
        else if(ack_posedge)
            count <= 3'b0;
        else
            count <= count + 1'b1;
    end
 
    always@(posedge clk_a or negedge rst_n) begin
        if(!rst_n) begin
            data <= 3'b000;
            data_req <= 1'b0;
        end
        else if(count == 3'b100)
            data_req <= 1'b1;
        else if(ack_posedge) begin
            data_req <= 1'b0;
            data <= data + 1'b1;
        end
    end
endmodule
 
module data_receiver(
    input clk_b,
    input rst_n,
    input data_req,
    input [3:0] data,
    output reg data_ack
 
);
    reg [3:0] data_reg;
    reg [2:0] req_reg;
    always@(posedge clk_b or negedge rst_n) begin
        if(!rst_n)
          req_reg <= 3'b0;
        else
          req_reg <= {req_reg[1:0],data_req};
        end
    //req上升沿
    assign req_posedge = !req_reg[2] && req_reg[1];
 
    always@(posedge clk_b or negedge rst_n) begin
        if(!rst_n) begin
            data_ack <= 1'b0;
        end
        else if(req_reg[1]) begin
            data_ack <= 1'b1;
        else
            data_ack <= 1'b0;
        end
    end
 
    always@(posedge clk_b or negedge rst_n) begin
        if(!rst_n) begin
            data_reg <= 4'b0;
        end
        else if(req_posedge) begin
            data_reg <= data;
        end
    end  
endmodule

tb:

reg clk_a,clk_b;
reg rst_n;
 
data_driver dut_drv(.clk_a(clk_a),
                    .rst_n(rst_n),
                    .data_ack(data_ack),
                    .data(data),
                    .data_req(data_req));
data_receiver dut_rec(.clk_b(clk_b),
                      .rst_n(rst_n),
                      .data_ack(data_ack),
                      .data(data),
                      .data_req(data_req));
 
initial begin
  clk_a = 0;
  forever begin
  #5ns clk_a = ~clk_a;
  end
end
 
initial begin
  clk_b = 0;
  forever begin
  #10ns clk_b = ~clk_b;
  end
end
 
initial begin
  rst_n = 0;
  #20ns
  rst_n = 1;
  #1us
  $stop();
end
endmodule : tb

在这里插入图片描述

4.小数分频

牛客
描述

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

注意rst为低电平复位

提示:
其实本质上是一个简单的数学问题,即如何使用最小公倍数得到时钟周期的分别频比。
设小数为nn,此处以8.7倍分频的时钟周期为例。
首先,由于不能在硬件上进行小数的运算(比如2.1个时钟这种是不现实的,也不存在3.3个寄存器),小数分频不能做到分频后每个时钟周期都是源时钟的nn倍,也无法实现占空比为1/2,因此,考虑小数分频,其实现方式应当为53个clkout时钟周期是10个clkin时钟周期的8.7倍。
信号示意图:
在这里插入图片描述
8.7分频,即周期扩大8.7倍。原来87个周期,现在10个周期。
则 x+y = 10
Mx+Ny = 87
题目所给M = 8 N = 9 ,所以可以由三个8分频的时钟和七个9分频的时钟组合而成。
设置一个div_flag,在计数27 87翻转,完成时钟的切换。


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; //奇数周期
//*************code***********//
reg div_flag;
reg [6:0] clk_cnt;
reg [3:0] cnt;
reg clk_out_r;
always@(posedge clk_in or negedge rst) begin
    if(!rst) 
        div_flag <= 'b0;
    else 
        div_flag <= (clk_cnt== c89-1 || clk_cnt==M_N-1)? ~div_flag : div_flag;
end
always@(posedge clk_in or negedge rst) begin
    if(!rst)
        cnt <= 'b0;
    else if(!div_flag)
        cnt <= (cnt == div_e - 1?  0 : cnt + 1);
    else 
        cnt <= (cnt == div_o - 1?  0 : cnt + 1);
end


always@(posedge clk_in or negedge rst) begin
    if(!rst)
        clk_cnt <= 'b0;
    else 
        clk_cnt <= (clk_cnt == M_N-1) ? 'b0 : clk_cnt+1;
end
always@(posedge clk_in or negedge rst) begin
    if(!rst)
        clk_out_r <= 'b0;
    else if(!div_flag)
        clk_out_r <= cnt<=(div_e>>1-1);
    else 
        clk_out_r <= cnt<=(div_o>>1-1);
end
assign clk_out = clk_out_r;
//*************code***********//
endmodule

在这里插入图片描述

5 状态机

5.1 售货机

牛客
描述
请设计状态机电路,实现自动售卖机功能,A饮料5元钱,B饮料10元钱,售卖机可接收投币5元钱和10元钱,每次投币只可买一种饮料,考虑找零的情况。

电路的接口如下图所示。sel信号会先于din信号有效,且在购买一种饮料时值不变。

sel为选择信号,用来选择购买饮料的种类,sel=0,表示购买A饮料,sel=1,表示购买B饮料;
din表示投币输入,din=0表示未投币,din=1表示投币5元,din=2表示投币10元,不会出现din=3的情况;
drinks_out表示饮料输出,drinks_out=0表示没有饮料输出,drinks_out=1表示输出A饮料,drinks_out=2表示输出B饮料,不出现drinks_out =3的情况,输出有效仅保持一个时钟周期;
change_out表示找零输出,change_out=0表示没有找零,change_out=1表示找零5元,输出有效仅保持一个时钟周期。
接口电路图如下:
在这里插入图片描述

使用Mealy型描述,所需要的状态数目会少很多,相对本题来说。

`timescale 1ns/1ns

module sale(
   input                clk   ,
   input                rst_n ,
   input                sel   ,//sel=0,5$dranks,sel=1,10&=$drinks
   input          [1:0] din   ,//din=1,input 5$,din=2,input 10$
 
   output   reg  [1:0] drinks_out,//drinks_out=1,output 5$ drinks,drinks_out=2,output 10$ drinks
   output	reg        change_out   
);
parameter IDLE = 'd0;
parameter S0   = 'd1;
reg n_state,c_state;
wire [2:0] inp;
assign inp = {sel,din};
always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        c_state <= IDLE;
    else 
        c_state <= n_state;
end
always@(*)
case (c_state)
    IDLE : n_state = (inp=='b101) ? S0 : IDLE;
    S0   : n_state = (din=='b0) ? S0 : IDLE;
    default:n_state = IDLE;
endcase 

always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
        {drinks_out,change_out} <= 'b00;
    else begin
        if(c_state== S0) begin
            if(inp=='b101)
                {drinks_out,change_out} <= 'b100;
            else if(inp == 'b110) 
                {drinks_out,change_out} <= 'b101;
            else 
                {drinks_out,change_out} <= 'b0;
        end 
        else begin
            if(inp=='b001)
                {drinks_out,change_out} <= 'b010;
            else if(inp == 'b010)
                {drinks_out,change_out} <= 'b011;
            else if(inp == 'b110)
                {drinks_out,change_out} <= 'b100;
            else 
                {drinks_out,change_out} <= 'b0;
        end
    end

    end

endmodule
  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值