I have a serial output of a verilog module I'd like to testbench using system-verilog.
The output, called 'SO' will output something like 8'hC6 given the correct serial input 'SI' with a value of say 8'h9A.
Is there an easy way to encode / decode serial IOs without having to explicitly describe each signal?
For example:
assert property @(posedge clk) $rose(EN) |-> ##[1:3] SI ##1 !SI[*2] ##1 SI[*2] ##1 !SI ##1 SI ##1 !SI
##[1:3] SO[*2] ##1 !SO[*3] ##1 SO[*2] ##1 !SO;
It looks like a jumbled mess and is barely readable. I'd very much like to just write
8'h9A ##[1:3] 8'hC6
but obviously this doesn't work. Any advice or examples would be more than welcome. Thanks in advance.
Try a sequence and refer to IEEE Std 1800-2012 section 16.10 (Local variables):
sequence seq_serial(logic signal, local logic [7:0] expected);
byte idx = 7;
(signal == expected[idx], idx--)[*8];
endsequence : seq_serial
asrt_si0x9A_so0xC6 : assert property ( @(posedge clk)
$rose(EN) |-> ##[1:3] seq_serial(SI, 8'h9A) ##[1:3] seq_serial(SO, 8'hC6) );
This is equivalent to the the assertion provided and is more readable.
Do note the local
keyword which will treat expected
as a variable rather then a reference and allows you to pass constant (e.g. 8'h9A
, 8'hC6
) and still allows you pas net references. See IEEE Std 1800-2012 section 16.8.2 (Local variable formal arguments in sequence declarations) for more.
Here is a simple test bench to prove the assertion. I'm driving SO
because I don't have a real DUT and I want to demonstrate both a pass & fail scenario.
bit EN, clk;
logic SI,SO;
logic [7:0] si_var, so_var;
initial forever #10ns clk++; // clock generator
default clocking cb @(posedge clk); output #1ns EN,SI,SO; endclocking : cb
initial begin : test_vector
si_var = 8'h9A;
so_var = 8'hC6;
##1 cb.EN <= 1;
##($urandom_range(2,0)); // rand delay
foreach(si_var[i]) ##1 cb.SI <= si_var[i];
##($urandom_range(2,0)); // rand delay
foreach(so_var[i]) ##1 cb.SO <= so_var[i];
##1 cb.EN <= 0;
/* Now make the assertion fail */
so_var = 8'hC7; // make fail
##3 cb.EN <= 1;
##($urandom_range(2,0)); // rand delay
foreach(si_var[i]) ##1 cb.SI <= si_var[i];
##($urandom_range(2,0)); // rand delay
foreach(so_var[i]) ##1 cb.SO <= so_var[i];
##1 cb.EN <= 0;
#10ns; // little delay before finish
$finish(2);
end : test_vector
You usually don't use assertions to describe checks on data items, but on control signals. What you need in this case is to collect your whole input stream into a 16bit vector, collect you whole output stream and check that what you got on the SO line matches what you're supposed to get (some transformation of what was on the SI line).
My SystemVerilog is rusty, but I'll give you a quick example of what I mean. Be aware that it's not compilable.
// collect input
always @(posedge clk) begin
if en == 1 begin
collect_input();
end
end
logic[7:0] si;
task collect_input();
for i from 0 to 7 begin
si[i] = SI; // take care of endianness here, might be si[7-i];
end
endtask
// collect output
...
logic[7:0] so;
...
collect_output();
for ...
// after collecting so, check that it's correct
if so != transform(si) begin
$error("wrong output data");
end
endtask
Hope it gives you an idea.