blob: c84dc20ef51de8a49e73a026f42e3e93d01319b1 [file] [log] [blame]
// File name: tb_CAN_receiver.sv
// Created: 12/1/2020
// Author: Zachary Ellis
// Version: 1.0 Initial Design Entry
// Description: testbench cor CAN receiver top module
`timescale 1ns / 10ps
module tb_CAN_receiver();
localparam CLK_PERIOD = 100;
localparam STROBE_PERIOD = 10 * CLK_PERIOD;
// Declare DUT portmap signals
reg tb_clk;
reg tb_nRST;
reg tb_CANRX;
reg [3:0] tb_readaddr;
wire tb_ACK;
wire [7:0] tb_byte_out;
wire tb_busy;
wire tb_bitstuff_error;
wire [10:0] tb_CAN_ID;
wire tb_CRC_Error;
// Declare test bench signals
integer tb_test_num;
string tb_test_case;
integer tb_stream_test_num;
string tb_stream_check_tag;
reg [10:0] msg_msg_id;
reg [3:0] msg_pkt_size;
reg [7:0] [7:0] msg_pkt_data;
reg [14:0] msg_CRC;
reg enable_biterror;
reg bitstream [];
integer lag;
// Task for standard DUT reset procedure
task reset_dut;
begin
// Activate the reset
tb_nRST = 1'b0;
// Maintain the reset for more than one cycle
@(posedge tb_clk);
@(posedge tb_clk);
// Wait until safely away from rising edge of the clock before releasing
@(negedge tb_clk);
tb_nRST = 1'b1;
// Leave out of reset for a couple cycles before allowing other stimulus
// Wait for negative clock edges,
// since inputs to DUT should normally be applied away from rising clock edges
@(negedge tb_clk);
@(negedge tb_clk);
#(STROBE_PERIOD * 11);
end
endtask
task construct_pkt;
input logic [10:0] msg_id;
input logic [3:0] pkt_size;
input logic [7:0] [7:0] pkt_data;
input logic [14:0] CRC;
begin
integer package_size;
integer index;
integer ones;
integer zeros;
ones = 1;
zeros = 0;
if(enable_biterror) begin
ones = -100;
zeros = -100;
end
package_size = 50 + (pkt_size * 8);
index = 0;
bitstream = new[package_size];
bitstream[index++] = 1;
for(integer i = 0; i < 11; i++) begin //msg id
bitstream[index++] = msg_id[10-i];
if(bitstream[index - 1]) begin
ones++;
zeros = (enable_biterror) ? -100 : 0;
if(ones == 5) begin
bitstream[index++] = 1'b0;
$display("added stuffed bit");
ones = 0;
end
end
else begin
ones = (enable_biterror) ? -100: 0;
zeros++;
if(zeros == 5) begin
bitstream[index++] = 1'b1;
$display("added stuffed bit");
zeros = 0;
end
end
end
bitstream[index++] = 1'b1; //RTR bit
if(bitstream[index - 1]) begin
ones++;
zeros = (enable_biterror) ? -100 : 0;
if(ones == 5) begin
bitstream[index++] = 1'b0;
$display("added stuffed bit");
ones = 0;
end
end
else begin
ones = (enable_biterror) ? -100 : 0;
zeros++;
if(zeros == 5) begin
bitstream[index++] = 1'b1;
$display("added stuffed bit");
zeros = 0;
end
end
bitstream[index++] = 1'b1; //IDE bit
if(bitstream[index - 1]) begin
ones++;
zeros = (enable_biterror) ? -100 : 0;
if(ones == 5) begin
bitstream[index++] = 1'b0;
$display("added stuffed bit");
ones = 0;
end
end
else begin
ones = (enable_biterror) ? -100 : 0;
zeros++;
if(zeros == 5) begin
bitstream[index++] = 1'b1;
$display("added stuffed bit");
zeros = 0;
end
end
bitstream[index++] = 1'b0; //reserved bit
zeros++;
for(integer i = 0; i < 4; i++) begin //pkt_size
bitstream[index++] = pkt_size[3-i];
if(bitstream[index - 1]) begin
ones++;
zeros = (enable_biterror) ? -100 : 0;
if(ones == 5) begin
bitstream[index++] = 1'b0;
$display("added stuffed bit");
ones = 0;
end
end
else begin
ones = (enable_biterror) ? -100 : 0;
zeros++;
if(zeros == 5) begin
bitstream[index++] = 1'b1;
$display("added stuffed bit");
zeros = 0;
end
end
end
for(integer i = 0; i < pkt_size; i++) begin //data contained
for(integer j = 0; j < 8; j++) begin
bitstream[index++] = pkt_data[i][7-j];
if(bitstream[index - 1]) begin
ones++;
zeros = (enable_biterror) ? -100 : 0;
if(ones == 5) begin
bitstream[index++] = 1'b0;
$display("added stuffed bit");
ones = 0;
end
end
else begin
ones = (enable_biterror) ? -100 : 0;
zeros++;
if(zeros == 5) begin
bitstream[index++] = 1'b1;
$display("added stuffed bit");
zeros = 0;
end
end
end
end
for(integer i = 0; i < 15; i++) begin //crc code
bitstream[index++] = CRC[14-i];
if(bitstream[index - 1]) begin
ones++;
zeros = (enable_biterror) ? -100 : 0;
if(ones == 5) begin
bitstream[index++] = 1'b0;
$display("added stuffed bit");
ones = 0;
end
end
else begin
ones = (enable_biterror) ? -100 : 0;
zeros++;
if(zeros == 5) begin
bitstream[index++] = 1'b1;
$display("added stuffed bit");
zeros = 0;
end
end
end
bitstream[index++] = 1'b0;
bitstream[index++] = 1'b0;
bitstream[index++] = 1'b0;
for(int i = index; i < bitstream.size(); i++)begin
bitstream[index++] = 1'b0;
end
end
endtask
task RXstream;
input logic bitstream [];
input integer streamlen;
begin
tb_CANRX = bitstream[0];
for(integer i = 1; i < streamlen; i++) begin
#(STROBE_PERIOD + lag);
tb_CANRX = bitstream[i];
end
end
endtask
task check_out;
input logic [10:0] real_out;
input logic [10:0] expected_out;
input string signal_name;
begin
if(expected_out == real_out) begin // Check passed
$info("Correct %s output during %s test case", signal_name, tb_test_case);
end
else begin // Check failed
$error("Incorrect %s output during %s test case", signal_name, tb_test_case);
end
end
endtask
always
begin
// Start with clock low to avoid false rising edge events at t=0
tb_clk = 1'b0;
// Wait half of the clock period before toggling clock value (maintain 50% duty cycle)
#(CLK_PERIOD/2.0);
tb_clk = 1'b1;
// Wait half of the clock period before toggling clock value via rerunning the block (maintain 50% duty cycle)
#(CLK_PERIOD/2.0);
end
CAN_receiver DUT (
.clk(tb_clk),
.nRST(tb_nRST),
.CANRX(tb_CANRX),
.readaddr(tb_readaddr),
.ACK(tb_ACK),
.byte_out(tb_byte_out),
.busy(tb_busy),
.bitstuff_error(tb_bitstuff_error),
.CAN_ID(tb_CAN_ID),
.CRC_Error(tb_CRC_Error)
);
initial begin
tb_nRST = 1'b1;
tb_CANRX = 1'b0;
tb_readaddr = 1'b0;
tb_test_num = 0;
tb_test_case = "Test bench initializaton";
tb_stream_test_num = 0;
tb_stream_check_tag = "N/A";
msg_msg_id = '0;
msg_pkt_size = '0;
msg_pkt_data = '0;
msg_CRC = '0;
enable_biterror = 1'b0;
lag = 0;
// ************************************************************************
// Test Case 1: Basic correct bitstream
// ************************************************************************
tb_test_num = tb_test_num + 1;
tb_test_case = "Basic correct bitstream";
reset_dut();
msg_msg_id = 11'b10110010110;
msg_pkt_size = 4'd2;
msg_pkt_data[0] = 8'b10101100;
msg_pkt_data[1] = 8'b10101101;
msg_CRC = 15'b111001110100010;
construct_pkt(msg_msg_id, msg_pkt_size, msg_pkt_data, msg_CRC);
RXstream(bitstream, bitstream.size());
check_out(msg_msg_id, tb_CAN_ID, "CAN ID");
tb_readaddr = 4'd0;
@(posedge tb_clk);
check_out(msg_pkt_data[0], {3'd0,tb_byte_out}, "data 0");
tb_readaddr = 4'd1;
@(posedge tb_clk);
check_out(msg_pkt_data[1], {3'd0,tb_byte_out}, "data 1");
#(STROBE_PERIOD * 11);
// ************************************************************************
// Test Case 2: long bitstream
// ************************************************************************
tb_test_num = tb_test_num + 1;
tb_test_case = "long bitstream";
reset_dut();
msg_msg_id = 11'b10110010110;
msg_pkt_size = 4'd8;
msg_pkt_data[0] = 8'b10101100;
msg_pkt_data[1] = 8'b10101101;
msg_pkt_data[2] = 8'b10101001;
msg_pkt_data[3] = 8'b10001101;
msg_pkt_data[4] = 8'b10100101;
msg_pkt_data[5] = 8'b10101111;
msg_pkt_data[6] = 8'b00111101;
msg_pkt_data[7] = 8'b11101101;
msg_CRC = 15'b001100110011111;
construct_pkt(msg_msg_id, msg_pkt_size, msg_pkt_data, msg_CRC);
RXstream(bitstream, bitstream.size());
check_out(msg_msg_id, tb_CAN_ID, "CAN ID");
tb_readaddr = 4'd0;
@(posedge tb_clk);
check_out(msg_pkt_data[0], {3'd0,tb_byte_out}, "data 0");
tb_readaddr = 4'd1;
@(posedge tb_clk);
check_out(msg_pkt_data[1], {3'd0,tb_byte_out}, "data 1");
tb_readaddr = 4'd2;
@(posedge tb_clk);
check_out(msg_pkt_data[2], {3'd0,tb_byte_out}, "data 2");
tb_readaddr = 4'd3;
@(posedge tb_clk);
check_out(msg_pkt_data[3], {3'd0,tb_byte_out}, "data 3");
tb_readaddr = 4'd4;
@(posedge tb_clk);
check_out(msg_pkt_data[4], {3'd0,tb_byte_out}, "data 4");
tb_readaddr = 4'd5;
@(posedge tb_clk);
check_out(msg_pkt_data[5], {3'd0,tb_byte_out}, "data 5");
tb_readaddr = 4'd6;
@(posedge tb_clk);
check_out(msg_pkt_data[6], {3'd0,tb_byte_out}, "data 6");
tb_readaddr = 4'd7;
@(posedge tb_clk);
check_out(msg_pkt_data[7], {3'd0,tb_byte_out}, "data 7");
#(STROBE_PERIOD * 11);
// ************************************************************************
// Test Case 3: long bitstream + stuffed bits
// ************************************************************************
tb_test_num = tb_test_num + 1;
tb_test_case = "long bitstream + stuffed bits";
reset_dut();
msg_msg_id = 11'b10110010110;
msg_pkt_size = 4'd8;
msg_pkt_data[0] = 8'b10101111;
msg_pkt_data[1] = 8'b11101101;
msg_pkt_data[2] = 8'b10101000;
msg_pkt_data[3] = 8'b00001101;
msg_pkt_data[4] = 8'b10100101;
msg_pkt_data[5] = 8'b10101111;
msg_pkt_data[6] = 8'b10111101;
msg_pkt_data[7] = 8'b11101101;
msg_CRC = 15'b001100110101010;
construct_pkt(msg_msg_id, msg_pkt_size, msg_pkt_data, msg_CRC);
RXstream(bitstream, bitstream.size());
check_out(msg_msg_id, tb_CAN_ID, "CAN ID");
tb_readaddr = 4'd0;
@(posedge tb_clk);
check_out(msg_pkt_data[0], {3'd0,tb_byte_out}, "data 0");
tb_readaddr = 4'd1;
@(posedge tb_clk);
check_out(msg_pkt_data[1], {3'd0,tb_byte_out}, "data 1");
tb_readaddr = 4'd2;
@(posedge tb_clk);
check_out(msg_pkt_data[2], {3'd0,tb_byte_out}, "data 2");
tb_readaddr = 4'd3;
@(posedge tb_clk);
check_out(msg_pkt_data[3], {3'd0,tb_byte_out}, "data 3");
tb_readaddr = 4'd4;
@(posedge tb_clk);
check_out(msg_pkt_data[4], {3'd0,tb_byte_out}, "data 4");
tb_readaddr = 4'd5;
@(posedge tb_clk);
check_out(msg_pkt_data[5], {3'd0,tb_byte_out}, "data 5");
tb_readaddr = 4'd6;
@(posedge tb_clk);
check_out(msg_pkt_data[6], {3'd0,tb_byte_out}, "data 6");
tb_readaddr = 4'd7;
@(posedge tb_clk);
check_out(msg_pkt_data[7], {3'd0,tb_byte_out}, "data 7");
#(STROBE_PERIOD * 11);
// ************************************************************************
// Test Case 3: edge case stuffed bits at the end of a byte
// ************************************************************************
tb_test_num = tb_test_num + 1;
tb_test_case = "edge case stuffed bits at the end of a byte";
reset_dut();
msg_msg_id = 11'b10110010110;
msg_pkt_size = 4'd3;
msg_pkt_data[0] = 8'b10111111;
msg_pkt_data[1] = 8'b01101101;
msg_pkt_data[2] = 8'b10101010;
msg_CRC = 15'b011010011100101;
construct_pkt(msg_msg_id, msg_pkt_size, msg_pkt_data, msg_CRC);
RXstream(bitstream, bitstream.size());
check_out(msg_msg_id, tb_CAN_ID, "CAN ID");
tb_readaddr = 4'd0;
@(posedge tb_clk);
check_out(msg_pkt_data[0], {3'd0,tb_byte_out}, "data 0");
tb_readaddr = 4'd1;
@(posedge tb_clk);
check_out(msg_pkt_data[1], {3'd0,tb_byte_out}, "data 1");
tb_readaddr = 4'd2;
@(posedge tb_clk);
check_out(msg_pkt_data[2], {3'd0,tb_byte_out}, "data 2");
#(STROBE_PERIOD * 11);
// ************************************************************************
// Test Case 4: long bitstream + bit error
// ************************************************************************
tb_test_num = tb_test_num + 1;
tb_test_case = "long bitstream + bit error";
enable_biterror = 1'b1;
reset_dut();
msg_msg_id = 11'b10110010110;
msg_pkt_size = 4'd8;
msg_pkt_data[0] = 8'b10101111;
msg_pkt_data[1] = 8'b11101101;
msg_pkt_data[2] = 8'b10101000;
msg_pkt_data[3] = 8'b00001101;
msg_pkt_data[4] = 8'b10100101;
msg_pkt_data[5] = 8'b10101111;
msg_pkt_data[6] = 8'b10111101;
msg_pkt_data[7] = 8'b11101101;
msg_CRC = 15'b001100110101010;
construct_pkt(msg_msg_id, msg_pkt_size, msg_pkt_data, msg_CRC);
RXstream(bitstream, bitstream.size());
check_out(msg_msg_id, tb_CAN_ID, "CAN ID");
tb_readaddr = 4'd0;
@(posedge tb_clk);
check_out(msg_pkt_data[0], {3'd0,tb_byte_out}, "data 0");
tb_readaddr = 4'd1;
@(posedge tb_clk);
check_out(msg_pkt_data[1], {3'd0,tb_byte_out}, "data 1");
tb_readaddr = 4'd2;
@(posedge tb_clk);
check_out(msg_pkt_data[2], {3'd0,tb_byte_out}, "data 2");
tb_readaddr = 4'd3;
@(posedge tb_clk);
check_out(msg_pkt_data[3], {3'd0,tb_byte_out}, "data 3");
tb_readaddr = 4'd4;
@(posedge tb_clk);
check_out(msg_pkt_data[4], {3'd0,tb_byte_out}, "data 4");
tb_readaddr = 4'd5;
@(posedge tb_clk);
check_out(msg_pkt_data[5], {3'd0,tb_byte_out}, "data 5");
tb_readaddr = 4'd6;
@(posedge tb_clk);
check_out(msg_pkt_data[6], {3'd0,tb_byte_out}, "data 6");
tb_readaddr = 4'd7;
@(posedge tb_clk);
check_out(msg_pkt_data[7], {3'd0,tb_byte_out}, "data 7");
#(STROBE_PERIOD * 11);
// ************************************************************************
// Test Case 5: long bitstream + 1.5% slower
// ************************************************************************
tb_test_num = tb_test_num + 1;
tb_test_case = "long bitstream + 1.5% slower";
lag = STROBE_PERIOD * 0.015;
reset_dut();
msg_msg_id = 11'b10110010110;
msg_pkt_size = 4'd8;
msg_pkt_data[0] = 8'b10101100;
msg_pkt_data[1] = 8'b10101101;
msg_pkt_data[2] = 8'b10101001;
msg_pkt_data[3] = 8'b10001101;
msg_pkt_data[4] = 8'b10100101;
msg_pkt_data[5] = 8'b10101111;
msg_pkt_data[6] = 8'b00111101;
msg_pkt_data[7] = 8'b11101101;
msg_CRC = 15'b001100110011111;
construct_pkt(msg_msg_id, msg_pkt_size, msg_pkt_data, msg_CRC);
RXstream(bitstream, bitstream.size());
check_out(msg_msg_id, tb_CAN_ID, "CAN ID");
tb_readaddr = 4'd0;
@(posedge tb_clk);
check_out(msg_pkt_data[0], {3'd0,tb_byte_out}, "data 0");
tb_readaddr = 4'd1;
@(posedge tb_clk);
check_out(msg_pkt_data[1], {3'd0,tb_byte_out}, "data 1");
tb_readaddr = 4'd2;
@(posedge tb_clk);
check_out(msg_pkt_data[2], {3'd0,tb_byte_out}, "data 2");
tb_readaddr = 4'd3;
@(posedge tb_clk);
check_out(msg_pkt_data[3], {3'd0,tb_byte_out}, "data 3");
tb_readaddr = 4'd4;
@(posedge tb_clk);
check_out(msg_pkt_data[4], {3'd0,tb_byte_out}, "data 4");
tb_readaddr = 4'd5;
@(posedge tb_clk);
check_out(msg_pkt_data[5], {3'd0,tb_byte_out}, "data 5");
tb_readaddr = 4'd6;
@(posedge tb_clk);
check_out(msg_pkt_data[6], {3'd0,tb_byte_out}, "data 6");
tb_readaddr = 4'd7;
@(posedge tb_clk);
check_out(msg_pkt_data[7], {3'd0,tb_byte_out}, "data 7");
#(STROBE_PERIOD * 11);
// ************************************************************************
// Test Case 6: long bitstream + 1.5% faster
// ************************************************************************
tb_test_num = tb_test_num + 1;
tb_test_case = "long bitstream + 1.5% faster";
lag = STROBE_PERIOD * 0.015;
lag = -lag;
reset_dut();
msg_msg_id = 11'b10110010110;
msg_pkt_size = 4'd8;
msg_pkt_data[0] = 8'b10101100;
msg_pkt_data[1] = 8'b10101101;
msg_pkt_data[2] = 8'b10101001;
msg_pkt_data[3] = 8'b10001101;
msg_pkt_data[4] = 8'b10100101;
msg_pkt_data[5] = 8'b10101111;
msg_pkt_data[6] = 8'b00111101;
msg_pkt_data[7] = 8'b11101101;
msg_CRC = 15'b001100110011111;
construct_pkt(msg_msg_id, msg_pkt_size, msg_pkt_data, msg_CRC);
RXstream(bitstream, bitstream.size());
check_out(msg_msg_id, tb_CAN_ID, "CAN ID");
tb_readaddr = 4'd0;
@(posedge tb_clk);
check_out(msg_pkt_data[0], {3'd0,tb_byte_out}, "data 0");
tb_readaddr = 4'd1;
@(posedge tb_clk);
check_out(msg_pkt_data[1], {3'd0,tb_byte_out}, "data 1");
tb_readaddr = 4'd2;
@(posedge tb_clk);
check_out(msg_pkt_data[2], {3'd0,tb_byte_out}, "data 2");
tb_readaddr = 4'd3;
@(posedge tb_clk);
check_out(msg_pkt_data[3], {3'd0,tb_byte_out}, "data 3");
tb_readaddr = 4'd4;
@(posedge tb_clk);
check_out(msg_pkt_data[4], {3'd0,tb_byte_out}, "data 4");
tb_readaddr = 4'd5;
@(posedge tb_clk);
check_out(msg_pkt_data[5], {3'd0,tb_byte_out}, "data 5");
tb_readaddr = 4'd6;
@(posedge tb_clk);
check_out(msg_pkt_data[6], {3'd0,tb_byte_out}, "data 6");
tb_readaddr = 4'd7;
@(posedge tb_clk);
check_out(msg_pkt_data[7], {3'd0,tb_byte_out}, "data 7");
#(STROBE_PERIOD * 11);
end
endmodule