From df0274572ca93c6b7acc7a874049ba17f0e745c5 Mon Sep 17 00:00:00 2001 From: Luca Rufer Date: Tue, 30 Jun 2026 15:11:09 +0200 Subject: [PATCH 1/3] [Testing] Add spec compliant randomization for random manager --- src/test/obi_test.sv | 197 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 190 insertions(+), 7 deletions(-) diff --git a/src/test/obi_test.sv b/src/test/obi_test.sv index d0911ae..cdd86b3 100644 --- a/src/test/obi_test.sv +++ b/src/test/obi_test.sv @@ -193,6 +193,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[(1<= 0) begin + return 1'b1; + end + end + return 1'b0; endfunction task automatic rand_wait(input int unsigned min, input int unsigned max); @@ -221,6 +250,56 @@ 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; @@ -228,16 +307,109 @@ package obi_test; 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)); + assert(std::randomize(a_addr) with { + a_addr >= MinAddr; + a_addr <= MaxAddr; + }); + 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; + // 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; + }); + end + + 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); @@ -251,13 +423,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 From edb32f280b4d75b5ff60c6197914b3911c6feb3b Mon Sep 17 00:00:00 2001 From: Luca Rufer Date: Thu, 6 Aug 2026 17:01:33 +0200 Subject: [PATCH 2/3] [Testing] Random manager: apply R-9 also for BEFULL --- src/test/obi_test.sv | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/test/obi_test.sv b/src/test/obi_test.sv index cdd86b3..a7fe33e 100644 --- a/src/test/obi_test.sv +++ b/src/test/obi_test.sv @@ -317,10 +317,9 @@ package obi_test; if (ObiCfg.BeFull) begin // Requirement R-8: No restrictions on BE assert(std::randomize(a_be)); - assert(std::randomize(a_addr) with { - a_addr >= MinAddr; - a_addr <= MaxAddr; - }); + // 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. @@ -331,14 +330,14 @@ package obi_test; be_width > 0; }); a_be = ((1 << (be_width)) - 1) << be_low; - // 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; - }); 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 From 9f4c58ed6ab6e94a35491f2905d2c05b7767c8ef Mon Sep 17 00:00:00 2001 From: Luca Rufer Date: Tue, 11 Aug 2026 10:47:04 +0200 Subject: [PATCH 3/3] [Testing] Fix Linting --- src/test/obi_test.sv | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/test/obi_test.sv b/src/test/obi_test.sv index a7fe33e..9f6bb3e 100644 --- a/src/test/obi_test.sv +++ b/src/test/obi_test.sv @@ -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 ), @@ -197,7 +200,7 @@ package obi_test; // 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[(1<= 0) begin return 1'b1; end @@ -250,7 +253,8 @@ 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); + 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);