PCS-2/ver/scoreboard.sv
2023-12-03 19:31:49 +01:00

196 lines
6.6 KiB
Systemverilog

// signal_driver.sv: Driver of signal interface
// Copyright (C) 2019 FIT BUT
// Author(s): Lukas Kekely <ikekely@fit.vutbr.cz>
//
// SPDX-License-Identifier: BSD-3-Clause
// Exact match filtering model extended for hashing tables using Jenkins hash
class JenkinsHashExactMatch extends ExactMatch #(KEY_WIDTH, DATA_WIDTH);
`define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
`define mix(a,b,c) \
a = a-c; a = a^`rot(c, 4); c = c+b; \
b = b-a; b = b^`rot(a, 6); a = a+c; \
c = c-b; c = c^`rot(b, 8); b = b+a; \
a = a-c; a = a^`rot(c,16); c = c+b; \
b = b-a; b = b^`rot(a,19); a = a+c; \
c = c-b; c = c^`rot(b, 4); b = b+a;
`define final(a,b,c) \
c = c^b; c = c-`rot(b,14); \
a = a^c; a = a-`rot(c,11); \
b = b^a; b = b-`rot(a,25); \
c = c^b; c = c-`rot(b,16); \
a = a^c; a = a-`rot(c,4); \
b = b^a; b = b-`rot(a,14); \
c = c^b; c = c-`rot(b,24);
function int unsigned jenkins_hash_base(int unsigned k[], int unsigned length, int unsigned initval);
int unsigned a,b,c,kk;
/* Set up the internal state */
a = 32'hdeadbeef + (length<<2) + initval; b = a; c = a;
/*------------------------------------------------- handle most of the key */
kk = 0;
while (length > 3) begin
a += k[kk+0];
b += k[kk+1];
c += k[kk+2];
`mix(a,b,c)
length = length - 3;
kk = kk + 3;
end
/*------------------------------------------- handle the last 3 uint32_t's */
/* all the case statements fall through */
if(length >= 3) c = c + k[kk+2];
if(length >= 2) b = b + k[kk+1];
if(length >= 1) a = a + k[kk+0];
/* case 0: nothing left to add */
`final(a,b,c)
/*------------------------------------------------------ report the result */
return c;
endfunction
function int unsigned jenkins_hash(bit [RULE_WIDTH-1 : 0] r, int unsigned initval);
int unsigned k[KEY_WORDS];
for(int i = 0; i<KEY_WORDS-1; i++)
k[i] = r[32*i+DATA_WIDTH+1 +: 32];
k[KEY_WORDS-1] = r[RULE_WIDTH-1 : (KEY_WORDS-1)*32+DATA_WIDTH+1];
return jenkins_hash_base(k, KEY_WORDS, initval) % TABLE_SIZE;
endfunction
task random_fill(int N, SignalDriver #(CONFIG_WIDTH) cd, ref int rules);
bit [RULE_WIDTH-1 : 0] memory[TABLES][TABLE_SIZE], rule;
bit [CONFIG_WIDTH-1 : 0] hw_record;
int unsigned t, s, hash;
super.random_fill(N); // random matching rules generation
for(t=0; t<TABLES; t++) // initialize tables to empty rules
for(s=0; s<TABLE_SIZE; s++)
memory[t][s] = 1;
for(int i=0; i<keys.size(); i++) begin // spread of rules into hash tables
rule[KEY_WIDTH+DATA_WIDTH : DATA_WIDTH+1] = get_key(i);
rule[DATA_WIDTH : 1] = get_data(i);
rule[0] = 0;
for(t = 0; t<TABLES; t++) begin
hash = jenkins_hash(rule, t+1);
if(memory[t][hash][0]) begin// empty record
memory[t][hash] = rule;
break;
end
end
if(t == TABLES) begin
assert(remove(rule[RULE_WIDTH-1 : RULE_WIDTH-KEY_WIDTH]));
i--;
end else
rules++;
end
for(t=0; t<TABLES; t++) // fill DUT tables with rules through CONFIG interface
for(s=0; s<TABLE_SIZE; s++) begin
hw_record[CONFIG_WIDTH-1 : CONFIG_WIDTH-$clog2(TABLES)] = t;
hw_record[CONFIG_WIDTH-$clog2(TABLES)-1 : CONFIG_WIDTH-$clog2(TABLES)-$clog2(TABLE_SIZE)] = s;
hw_record[RULE_WIDTH-1 : 0] = memory[t][s];
cd.put(hw_record);
end
endtask
endclass
// Main scoreboard implementation
class Scoreboard;
protected bit enabled;
protected mailbox #(bit [KEY_WIDTH-1 : 0]) mbox;
protected SignalDriver #(KEY_WIDTH) driver;
protected SignalMonitor #(RULE_WIDTH) monitor;
protected ExactMatch #(KEY_WIDTH,DATA_WIDTH) model;
protected int transactions, matching, nonmatching;
function new(SignalDriver #(KEY_WIDTH) d, SignalMonitor #(RULE_WIDTH) m, ExactMatch #(KEY_WIDTH,DATA_WIDTH) mod);
enabled = 0;
mbox = new();
driver = d;
monitor = m;
model = mod;
transactions = 0;
matching = 0;
nonmatching = 0;
endfunction
function void start();
enabled = 1;
fork
run();
join_none;
endfunction
function bit running();
return enabled;
endfunction
task run();
bit [KEY_WIDTH-1 : 0] dut_key, model_key;
bit [RULE_WIDTH-1 : 0] dut_raw;
bit [DATA_WIDTH-1 : 0] dut_data, model_data;
bit dut_found, model_found;
while(mbox.try_get(model_key) != 0) begin
monitor.get(dut_raw);
dut_key = dut_raw[KEY_WIDTH+DATA_WIDTH : DATA_WIDTH+1];
dut_data = dut_raw[DATA_WIDTH : 1];
dut_found = dut_raw[0];
model_found = model.match(model_key, model_data);
if(model_key != dut_key || model_found != dut_found || (model_found == 1 && model_data != dut_data)) begin
$write("Data mismatch detected on transaction #%0d! Details:\n", transactions+1);
$write(" MODEL: matching key 0x%0x results in ", model_key);
if(model_found)
$write("FOUND rule with data 0x%0x\n", model_data);
else
$write("NOT FOUND\n");
$write(" DUT: matching key 0x%0x results in ", dut_key);
if(dut_found)
$write("FOUND rule with data 0x%0x\n", dut_data);
else
$write("NOT FOUND\n");
$write(" REASON: ");
if(model_key != dut_key)
$write("Different keys paired together (possible desynchronization in DUT)!");
else if(model_found != dut_found)
$write("Different FOUND flag!\n");
else if(model_data != dut_data)
$write("Different value of data (found rule identificator)!\n");
else
$write("Unknown error!\n");
display();
$write("\n\nERROR: Verification data mismatch! Stopping ...\n");
$stop();
end
transactions++;
if(model_found)
matching++;
else
nonmatching++;
end
enabled = 0;
endtask
task put(bit [KEY_WIDTH-1 : 0] k);
driver.put(k);
mbox.put(k);
if(!enabled)
start();
endtask
task display();
$write("\n\n============================================================\n");
$write("= Verification scoreboard\n");
$write("============================================================\n");
$write(" Waiting input keys: %08d\n", mbox.num());
$write(" Waiting output data: %08d\n", monitor.num());
$write("Processed transactions: %08d (%0d keys matched & %0d did not)\n", transactions, matching, nonmatching);
endtask
endclass