这里开始分析一下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'