diff --git a/include/obi/typedef.svh b/include/obi/typedef.svh index 9bbe65a..c15f375 100644 --- a/include/obi/typedef.svh +++ b/include/obi/typedef.svh @@ -7,14 +7,16 @@ `ifndef OBI_TYPEDEF_SVH `define OBI_TYPEDEF_SVH +`define OBI_TYPEDEF_MIN_WIDTH(WIDTH) ((WIDTH) > 0 ? (WIDTH) : 1) + `define OBI_TYPEDEF_A_CHAN_T(a_chan_t, ADDR_WIDTH, DATA_WIDTH, ID_WIDTH, a_optional_t) \ typedef struct packed { \ - logic [ ADDR_WIDTH-1:0] addr; \ - logic we; \ - logic [DATA_WIDTH/8-1:0] be; \ - logic [ DATA_WIDTH-1:0] wdata; \ - logic [ ID_WIDTH-1:0] aid; \ - a_optional_t a_optional; \ + logic [ ADDR_WIDTH-1:0] addr; \ + logic we; \ + logic [ DATA_WIDTH/8-1:0] be; \ + logic [ DATA_WIDTH-1:0] wdata; \ + logic [`OBI_TYPEDEF_MIN_WIDTH(ID_WIDTH)-1:0] aid; \ + a_optional_t a_optional; \ } a_chan_t; `define OBI_TYPEDEF_TYPE_A_CHAN_T(a_chan_t, addr_t, data_t, strb_t, id_t, a_optional_t) \ @@ -37,22 +39,22 @@ `define OBI_TYPEDEF_ALL_A_OPTIONAL(a_optional_t, AUSER_WIDTH, WUSER_WIDTH, MID_WIDTH, ACHK_WIDTH) \ typedef struct packed { \ - logic [ AUSER_WIDTH-1:0] auser; \ - logic [ WUSER_WIDTH-1:0] wuser; \ - obi_pkg::atop_t atop; \ - obi_pkg::memtype_t memtype; \ - logic [ MID_WIDTH-1:0] mid; \ - obi_pkg::prot_t prot; \ - logic dbg; \ - logic [ ACHK_WIDTH-1:0] achk; \ + logic [`OBI_TYPEDEF_MIN_WIDTH(AUSER_WIDTH)-1:0] auser; \ + logic [`OBI_TYPEDEF_MIN_WIDTH(WUSER_WIDTH)-1:0] wuser; \ + obi_pkg::atop_t atop; \ + obi_pkg::memtype_t memtype; \ + logic [ `OBI_TYPEDEF_MIN_WIDTH(MID_WIDTH)-1:0] mid; \ + obi_pkg::prot_t prot; \ + logic dbg; \ + logic [ `OBI_TYPEDEF_MIN_WIDTH(ACHK_WIDTH)-1:0] achk; \ } a_optional_t; `define OBI_TYPEDEF_R_CHAN_T(r_chan_t, RDATA_WIDTH, ID_WIDTH, r_optional_t) \ typedef struct packed { \ - logic [RDATA_WIDTH-1:0] rdata; \ - logic [ ID_WIDTH-1:0] rid; \ - logic err; \ - r_optional_t r_optional; \ + logic [ RDATA_WIDTH-1:0] rdata; \ + logic [`OBI_TYPEDEF_MIN_WIDTH(ID_WIDTH)-1:0] rid; \ + logic err; \ + r_optional_t r_optional; \ } r_chan_t; `define OBI_TYPEDEF_TYPE_R_CHAN_T(r_chan_t, data_t, id_t, r_optional_t) \ @@ -68,9 +70,9 @@ `define OBI_TYPEDEF_ALL_R_OPTIONAL(r_optional_t, RUSER_WIDTH, RCHK_WIDTH) \ typedef struct packed { \ - logic [RUSER_WIDTH-1:0] ruser; \ - logic exokay; \ - logic [ RCHK_WIDTH-1:0] rchk; \ + logic [`OBI_TYPEDEF_MIN_WIDTH(RUSER_WIDTH)-1:0] ruser; \ + logic exokay; \ + logic [ `OBI_TYPEDEF_MIN_WIDTH(RCHK_WIDTH)-1:0] rchk; \ } r_optional_t; `define OBI_TYPEDEF_DEFAULT_REQ_T(req_t, a_chan_t) \ diff --git a/src/test/obi_test.sv b/src/test/obi_test.sv index 90a49ef..cdd86b3 100644 --- a/src/test/obi_test.sv +++ b/src/test/obi_test.sv @@ -50,6 +50,7 @@ package obi_test; obi.rvalidpar <= '1; obi.rdata <= '0; obi.rid <= '0; + obi.err <= '0; obi.r_optional <= '0; endfunction @@ -93,12 +94,14 @@ package obi_test; task send_r ( input logic [ObiCfg.DataWidth-1:0] rdata, input logic [ ObiCfg.IdWidth-1:0] rid, + input logic err, input obi_r_optional_t r_optional ); obi.rvalid <= #TA 1'b1; obi.rvalidpar <= #TA 1'b0; obi.rdata <= #TA rdata; obi.rid <= #TA rid; + obi.err <= #TA err; obi.r_optional <= #TA r_optional; cycle_start(); if (ObiCfg.UseRReady) begin @@ -109,6 +112,7 @@ package obi_test; obi.rvalidpar <= #TA 1'b1; obi.rdata <= #TA '0; obi.rid <= #TA '0; + obi.err <= #TA '0; obi.r_optional <= #TA '0; endtask @@ -188,7 +192,13 @@ package obi_test; string name; obi_driver_t drv; addr_t a_queue[$]; - logic r_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); @@ -218,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; @@ -225,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); @@ -248,10 +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(); - rand_wait(RMinWaitCycles, RMaxWaitCycles); + + 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 @@ -298,6 +487,8 @@ package obi_test; parameter obi_cfg_t ObiCfg = ObiDefaultConfig, parameter type obi_a_optional_t = logic, parameter type obi_r_optional_t = logic, + // Response Settings + parameter bit RandResp = 0, // Stimuli Parameters parameter time TA = 2ns, parameter time TT = 8ns, @@ -322,7 +513,7 @@ package obi_test; obi_driver_t drv; addr_t a_queue[$]; id_t id_queue[$]; - logic r_queue[$]; + logic is_excl_queue[$]; function new( virtual OBI_BUS_DV #( @@ -360,10 +551,18 @@ 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 logic is_excl; + rand_wait(AMinWaitCycles, AMaxWaitCycles); this.drv.recv_a(a_addr, a_we, a_be, a_wdata, a_aid, a_optional); this.a_queue.push_back(a_addr); this.id_queue.push_back(a_aid); + if (ObiCfg.OptionalCfg.UseAtop) begin + is_excl = a_optional.atop inside {obi_pkg::ATOPLR, obi_pkg::ATOPSC}; + end else begin + is_excl = 1'b0; + end + this.is_excl_queue.push_back(is_excl); end endtask @@ -371,18 +570,40 @@ package obi_test; forever begin automatic logic rand_success; automatic addr_t a_addr; + automatic logic is_excl; automatic logic [ObiCfg.DataWidth-1:0] r_rdata; automatic logic [ ObiCfg.IdWidth-1:0] r_rid; + automatic logic r_err; automatic obi_r_optional_t r_optional; - wait (a_queue.size() > 0); - wait (id_queue.size() > 0); + wait (this.a_queue.size() > 0); + wait (this.id_queue.size() > 0); + wait (this.is_excl_queue.size() > 0); a_addr = this.a_queue.pop_front(); r_rid = this.id_queue.pop_front(); + is_excl = this.is_excl_queue.pop_front(); + + r_err = RandResp ? ($urandom() % 2) : 1'b0; rand_success = std::randomize(r_rdata); assert(rand_success); rand_success = std::randomize(r_optional); assert(rand_success); - this.drv.send_r(r_rdata, r_rid, r_optional); + + if (ObiCfg.OptionalCfg.UseAtop) begin + // Requirement R13.1 and R13.2: For a LR.W/D related transaction success shall be signaled + // via exokay [...]. + if (!is_excl) begin + // Requirement R13.3: Other [non-exclusive] transactions shall signal exokay = 0. + r_optional.exokay = 1'b0; + end else if (r_err) begin + // Requirement R13.4: The exokay and err signals shall [not issue {err, exokay} = 2'b11]. + r_optional.exokay = 1'b0; + end else begin + r_optional.exokay = RandResp ? ($urandom() % 2) : 1'b1; + end + end + + rand_wait(RMinWaitCycles, RMaxWaitCycles); + this.drv.send_r(r_rdata, r_rid, r_err, r_optional); end endtask