206 lines
7.5 KiB
Systemverilog
206 lines
7.5 KiB
Systemverilog
//
|
|
// Copyright 2018 Ettus Research, A National Instruments Company
|
|
//
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
//
|
|
// Module: chdr_traffic_source_sim
|
|
// Description:
|
|
// A traffic generator for CHDR traffic. Simulation only.
|
|
// Supports multiple traffic pattern and injection rates.
|
|
//
|
|
|
|
`timescale 1ns/1ps
|
|
|
|
`include "sim_cvita_lib.svh"
|
|
|
|
module chdr_traffic_source_sim #(
|
|
parameter WIDTH = 64, // Width of the AXI-Stream data bus
|
|
parameter MTU = 5, // log2 of the max number of lines in a packet
|
|
parameter [15:0] NODE_ID = 'd0, // Node ID for this generator
|
|
parameter [15:0] NUM_NODES = 'd16 // Total number of generators in the application
|
|
) (
|
|
// Clocks and resets
|
|
input clk, // AXI-Stream clock
|
|
input rst, // AXI-Stream reset
|
|
// Settings
|
|
input [63:0] current_time, // The current value of the global timebase (synch to clk)
|
|
input start_stb, // A strobe that indicates the start of a generation session
|
|
input [7:0] injection_rate, // The inject rate (in percent) to simulate
|
|
input [15:0] lines_per_pkt, // Number of lines per packet to generate
|
|
input [7:0] traffic_patt, // The traffic pattern (see localparams below for values)
|
|
input [31:0] num_pkts_to_send, // Number of packets to send
|
|
// CHDR master interface
|
|
output [WIDTH-1:0] m_axis_tdata, // AXI-Stream master tdata
|
|
output m_axis_tlast, // AXI-Stream master tlast
|
|
output m_axis_tvalid, // AXI-Stream master tvalid
|
|
input m_axis_tready, // AXI-Stream master tready
|
|
// Metrics
|
|
output session_active, // Signal indicating if generation session is active
|
|
output [63:0] session_duration, // Session duration (only valid after session ends)
|
|
output [31:0] xfer_count, // Number of lines transferred (only valid after session ends)
|
|
output [31:0] pkt_count // Number of packets transferred (only valid after session ends)
|
|
);
|
|
// **** Supported Traffic Patters ****
|
|
localparam [7:0] TRAFFIC_PATT_LOOPBACK = 8'd76; //L
|
|
localparam [7:0] TRAFFIC_PATT_NEIGHBOR = 8'd78; //N
|
|
localparam [7:0] TRAFFIC_PATT_BIT_COMPLEMENT = 8'd67; //C
|
|
localparam [7:0] TRAFFIC_PATT_SEQUENTIAL = 8'd83; //S
|
|
localparam [7:0] TRAFFIC_PATT_UNIFORM = 8'd85; //U
|
|
localparam [7:0] TRAFFIC_PATT_UNIFORM_OTHERS = 8'd79; //O
|
|
localparam [7:0] TRAFFIC_PATT_RANDOM_PERM = 8'd82; //R
|
|
|
|
cvita_master #(.DWIDTH(WIDTH)) m_chdr (.clk(clk));
|
|
axis_t #(.DWIDTH(WIDTH)) post_fifo (.clk(clk));
|
|
axis_t #(.DWIDTH(WIDTH)) pre_gate (.clk(clk));
|
|
cvita_hdr_t header;
|
|
reg throttle = 1'b1;
|
|
|
|
logic running = 0;
|
|
logic [31:0] curr_pkt_num = 'd0;
|
|
logic [31:0] num_samps_xferd = 'd0;
|
|
logic [63:0] start_time = 0;
|
|
logic [63:0] stop_time = 0;
|
|
logic [15:0] last_gen_sid = (NODE_ID - 16'd1);
|
|
|
|
assign xfer_count = num_samps_xferd;
|
|
assign pkt_count = curr_pkt_num;
|
|
assign session_duration = (stop_time - start_time);
|
|
assign session_active = running;
|
|
|
|
// Utility function to assign SIDs based on traffic pattern
|
|
function [15:0] gen_dst_sid;
|
|
input [7:0] traffic_patt;
|
|
input [15:0] last_sid;
|
|
|
|
logic [31:0] rnum;
|
|
|
|
if (traffic_patt == TRAFFIC_PATT_UNIFORM) begin
|
|
gen_dst_sid = $urandom_range('d0, NUM_NODES-'d1);
|
|
end else if (traffic_patt == TRAFFIC_PATT_UNIFORM_OTHERS) begin
|
|
rnum = $urandom_range('d0, NUM_NODES-'d2);
|
|
if (rnum < NODE_ID)
|
|
gen_dst_sid = rnum[15:0];
|
|
else
|
|
gen_dst_sid = rnum[15:0] + 16'd1;
|
|
end else if (traffic_patt == TRAFFIC_PATT_SEQUENTIAL) begin
|
|
gen_dst_sid = (last_sid + 16'd1) % NUM_NODES;
|
|
end else if (traffic_patt == TRAFFIC_PATT_NEIGHBOR) begin
|
|
gen_dst_sid = (NODE_ID + 16'd1) % NUM_NODES;
|
|
end else if (traffic_patt == TRAFFIC_PATT_LOOPBACK) begin
|
|
gen_dst_sid = NODE_ID;
|
|
end else if (traffic_patt == TRAFFIC_PATT_BIT_COMPLEMENT) begin
|
|
gen_dst_sid = (NUM_NODES - NODE_ID - 1) % NUM_NODES;
|
|
end else if (traffic_patt == TRAFFIC_PATT_RANDOM_PERM) begin
|
|
//TODO: Implement me
|
|
gen_dst_sid = 0;
|
|
end else begin
|
|
gen_dst_sid = 'd0;
|
|
end
|
|
endfunction
|
|
|
|
// Generation loop. Push to m_chdr infinitely fast
|
|
initial begin: gen_blk
|
|
// Generate infinitely
|
|
std::process p;
|
|
p = process::self();
|
|
p.srandom(NODE_ID + NUM_NODES);
|
|
m_chdr.reset();
|
|
while (1) begin
|
|
// A generation session begins on the posedge of start_stb
|
|
while (~start_stb) @(posedge clk);
|
|
curr_pkt_num = 'd0;
|
|
m_chdr.reset();
|
|
num_samps_xferd = 'd0;
|
|
start_time = current_time;
|
|
running = 1;
|
|
while (curr_pkt_num < num_pkts_to_send) begin
|
|
header = '{
|
|
pkt_type:DATA, has_time:1, eob:0,
|
|
seqnum:curr_pkt_num[11:0], length:(lines_per_pkt*8),
|
|
src_sid:NODE_ID, dst_sid:gen_dst_sid(traffic_patt, last_gen_sid),
|
|
timestamp:0 //TS attached later
|
|
};
|
|
last_gen_sid = header.dst_sid;
|
|
curr_pkt_num = curr_pkt_num + 'd1;
|
|
m_chdr.push_ramp_pkt(lines_per_pkt-2, 'h0, 'h1, header);
|
|
num_samps_xferd = num_samps_xferd + lines_per_pkt;
|
|
end
|
|
running = 0;
|
|
stop_time = current_time;
|
|
end
|
|
end
|
|
|
|
// Capture packets in a really short FIFO (for backpressure)
|
|
axi_fifo #(
|
|
.WIDTH(WIDTH+1), .SIZE(MTU + 1)
|
|
) fifo_i (
|
|
.clk (clk),
|
|
.reset (rst),
|
|
.clear (1'b0),
|
|
.i_tdata ({m_chdr.axis.tlast, m_chdr.axis.tdata}),
|
|
.i_tvalid (m_chdr.axis.tvalid),
|
|
.i_tready (m_chdr.axis.tready),
|
|
.o_tdata ({post_fifo.tlast, post_fifo.tdata}),
|
|
.o_tvalid (post_fifo.tvalid),
|
|
.o_tready (post_fifo.tready),
|
|
.space (),
|
|
.occupied ()
|
|
);
|
|
|
|
// Attach timestamp after the packet leaves the FIFO after
|
|
// throttling.
|
|
|
|
localparam [1:0] ST_HDR = 2'd0;
|
|
localparam [1:0] ST_TS = 2'd1;
|
|
localparam [1:0] ST_BODY = 2'd2;
|
|
|
|
reg [1:0] pkt_state = ST_HDR;
|
|
always_ff @(posedge clk) begin
|
|
if (rst) begin
|
|
pkt_state <= ST_HDR;
|
|
end else if (pre_gate.tvalid & pre_gate.tready) begin
|
|
case (pkt_state)
|
|
ST_HDR:
|
|
if (~pre_gate.tlast)
|
|
pkt_state <= pre_gate.tdata[61] ? ST_TS : ST_BODY;
|
|
ST_TS:
|
|
pkt_state <= pre_gate.tlast ? ST_HDR : ST_BODY;
|
|
ST_BODY:
|
|
pkt_state <= pre_gate.tlast ? ST_HDR : ST_BODY;
|
|
default:
|
|
pkt_state <= ST_HDR;
|
|
endcase
|
|
end
|
|
end
|
|
|
|
// Enforce injection rate by pulling from FIFO with a certain time probability
|
|
always_ff @(posedge clk) begin
|
|
throttle <= ($urandom_range(32'd99, 32'd0) > {24'h0, injection_rate});
|
|
end
|
|
|
|
// Insert timestamp + throttle logic
|
|
assign pre_gate.tdata = (pkt_state == ST_TS) ? current_time : post_fifo.tdata;
|
|
assign pre_gate.tlast = post_fifo.tlast;
|
|
assign pre_gate.tvalid = post_fifo.tvalid & ~throttle;
|
|
assign post_fifo.tready = pre_gate.tready & ~throttle;
|
|
|
|
// Gate the packet to smooth out throttle-related noise.
|
|
// This also serves as a buffer for the packet in case things are backed up
|
|
axi_packet_gate #(
|
|
.WIDTH(WIDTH), .SIZE(MTU + 4), .USE_AS_BUFF(1)
|
|
) pkt_gate_i (
|
|
.clk (clk),
|
|
.reset (rst),
|
|
.clear (1'b0),
|
|
.i_tdata (pre_gate.tdata),
|
|
.i_tlast (pre_gate.tlast),
|
|
.i_terror (1'b0),
|
|
.i_tvalid (pre_gate.tvalid),
|
|
.i_tready (pre_gate.tready),
|
|
.o_tdata (m_axis_tdata),
|
|
.o_tlast (m_axis_tlast),
|
|
.o_tvalid (m_axis_tvalid),
|
|
.o_tready (m_axis_tready)
|
|
);
|
|
|
|
endmodule |