1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
|
//
// Copyright 2014 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
//
// Synthesizable test pattern generator and checker
// for AXI-Stream that can be used to test transparent blocks
// (FIFOs, switches, etc)
//
module axi_chdr_test_pattern #(
parameter SR_BASE = 8'h0, //Base address for settings in this module
parameter DELAY_MODE = "DYNAMIC", //Are delays configurable at runtime {STATIC, DYNAMIC}
parameter SID_MODE = "DYNAMIC", //Is the SID configurable at runtime {STATIC, DYNAMIC}
parameter STATIC_SID = 32'h0, //SID Value if it is static
parameter BW_COUNTER = 1 //Instantiate counters to measure bandwidth (Cycles of Data Xfer / Total cycles)
) (
input clk,
input reset,
// AXI stream to hook up to input of DUT
output reg [63:0] i_tdata,
output reg i_tlast,
output reg i_tvalid,
input i_tready,
// AXI stream to hook up to output of DUT
input [63:0] o_tdata,
input o_tlast,
input o_tvalid,
output reg o_tready,
//Settings bus interface
input set_stb,
input [7:0] set_addr,
input [31:0] set_data,
// Test flags
output reg running, //Test is currently in progress
output reg done, //(Sticky) Test has finished executing
output reg [1:0] error, //Error code from last test execution
output [127:0] status_vtr, //More information about test failure.
output [95:0] bw_ratio //Bandwidth counter info
);
//
// Error Codes
//
localparam ERR_SUCCESS = 0;
localparam ERR_DATA_MISMATCH = 1;
localparam ERR_SIZE_MISMATCH_TOO_LONG = 2;
localparam ERR_SIZE_MISMATCH_TOO_SHORT = 3;
localparam ERR_TIMEOUT_LOG2 = 10;
//
// Settings
//
wire bist_size_ramp;
wire [1:0] bist_test_patt;
wire [12:0] bist_max_pkt_size;
wire bist_go, bist_cont, bist_ctrl_wr;
wire [1:0] bist_ctrl_reserved;
wire [17:0] bist_max_pkts;
wire [15:0] bist_tx_pkt_delay;
wire [7:0] bist_rx_samp_delay;
wire [31:0] bist_cvita_sid;
localparam TEST_PATT_ZERO_ONE = 2'd0;
localparam TEST_PATT_CHECKERBOARD = 2'd1;
localparam TEST_PATT_COUNT = 2'd2;
localparam TEST_PATT_COUNT_INV = 2'd3;
// SETTING: Test Control Register
// Fields:
// - [0] : (Strobe) Start the test if 1, otherwise stop a running test.
// If no test is running then reset the status. (Reseting a
// continuously running test requires two writes to this reg)
// - [1] : Start the test in continuous mode. (Run until reset or failure)
// - [3:2] : <Unused>
// - [5:4] : Test pattern:
// * 00 = Zeros and Ones (0x0000000000000000 <-> 0xFFFFFFFFFFFFFFFF)
// * 01 = Checkerboard (0x5555555555555555 <-> 0xAAAAAAAAAAAAAAAA)
// * 10 = Counter (Each byte will count up)
// * 11 = Invert Counter (Each byte will count up and invert)
setting_reg #(
.my_addr(SR_BASE + 0), .width(6), .at_reset(3'b0)
) reg_ctrl (
.clk(clk), .rst(reset),
.strobe(set_stb), .addr(set_addr), .in(set_data),
.out({bist_test_patt, bist_ctrl_reserved, bist_cont, bist_go}),.changed(bist_ctrl_wr)
);
wire bist_start = bist_ctrl_wr & bist_go;
wire bist_clear = bist_ctrl_wr & ~bist_go;
// SETTING: Test Packet Configuration Register
// Fields:
// - [17:0] : Number of packets to transfer per BIST execution
// - [30:18] : Max number of bytes of payload per packet
// - [31] : Send variable (ramping) sized packets
setting_reg #(
.my_addr(SR_BASE + 1), .width(32), .at_reset(32'b0)
) reg_pkt_config (
.clk(clk), .rst(reset),
.strobe(set_stb), .addr(set_addr), .in(set_data),
.out({bist_size_ramp, bist_max_pkt_size, bist_max_pkts}),.changed()
);
generate if (DELAY_MODE == "DYNAMIC") begin
// SETTING: Delay Register
// Fields:
// - [15:0] : Number of cycles to wait between generating consecutive *packets*
// - [23:16] : Number of cycles to wait between consuming consecutive *samples*
setting_reg #(
.my_addr(SR_BASE + 2), .width(24), .at_reset(24'b0)
) reg_delay (
.clk(clk), .rst(reset),
.strobe(set_stb), .addr(set_addr), .in(set_data),
.out({bist_rx_samp_delay, bist_tx_pkt_delay}),.changed()
);
end else begin
assign {bist_rx_samp_delay, bist_tx_pkt_delay} = 24'h0;
end endgenerate
generate if (SID_MODE == "DYNAMIC") begin
// SETTING: CHDR Stream ID Register
// Fields:
// - [31:0] : Stream ID to attach to CHDR packets
setting_reg #(
.my_addr(SR_BASE + 3), .width(32), .at_reset(32'b0)
) reg_sid (
.clk(clk), .rst(reset),
.strobe(set_stb), .addr(set_addr), .in(set_data),
.out(bist_cvita_sid),.changed()
);
end else begin
assign bist_cvita_sid = STATIC_SID;
end endgenerate
//
// State
//
localparam TX_IDLE = 3'd0;
localparam TX_START = 3'd1;
localparam TX_ACTIVE = 3'd2;
localparam TX_GAP = 3'd3;
localparam TX_DONE = 3'd4;
localparam TX_WAIT = 3'd5;
localparam RX_IDLE = 3'd0;
localparam RX_ACTIVE = 3'd1;
localparam RX_FAIL = 3'd2;
localparam RX_DONE = 3'd3;
localparam RX_WAIT = 3'd4;
reg [2:0] tx_state, rx_state;
reg [ERR_TIMEOUT_LOG2-1:0] err_timeout;
reg [1:0] test_pattern;
reg rearm_test;
reg [17:0] tx_pkt_cnt, rx_pkt_cnt;
reg [13:0] tx_byte_cnt, rx_byte_cnt;
reg [23:0] test_run_cnt;
reg [15:0] tx_delay;
reg [7:0] rx_delay;
wire [63:0] tx_cvita_hdr, rx_cvita_hdr;
wire tx_next_pkt_cond, rx_next_pkt_cond;
assign tx_next_pkt_cond = (tx_byte_cnt[12:3] == bist_max_pkt_size[12:3]) || //Packet size reaches max OR
(bist_size_ramp && ({7'h0, tx_byte_cnt[13:3]} == tx_pkt_cnt)); //Packet size / 8 == Packet Count
assign rx_next_pkt_cond = (rx_byte_cnt[12:3] == bist_max_pkt_size[12:3]) ||
(bist_size_ramp && ({7'h0, rx_byte_cnt[13:3]} == rx_pkt_cnt));
wire tx_test_done_cond, rx_test_done_cond;
assign tx_test_done_cond = (tx_pkt_cnt == bist_max_pkts);
assign rx_test_done_cond = (rx_pkt_cnt == bist_max_pkts);
reg [63:0] tx_data_next, rx_data_exp;
always @(*) begin
case (test_pattern)
TEST_PATT_ZERO_ONE: begin
tx_data_next <= {8{tx_byte_cnt[3] ? 8'h00 : 8'hFF}};
rx_data_exp <= {8{rx_byte_cnt[3] ? 8'h00 : 8'hFF}};
end
TEST_PATT_CHECKERBOARD: begin
tx_data_next <= {32{tx_byte_cnt[3] ? 2'b01 : 2'b10}};
rx_data_exp <= {32{rx_byte_cnt[3] ? 2'b01 : 2'b10}};
end
TEST_PATT_COUNT: begin
tx_data_next <= {8{tx_byte_cnt[10:3]}};
rx_data_exp <= {8{rx_byte_cnt[10:3]}};
end
TEST_PATT_COUNT_INV: begin
tx_data_next <= {8{(tx_byte_cnt[3] ? 8'hFF : 8'h00) ^ tx_byte_cnt[10:3]}};
rx_data_exp <= {8{(rx_byte_cnt[3] ? 8'hFF : 8'h00) ^ rx_byte_cnt[10:3]}};
end
default: begin
tx_data_next <= 64'd0;
rx_data_exp <= 64'd0;
end
endcase
end
//NOTE: We always attach the max size in the packet header for simplicity.
// This will not work with state machines that validate the packet length in the
// header with the tlast position.
assign tx_cvita_hdr = {4'h0, tx_pkt_cnt[11:0], 2'b00, bist_max_pkt_size, bist_cvita_sid};
assign rx_cvita_hdr = {4'h0, rx_pkt_cnt[11:0], 2'b00, bist_max_pkt_size, bist_cvita_sid};
reg [63:0] o_tdata_fail;
assign status_vtr = { //Status at the time of failure
o_tdata_fail, //[127:64]
test_run_cnt, //[63:40]
rx_data_exp[7:0], //[39:32]
rx_pkt_cnt, //[31:14]
rx_byte_cnt //[13:0]
};
//-------------------------------------------------------
// Transmitter
//-------------------------------------------------------
always @(posedge clk) begin
if (reset | (bist_clear & ~rearm_test)) begin
tx_delay <= 0;
tx_pkt_cnt <= 0;
tx_byte_cnt <= 0;
i_tdata <= 64'h0;
i_tlast <= 1'b0;
i_tvalid <= 1'b0;
tx_state <= TX_IDLE;
end else begin
case(tx_state)
TX_IDLE: begin
tx_delay <= 0;
tx_pkt_cnt <= 1;
tx_byte_cnt <= 0;
i_tdata <= 64'h0;
i_tlast <= 1'b0;
i_tvalid <= 1'b0;
// Run when bist_start asserted.
if (bist_start | rearm_test) begin
tx_state <= TX_START;
test_pattern <= bist_test_patt;
end
end // case: TX_IDLE
// START signal is asserted.
// Now need to start transmiting a packet.
TX_START: begin
// At the next clock edge drive first beat of new packet onto HDR bus.
i_tlast <= 1'b0;
i_tvalid <= 1'b1;
tx_byte_cnt <= tx_byte_cnt + 8;
i_tdata <= tx_cvita_hdr;
tx_state <= TX_ACTIVE;
end
// Valid data is (already) being driven onto the CHDR bus.
// i_tlast may also be driven asserted if current data count has reached EOP.
// Watch i_tready to see when it's consumed.
// When packets are consumed increment data counter or transition state if
// EOP has sucsesfully concluded.
TX_ACTIVE: begin
i_tvalid <= 1'b1; // Always assert tvalid
if (i_tready) begin
i_tdata <= tx_data_next;
// Will this next beat be the last in a packet?
if (tx_next_pkt_cond) begin
tx_byte_cnt <= 0;
i_tlast <= 1'b1;
tx_state <= TX_GAP;
end else begin
tx_byte_cnt <= tx_byte_cnt + 8;
i_tlast <= 1'b0;
tx_state <= TX_ACTIVE;
end
end else begin
//Keep driving all CHDR bus signals as-is until i_tready is asserted.
tx_state <= TX_ACTIVE;
end
end // case: TX_ACTIVE
// Force an inter-packet gap between packets in a BIST sequence where tvalid is driven low.
// As we leave this state check if all packets in BIST sequence have been generated yet,
// and if so go to done state.
TX_GAP: begin
if (i_tready) begin
i_tvalid <= 1'b0;
i_tdata <= 64'h0;
i_tlast <= 1'b0;
tx_pkt_cnt <= tx_pkt_cnt + 1;
if (tx_test_done_cond) begin
tx_state <= TX_DONE;
end else begin
tx_state <= TX_WAIT;
tx_delay <= bist_tx_pkt_delay;
end
end else begin // if (i_tready)
tx_state <= TX_GAP;
end
end // case: TX_GAP
// Simulate inter packet gap in real UHD system
TX_WAIT: begin
if (tx_delay == 0)
tx_state <= TX_START;
else begin
tx_delay <= tx_delay - 1;
tx_state <= TX_WAIT;
end
end
// Complete test pattern BIST sequence has been transmitted.
// Sit in this state until the RX side consumes all packets except
// for when the test is running in continuous mode.
TX_DONE: begin
i_tvalid <= 1'b0;
i_tlast <= 1'b0;
i_tdata <= 64'd0;
if (running & ~rearm_test) begin
tx_state <= TX_DONE;
end else begin
tx_state <= TX_IDLE;
end
end
endcase // case (tx_state)
end
end
//-------------------------------------------------------
// Receiver
//-------------------------------------------------------
always @(posedge clk) begin
if (reset | (bist_clear & ~rearm_test)) begin
rx_delay <= 0;
rx_pkt_cnt <= 0;
rx_byte_cnt <= 0;
o_tdata_fail <= 64'h0;
o_tready <= 1'b0;
error <= ERR_SUCCESS;
done <= 1'b0;
rx_state <= RX_IDLE;
err_timeout <= {ERR_TIMEOUT_LOG2{1'b0}};
test_run_cnt <= 0;
end else begin
case(rx_state)
RX_IDLE: begin
rx_delay <= 0;
rx_pkt_cnt <= 1;
rx_byte_cnt <= 0;
o_tdata_fail <= 64'h0;
o_tready <= 1'b0;
error <= ERR_SUCCESS;
done <= 1'b0;
err_timeout <= {ERR_TIMEOUT_LOG2{1'b0}};
// Not accepting data whilst Idle,
// switch to active when packet arrives
if (o_tvalid) begin
o_tready <= 1'b1;
rx_state <= RX_ACTIVE;
end else begin
rx_state <= RX_IDLE;
end
end
RX_ACTIVE: begin
o_tready <= 1'b1;
if (o_tvalid) begin
if (o_tdata != (rx_byte_cnt == 0 ? rx_cvita_hdr : rx_data_exp)) begin
$display("axis_test_pattern: o_tdata: %x != expected: %x @ time: %d", o_tdata, rx_data_exp, $time);
error <= ERR_DATA_MISMATCH;
rx_state <= RX_FAIL;
o_tdata_fail <= o_tdata;
end else if (rx_next_pkt_cond) begin
// Last not asserted when it should be!
if (~(o_tlast === 1)) begin
$display("axis_test_pattern: o_tlast not asserted when it should be @ time: %d", $time);
error <= ERR_SIZE_MISMATCH_TOO_LONG;
rx_state <= RX_FAIL;
end else begin
// End of packet, set up to RX next
rx_byte_cnt <= 0;
rx_pkt_cnt <= rx_pkt_cnt + 1;
rx_delay <= bist_rx_samp_delay;
if (rx_test_done_cond) begin
rx_state <= rearm_test ? RX_IDLE : RX_DONE;
error <= ERR_SUCCESS;
test_run_cnt <= test_run_cnt + 1;
end else begin
rx_state <= RX_WAIT;
end
o_tready <= 1'b0;
end
end else begin
// ...last asserted when it should not be!
if (~(o_tlast === 0)) begin
$display("axis_test_pattern: o_tlast asserted when it should not be @ time: %d", $time);
error <= ERR_SIZE_MISMATCH_TOO_SHORT;
rx_state <= RX_FAIL;
end else begin
// Still in packet body
rx_byte_cnt <= rx_byte_cnt + 8;
rx_delay <= bist_rx_samp_delay;
if (bist_rx_samp_delay == 0) begin
rx_state <= RX_ACTIVE;
end else begin
rx_state <= RX_WAIT;
o_tready <= 1'b0;
end
end
end
end else begin
// Nothing to do this cycle
rx_state <= RX_ACTIVE;
end
end // case: RX_ACTIVE
// To simulate the radio consuming samples at a steady rate set by the decimation
// have a programable delay here
RX_WAIT: begin
if (rx_delay == 0) begin
rx_state <= RX_ACTIVE;
o_tready <= 1'b1;
end else begin
rx_delay <= rx_delay - 1;
rx_state <= RX_WAIT;
end
end
RX_FAIL: begin
//The test has failed but the sender still has packets en route
//Consume all of them before asserting done. Packets could be
//malformed so just blindly consume lines and count cycles of
//gaps. If non-valid cycles are more than 2^ERR_TIMEOUT_LOG2 then stop.
o_tready <= 1'b1;
if (~o_tvalid) begin
if (err_timeout == {ERR_TIMEOUT_LOG2{1'b1}}) begin
rx_state <= RX_DONE;
end
err_timeout <= err_timeout + 1;
end
end
RX_DONE: begin
o_tready <= 1'b0;
done <= 1'b1;
//The only way to exit this state is by asserting bist_clear
end
endcase // case (rx_state)
end
end
//-------------------------------------------------------
// Status Monitor
//-------------------------------------------------------
always @(posedge clk) begin
if (reset)
running <= 1'b0;
else if (tx_state == TX_START)
running <= 1'b1;
else if (rx_state == RX_DONE)
running <= 1'b0;
end
always @(posedge clk) begin
if (reset | bist_clear)
rearm_test <= 1'b0;
else if (bist_start & bist_cont)
rearm_test <= 1'b1;
else if (rx_state == RX_FAIL)
rearm_test <= 1'b0;
end
//-------------------------------------------------------
// Bandwidth Counter
//-------------------------------------------------------
generate if (BW_COUNTER) begin
reg [47:0] word_count, cyc_count;
assign bw_ratio = {word_count, cyc_count};
//Count number of lines transferred
always @(posedge clk) begin
if (reset| (bist_clear & ~rearm_test) | bist_start)
word_count <= 48'd0;
else if (o_tvalid && rx_state == RX_ACTIVE)
word_count <= word_count + 48'd1;
end
//Count cycles as long as test is running
always @(posedge clk) begin
if (reset| (bist_clear & ~rearm_test) | bist_start)
cyc_count <= 48'd0;
else if (rx_state == RX_ACTIVE || rx_state == RX_WAIT)
cyc_count <= cyc_count + 48'd1;
end
end else begin
assign bw_ratio = 96'h0;
end endgenerate
endmodule
|