Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 193 additions & 7 deletions src/test/obi_test.sv
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@ package obi_test;
parameter int unsigned RMinWaitCycles = 0,
parameter int unsigned RMaxWaitCycles = 100
);

localparam int unsigned IdCount = 1 << ObiCfg.IdWidth;

typedef obi_test::obi_driver #(
.ObiCfg ( ObiCfg ),
.obi_a_optional_t ( obi_a_optional_t ),
Expand All @@ -193,6 +196,13 @@ package obi_test;
obi_driver_t drv;
addr_t a_queue[$];

// Scoreboard for id usage tracking. Values indicate:
// 0: Id is not used by any outstanding request,
// >0: Id is used by that many non-atomic requests,
// -1: Id is used by an atomic request.
int aid_atop_scoreboard[IdCount];
std::semaphore atop_sb_sem;

function new(
virtual OBI_BUS_DV #(
.OBI_CFG ( ObiCfg ),
Expand All @@ -203,12 +213,34 @@ package obi_test;
);
this.drv = new(obi);
this.name = name;
this.aid_atop_scoreboard = '{default: '0};
this.atop_sb_sem = new(1);
assert(ObiCfg.AddrWidth != 0) else $fatal(1, "ObiCfg.AddrWidth must be non-zero!");
assert(ObiCfg.DataWidth != 0) else $fatal(1, "ObiCfg.DataWidth must be non-zero!");
endfunction

function void reset();
drv.reset_manager();
this.a_queue.delete();
this.aid_atop_scoreboard = '{default: '0};
endfunction

function bit atop_id_available();
for (int unsigned i = 0; i < IdCount; i++) begin
if (this.aid_atop_scoreboard[i] == 0) begin
return 1'b1;
end
end
return 1'b0;
endfunction

function bit non_atop_id_available();
for (int unsigned i = 0; i < IdCount; i++) begin
if (this.aid_atop_scoreboard[i] >= 0) begin
return 1'b1;
end
end
return 1'b0;
endfunction

task automatic rand_wait(input int unsigned min, input int unsigned max);
Expand All @@ -221,23 +253,166 @@ package obi_test;
repeat (cycles) @(posedge this.drv.obi.clk_i);
endtask

task automatic legalize_id(input obi_a_optional_t a_optional,
output logic [ObiCfg.IdWidth-1:0] aid);
automatic bit is_atop;
if (ObiCfg.OptionalCfg.UseAtop) begin
is_atop = (a_optional.atop != obi_pkg::ATOPNONE);
end else begin
is_atop = 1'b0;
end

// Requirement R-12: An OBI manager shall prevent initiating a transaction if it would
// lead to multiple outstanding transactions with the same aid of which at least one
// transaction is an atomic transaction.
forever begin
this.atop_sb_sem.get();
if (is_atop) begin
if (!this.atop_id_available()) begin
// No legal ID available, wait until one becomes available
this.atop_sb_sem.put();
rand_wait(1, 1);
continue;
end

// Get a random available id
assert(std::randomize(aid) with {
this.aid_atop_scoreboard[aid] == 0;
});

// Mark aid used by atomic
this.aid_atop_scoreboard[aid] = -1;

end else begin
if (!this.non_atop_id_available()) begin
// No legal ID available, wait until one becomes available
this.atop_sb_sem.put();
rand_wait(1, 1);
continue;
end

// Get a random available id
assert(std::randomize(aid) with {
this.aid_atop_scoreboard[aid] != -1;
});

// Increment usage of aid for non-atomic
this.aid_atop_scoreboard[aid]++;
end
this.atop_sb_sem.put();
break;
end
endtask

task automatic send_as(input int unsigned n_reqs);
automatic addr_t a_addr;
automatic logic a_we;
automatic logic [ObiCfg.DataWidth/8-1:0] a_be;
automatic logic [ ObiCfg.DataWidth-1:0] a_wdata;
automatic logic [ ObiCfg.IdWidth-1:0] a_aid;
automatic obi_a_optional_t a_optional;
automatic int unsigned be_low, be_width;

repeat (n_reqs) begin
rand_wait(AMinWaitCycles, AMaxWaitCycles);

