PCIE驱动学习之8 对几个基本的verilog模块的分析

这里开始分析一下VIVADO项目中的几个VERILOG模块:

1,频率估计模块

//
// Copyright (c) 2016-2020 Fairwaves, Inc.
// SPDX-License-Identifier: CERN-OHL-W-2.0
//

module clk_estimator #(
    parameter EST_BITS = 20
)(
    input         rst,
    input         clk,

    input         meas_clk,

    input         cntr_ready,
    output        cntr_valid,
    output [31:0] cntr_data
);


reg [EST_BITS-1:0] div_clk;
always @(posedge clk) begin
  if (rst) begin
    div_clk <= 0;
  end else begin
    div_clk <= div_clk + 1'b1;
  end
end

wire out_dclk;
sync_reg  self_estim(
    .clk(meas_clk),
    .rst(rst),
    .in(div_clk[EST_BITS-1]),
    .out(out_dclk)
);

reg [EST_BITS-1:0] cntr_clk;
reg [EST_BITS-1:0] ref_cntr_data_r;
reg [3:0]  evnts;
reg prev_out_dclk;

always @(posedge meas_clk) begin
  if (rst) begin
    cntr_clk        <= 0;
    prev_out_dclk   <= 0;
    ref_cntr_data_r <= 0;
    evnts           <= 0;
  end else begin
    prev_out_dclk   <= out_dclk;

    if (prev_out_dclk == 0 && out_dclk == 1'b1) begin
      cntr_clk        <= 0;
      ref_cntr_data_r <= cntr_clk;
      evnts           <= evnts + 1'b1;
    end else begin
      cntr_clk        <= cntr_clk + 1'b1;
    end
  end
end

// Self clock estimation
assign cntr_valid = 1'b1;
assign cntr_data = { evnts, {(28 - EST_BITS){1'b0}}, ref_cntr_data_r };



endmodule

这里是根据用户频率大约估算另外一个频率。比如我们有100M的用户接口频率,还有RF芯片产生的sample rare clk. 这时候可以使用这个模块进行大体估算。

2,一位同步模块

//
// Copyright (c) 2016-2020 Fairwaves, Inc.
// SPDX-License-Identifier: CERN-OHL-W-2.0
//

module sync_reg #(
   parameter INIT         = 0,
   parameter ASYNC_RESET  = 0
) (
   input  clk,
   input  rst,
   input  in,
   output out

);

(* ASYNC_REG = "TRUE" *) reg sync1;
(* ASYNC_REG = "TRUE" *) reg sync2;

assign out = sync2;

generate
if (ASYNC_RESET) begin
  always @(posedge clk or posedge rst) begin
    if (rst) begin
      sync1 <= INIT;
      sync2 <= INIT;
    end else begin
      sync1 <= in;
      sync2 <= sync1;
    end
  end
end else begin
  always @(posedge clk) begin
    if (rst) begin
      sync1 <= INIT;
      sync2 <= INIT;
    end else begin
      sync1 <= in;
      sync2 <= sync1;
    end
  end
end
endgenerate

endmodule

简单说就是用新的时钟去打两个拍子,这里主要是要看有(* ASYNC_REG = "TRUE" *) 这个约束。有了这个约束相当月时钟路径分析设置为false.

3,穿越时钟域的计数器

//
// Copyright (c) 2016-2020 Fairwaves, Inc.
// SPDX-License-Identifier: CERN-OHL-W-2.0
//

module cross_counter #(
   parameter WIDTH = 8,
   parameter GRAY_BITS = WIDTH,
   parameter OUT_WIDTH = WIDTH,
   parameter OUT_LOWER_SKIP = 0,
   parameter OUT_RESET_ASYNC = 0,
   parameter OUT_PIPELINED = 0
)(
   input                                 inrst,
   input                                 inclk,
   input                                 incmdvalid,
   input                                 incmdinc,
   output [WIDTH - 1:0]                  incnt,

   input                                 outclk,
   input                                 outrst,
   output [OUT_WIDTH - 1:OUT_LOWER_SKIP] outcnt
);

genvar i;

reg [WIDTH - 1:0] counter;

reg  [GRAY_BITS - 1:OUT_LOWER_SKIP] gray_encoded;
wire [GRAY_BITS - 1:OUT_LOWER_SKIP] sync_out;

wire [GRAY_BITS - 1:OUT_LOWER_SKIP] gcode =
	counter[GRAY_BITS - 1:OUT_LOWER_SKIP] ^ counter[GRAY_BITS - 1:OUT_LOWER_SKIP+1];


always @(posedge inclk) begin
  if (inrst) begin
    counter      <= 0;
    gray_encoded <= 0;
  end else begin
    if (incmdvalid) begin
      if (incmdinc) begin
        counter <= counter + 1;
      end else begin
        counter <= counter - 1;
      end
    end
    gray_encoded <= gcode;
  end
end

assign incnt = counter;


generate
  for (i = OUT_LOWER_SKIP; i < GRAY_BITS; i=i+1) begin: forreg
    sync_reg #(.ASYNC_RESET(OUT_RESET_ASYNC)) sreg (
      .clk(outclk),
      .rst(outrst),
      .in(gray_encoded[i]),
      .out(sync_out[i])
    );
  end
endgenerate


wire [GRAY_BITS - 1:OUT_LOWER_SKIP] outgray;
assign outgray[GRAY_BITS - 1] = sync_out[GRAY_BITS - 1];

generate
for (i = GRAY_BITS - 1; i > OUT_LOWER_SKIP; i=i-1) begin
  assign outgray[i - 1] = outgray[i] ^ sync_out[i - 1];
end
endgenerate



generate
if (OUT_PIPELINED || OUT_WIDTH != GRAY_BITS) begin
  reg [OUT_WIDTH - 1:OUT_LOWER_SKIP] oval;
  assign outcnt = oval;

  if (OUT_RESET_ASYNC) begin
    always @(posedge outclk or posedge outrst) begin
      if (outrst) begin
        oval[GRAY_BITS - 1:OUT_LOWER_SKIP] <= 0;
      end else begin
        oval[GRAY_BITS - 1:OUT_LOWER_SKIP] <= outgray[GRAY_BITS - 1:OUT_LOWER_SKIP];
      end
    end
  end else begin
    always @(posedge outclk) begin
      if (outrst) begin
        oval[GRAY_BITS - 1:OUT_LOWER_SKIP] <= 0;
      end else begin
        oval[GRAY_BITS - 1:OUT_LOWER_SKIP] <= outgray[GRAY_BITS - 1:OUT_LOWER_SKIP];
      end
    end
  end

  if (OUT_WIDTH != GRAY_BITS) begin
    wire wrap_pos = (oval[GRAY_BITS - 1:OUT_LOWER_SKIP] > outgray[GRAY_BITS - 1:OUT_LOWER_SKIP]);

    if (OUT_RESET_ASYNC) begin
      always @(posedge outclk or posedge outrst) begin
        if (outrst) begin
          oval[OUT_WIDTH - 1:GRAY_BITS]      <= 0;
        end else begin
          oval[OUT_WIDTH - 1:GRAY_BITS]      <= oval[OUT_WIDTH - 1:GRAY_BITS] + wrap_pos;
        end
      end
    end else begin
      always @(posedge outclk) begin
        if (outrst) begin
          oval[OUT_WIDTH - 1:GRAY_BITS]      <= 0;
        end else begin
          oval[OUT_WIDTH - 1:GRAY_BITS]      <= oval[OUT_WIDTH - 1:GRAY_BITS] + wrap_pos;
        end
      end
    end
  end
end else begin
  assign outcnt = outgray[OUT_WIDTH - 1:OUT_LOWER_SKIP];
end
endgenerate


endmodule

这里分两个时钟域去看,第一个时钟域看到就是一个可以加可以减的计数器。第二个时钟域的输出就是第一个时钟域计数器的输出,这个输出保证了使用格雷码进行了时钟域的穿越。由于使用格雷码,数据一周期最多变化一位,编译软件分析到了后也不会按照穿越时钟域来报路径。

4,aixs接口的多路器.

//
// Copyright (c) 2016-2020 Fairwaves, Inc.
// SPDX-License-Identifier: CERN-OHL-W-2.0
//

module axis_mux4(
    input         s_axis_clk,
    input         s_arstn,

    input         m_axis_tready,
    output [63:0] m_axis_tdata,
    output [7:0]  m_axis_tkeep,
    output        m_axis_tlast,
    output        m_axis_tvalid,

    output        s0_axis_tready,
    input  [63:0] s0_axis_tdata,
    input  [7:0]  s0_axis_tkeep,
    input         s0_axis_tlast,
    input         s0_axis_tvalid,

    output        s1_axis_tready,
    input  [63:0] s1_axis_tdata,
    input  [7:0]  s1_axis_tkeep,
    input         s1_axis_tlast,
    input         s1_axis_tvalid,

    output        s2_axis_tready,
    input  [63:0] s2_axis_tdata,
    input  [7:0]  s2_axis_tkeep,
    input         s2_axis_tlast,
    input         s2_axis_tvalid,

    output        s3_axis_tready,
    input  [63:0] s3_axis_tdata,
    input  [7:0]  s3_axis_tkeep,
    input         s3_axis_tlast,
    input         s3_axis_tvalid
);


localparam MUX_S0   = 2'h0;
localparam MUX_S1   = 2'h1;
localparam MUX_S2   = 2'h2;
localparam MUX_S3   = 2'h3;

reg [1:0] state;

assign m_axis_tdata = (state == MUX_S0) ? s0_axis_tdata :
                      (state == MUX_S1) ? s1_axis_tdata :
                      (state == MUX_S2) ? s2_axis_tdata :
                    /*(state == MUX_S3) ?*/ s3_axis_tdata;

assign m_axis_tkeep = (state == MUX_S0) ? s0_axis_tkeep :
                      (state == MUX_S1) ? s1_axis_tkeep :
                      (state == MUX_S2) ? s2_axis_tkeep :
                    /*(state == MUX_S3) ?*/ s3_axis_tkeep;

assign m_axis_tvalid = (state == MUX_S0) ? s0_axis_tvalid :
                       (state == MUX_S1) ? s1_axis_tvalid :
                       (state == MUX_S2) ? s2_axis_tvalid :
                       (state == MUX_S3) ? s3_axis_tvalid :
                                                      1'b0;

assign m_axis_tlast = (state == MUX_S0) ? s0_axis_tlast :
                      (state == MUX_S1) ? s1_axis_tlast :
                      (state == MUX_S2) ? s2_axis_tlast :
                    /*(state == MUX_S3) ?*/ s3_axis_tlast;

assign s0_axis_tready = (state == MUX_S0) ? m_axis_tready : 1'b0;
assign s1_axis_tready = (state == MUX_S1) ? m_axis_tready : 1'b0;
assign s2_axis_tready = (state == MUX_S2) ? m_axis_tready : 1'b0;
assign s3_axis_tready = (state == MUX_S3) ? m_axis_tready : 1'b0;

wire [2:0] sel_prio_state = (s0_axis_tvalid) ? {1'b1, MUX_S0 } :
                            (s1_axis_tvalid) ? {1'b1, MUX_S1 } :
                            (s2_axis_tvalid) ? {1'b1, MUX_S2 } :
                            (s3_axis_tvalid) ? {1'b1, MUX_S3 } : {1'b0, MUX_S0 };

reg started;
wire last_transfer = m_axis_tready && m_axis_tvalid && m_axis_tlast;

always @(posedge s_axis_clk) begin
  if (~s_arstn) begin
    state   <= MUX_S0;
    started <= 1'b0;
  end else begin
    if (~started) begin
      if (m_axis_tvalid) begin
        started <= 1'b1;
      end else begin
        state   <= sel_prio_state[1:0];
      end
    end
    if (last_transfer) begin
      state   <= sel_prio_state[1:0];
      started <= (state != sel_prio_state[1:0]) && sel_prio_state[2];
    end
  end
end


endmodule

这个是四个输入到一个输出的多路器,跟一般多路器相比就是可以自主选择某一路。这个选择是根据这四路里的valid信号。并且可以看到这思路优先级是按照0-3。也就说mux0具备最高的优先级。

5,中断路由器

//
// Copyright (c) 2016-2020 Fairwaves, Inc.
// SPDX-License-Identifier: CERN-OHL-W-2.0
//

module int_router #(
    parameter COUNT = 16,
    parameter DELAY_CYCLE = 0
) (
    input clk,
    input reset,

    // PCI-e interface
    input            interrupt_msi_enabled,
    input            interrupt_rdy,

    output reg       interrupt,
    output reg       interrupt_assert,
    output reg [7:0] interrupt_num,

    input            legacy_interrupt_disabled,
    input      [2:0] interrupt_mmenable,

    output     [4:0] cap_interrupt_msgnum,
    output           cap_interrupt_stat,

    // User Interrupt status
    input                 int_stat_ready,
    output                int_stat_valid,
    output [COUNT - 1: 0] int_stat_data,

    // User Interrupt control
    output                int_ctrl_ready,
    input                 int_ctrl_valid,
    input [COUNT - 1: 0]  int_ctrl_data,


    // User Interrupr interfcae
    input  [COUNT - 1:0]  int_valid,
    output [COUNT - 1:0]  int_ready
);
assign cap_interrupt_stat   = 1'b0;

reg [COUNT-1:0] int_en;
wire [31:0] int_active = int_valid & int_en;


wire [4:0] msi_num_x;
wire       msi_num_xval;

wire [31:0] int_active_r;
genvar i;
generate
for (i = 0; i < 32; i=i+1) begin: rev
    assign int_active_r[31-i] = int_active[i];
end
endgenerate

clz #(.B_WIDTH(5)) clz_decode (
  .data(int_active_r),

  .count(msi_num_x),
  .count_nvalid(msi_num_xval)
);
wire [4:0] msi_num = /*(msi_num_xval) ? 0 :*/ msi_num_x;


wire [36:0] msi_msk_and_cap =
  (interrupt_mmenable == 3'b000 && (COUNT > 1))  ? { 5'b00001, 32'hffff_ffff } :
  (interrupt_mmenable == 3'b001 && (COUNT > 2))  ? { 5'b00010, 32'hffff_fffe } :
  (interrupt_mmenable == 3'b010 && (COUNT > 4))  ? { 5'b00100, 32'hffff_fff8 } :
  (interrupt_mmenable == 3'b011 && (COUNT > 8))  ? { 5'b01000, 32'hffff_ff80 } :
  (interrupt_mmenable == 3'b100 && (COUNT > 16)) ? { 5'b10000, 32'hffff_8000 } :
                                                   { COUNT[4:0], 32'h0000_0000 };
assign cap_interrupt_msgnum = msi_msk_and_cap[36:32];
wire [31:0] msi_ready_clean_msk =
  (interrupt_msi_enabled) ? msi_msk_and_cap[31:0] : 32'hffff_ffff;

wire [5:0] msi_num_fit =
  (interrupt_mmenable == 3'b000 && (COUNT > 1))                    ? 6'
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值