为了准备笔试及面试可能遇到的常见题型,特地来记录一下。

1. 跨时钟域处理

单bit跨时钟域

快到慢

  • 脉冲检测:

    • PS:需要进行脉冲展宽,要求快时钟域两个脉冲间有一定的时间间隔.
module f2s_1bit(
        input             rstn,
        input             clk_fast,
        input             vld_i,
        input             clk_slow,
        output             vld_o    
    );
reg     fast_flag;
reg     dly1,dly2,dly3;
wire     edge_flag;

always@(posedge clk_fast or negedge rstn)begin
    if(!rstn)
        fast_flag <= 1'b0;
    else if(vld_i)
        fast_flag <= ~fast_flag;
end
always@(posedge clk_slow or negedge rstn)begin
    if(!rstn)begin
        dly1 <= 1'b0;
        dly2 <= 1'b0;
        dly3 <= 1'b0;
    end
    else begin
          dly1 <= fast_flag;
        dly2 <= dly1;
        dly3 <= dly2;
    end
end

assign edge_flag = dly2^dly3;  
assign vld_o = edge_flag;
endmodule
  • 电平同步:

慢到快

直接打两拍进行。

多bit跨时钟

  • 采用DMUX技术进行多bit化为单bit,进行跨时钟域处理。
  • 采用异步FIFO方式进行同步。
  • 握手信号进行同步。

同步FIFO

  • 结构:读写地址指针,mem,空满信号判断
module sfifo#(
        parameter   DATA_WIDTH = 8,
        parameter   DEPTH      = 8
    )(
input                       rstn,
input                       clk,        
input                       wr_en,
input           [DATA_WIDTH-1:0]    din,
input                       rd_en,
output          [DATA_WIDTH-1:0]    dout,
output  reg                     full,
output  reg                 empty
);

localparam  ADDR_WIDTH = log2(DEPTH);

wire                    wr_vld,rd_vld;
reg  [ADDR_WIDTH-1:0]   wr_ptr_next,wr_ptr,rd_ptr_next,rd_ptr;
reg  [ADDR_WIDTH-1:0]   cnt;//计数器模式

//读写使能
assign wr_vld = wr_en & (~full);
assign rd_vld = rd_en & (~empty);
//写指针
always@* begin
    if(wr_vld)
        wr_ptr_next = wr_ptr + 1'b1;
    else 
        wr_ptr_next = wr_ptr;
end
always @(posedge clk or negedge rstn) begin
    if (rstn == 1'b0)
        wr_ptr <= {ADDR_WIDTH{1'b0}};
    else
        wr_ptr <= wr_ptr_next;
end
//读指针
always@* begin
    if(rd_vld)
        rd_ptr_next = rd_ptr + 1'b1;
    else 
        rd_ptr_next = rd_ptr;
end
always @(posedge clk or negedge rstn) begin
    if (rstn == 1'b0)
        rd_ptr <= {ADDR_WIDTH{1'b0}};
    else
        rd_ptr <= rd_ptr_next;
end
//空满判断方式1:空满判断计数器//该种方式随着后续深度的增加,比较器的效率降低,会导致频率降低
always@(posedge clk or negedge rstn)begin
    if(!rstn)
        cnt <= {DEPTH{1'b0}};
    else 
        case ({wr_vld,rd_vld})
            2'b10:
                cnt <= cnt + 1'b1;
            2'b01:
                cnt <= cnt - 1'b1;
            default:;
        endcase
end
//空判断
assign empty = cnt=={DEPTH{1'b0}};
//满判断
assign full = cnt==DEPTH;
//ram例化即mem
ram #(
        .DATA_WIDTH(DATA_WIDTH),
        .ADDR_WIDTH(ADDR_WIDTH)
    ) inst_ram (
        .rstn    (rstn),
        .clk     (clk),
        .wr_vld  (wr_vld),
        .wr_addr (wr_ptr[ADDR_WIDTH-1:0]),
        .din     (din),
        .rd_vld  (rd_vld),
        .rd_addr (rd_ptr[ADDR_WIDTH-1:0]),
        .dout    (dout)
    );

function integer log2; //实现log2计算,用以计算深度对应的位宽
input  integer din;
begin
   din = din-1;
   for(log2=0;din>0;log2=log2+1) 
        din = din >> 1;
end
endfunction
endmodule
  • 空满判断方式二rtl
//空满判断方式2
always@(posedge clk or negedge rstn)begin
    if(!rstn)
        wr_ptr <= {ADDR_WIDTH{1'b0}};
    else if(wr_vld)
        wr_ptr <= wr_ptr + 1'b1;
end
always@(posedge clk or negedge rstn)begin
    if(!rstn)
        rd_ptr <= {ADDR_WIDTH{1'b0}};
    else if(rd_vld)
        rd_ptr <= wr_ptr + 1'b1;
end
assign empty = rd_ptr == wr_ptr;
assign full = {~rd_ptr[ADDR_WIDTH],rd_ptr[ADDR_WIDTH-1:0]} == wr_ptr;

异步FIFO

  • 结构:读写地址指针,mem,格雷码跨时钟域处理,空满信号判断
最后修改:2022 年 09 月 25 日
嘿嘿嘿