a_addr = addr_t'($urandom_range(MinAddr, MaxAddr));
a_we = $urandom() % 2;
a_be = $urandom() % (1 << (ObiCfg.DataWidth/8));
a_wdata = $urandom() % (1 << ObiCfg.DataWidth);
a_aid = $urandom() % (1 << ObiCfg.IdWidth);
a_optional = obi_a_optional_t'($urandom());
assert(std::randomize(a_we));
assert(std::randomize(a_wdata));
if (ObiCfg.BeFull) begin
// Requirement R-8: No restrictions on BE
assert(std::randomize(a_be));
// Back-calculate be_low and be_width
be_low = a_be == '0 ? 0 : $clog2(a_be & -a_be);
be_width = $clog2(a_be + 1) - be_low;
end else begin
// Requirement R-7:
// - At least one of the be bits shall be set to 1.
// - The 1’s in be shall be contiguous.
assert(std::randomize(be_low, be_width) with {
be_low < ObiCfg.DataWidth/8;
be_low + be_width <= ObiCfg.DataWidth/8;
be_width > 0;
});
a_be = ((1 << (be_width)) - 1) << be_low;
end
// Requirement R-9: If i is the index of the least signification bit in be that is 1,
// then the least significant addr bits shall be <= i.
assert(std::randomize(a_addr) with {
a_addr >= MinAddr;
a_addr <= MaxAddr;
a_addr[$clog2(ObiCfg.DataWidth/8)-1:0] <= be_low;
});

a_optional = 'x;
if (ObiCfg.OptionalCfg.UseAtop) begin
automatic obi_pkg::atop_t atop;
// Requirement R-11.3: The transaction associated with a LR.W shall have we = 0;
// the other atomic memory transactions (i.e. SC.W, AMO*) shall use we = 1.
assert(std::randomize(atop) with {
a_we -> atop dist {
ATOPSC, AMOSWAP, AMOADD, AMOXOR, AMOAND, AMOOR, AMOMIN,
AMOMAX, AMOMINU, AMOMAXU, ATOPNONE
};
!a_we ->atop dist {
ATOPLR, ATOPNONE
};
});
a_optional.atop = atop;
if (a_optional.atop != ATOPNONE) begin
// Requirement R-11.5: The byte enables be used in an atomic memory transaction shall
// indicate a word or double-word transfer as implied by the related *.W or *.D
// instruction.
if (ObiCfg.DataWidth == 32) begin
a_be = '1;
end else begin
assert(std::randomize(be_low, be_width) with {
be_width == 4 || be_width == 8;
be_low % be_width == 0;
be_low + be_width <= ObiCfg.DataWidth/8;
});
a_be = ((1 << (be_width)) - 1) << be_low;
end
// Requirement R-11.4: The address addr used in an atomic memory transaction shall be
// naturally aligned.
a_addr &= ~(be_width - 1);
end
end
if (ObiCfg.OptionalCfg.UseMemtype) begin
automatic obi_pkg::memtype_t memtype;
assert(std::randomize(memtype));
a_optional.memtype = memtype;
end
if (ObiCfg.OptionalCfg.UseProt) begin
automatic obi_pkg::prot_t prot;
assert(std::randomize(prot));
a_optional.prot = prot;
end
if (ObiCfg.OptionalCfg.UseDbg) begin
automatic logic dbg;
assert(std::randomize(dbg));
a_optional.dbg = dbg;
end
if (ObiCfg.OptionalCfg.MidWidth > 0) begin
automatic logic [$bits(a_optional.mid)-1:0] a_mid;
assert(std::randomize(a_mid));
a_optional.mid = a_mid;
end
if (ObiCfg.OptionalCfg.AUserWidth > 0) begin
automatic logic [$bits(a_optional.auser)-1:0] a_auser;
assert(std::randomize(a_auser));
a_optional.auser = a_auser;
end
if (ObiCfg.OptionalCfg.WUserWidth > 0) begin
automatic logic [$bits(a_optional.wuser)-1:0] a_wuser;
assert(std::randomize(a_wuser));
a_optional.wuser = a_wuser;
end
if (ObiCfg.Integrity && ObiCfg.OptionalCfg.AChkWidth > 0) begin
automatic logic [$bits(a_optional.achk)-1:0] achk;
assert(std::randomize(achk));
a_optional.achk = achk;
end
legalize_id(a_optional, a_aid);

this.a_queue.push_back(a_addr);
this.drv.send_a(a_addr, a_we, a_be, a_wdata, a_aid, a_optional);
Expand All @@ -251,13 +426,24 @@ package obi_test;
automatic logic r_err;
automatic obi_r_optional_t r_optional;
repeat (n_rsps) begin
wait (a_queue.size() > 0);
wait (this.a_queue.size() > 0);
a_addr = this.a_queue.pop_front();

if (ObiCfg.UseRReady) begin
rand_wait(RMinWaitCycles, RMaxWaitCycles);
end
drv.recv_r(r_rdata, r_rid, r_err, r_optional);

// Update atomics scoreboard
this.atop_sb_sem.get();
if (aid_atop_scoreboard[r_rid] > 0) begin
// Non-atomic response
aid_atop_scoreboard[r_rid]--;
end else begin
// Atomic response
aid_atop_scoreboard[r_rid] = 0;
end
this.atop_sb_sem.put();
end
endtask

Expand Down
Loading