|
|||
Design of SystemVerilog Assertion IP
by E. Cerny1, J. Bergeron2, M. K. Thottasseri3 and T. Anderson4, Synopsys, Inc.
1Marlborough, USA; 2Ottawa, Canada; 3Bangalore, India; 4Mountain View, USA Abstract: SystemVerilog provides an effective means for designing assertion-based Verification IP and integrating it with a testbench. This paper explores guidelines for designing such IP within the Synopsys Verification Methodology. INTRODUCTION Concurrent properties (assert, cover, and assume) [SVA] are an integral part of SystemVerilog [LRM]. They provide an effective way to improve observability and localization of design errors by placing functional checks at critical points inside design blocks and on internal and external interfaces. When an assertion fails during simulation, the cause of the reported error can be more easily identified than by observing external design-under-test (DUT) outputs. Assertions can also be proven or falsified using formal tools, thereby increasing confidence in the quality of the design. In addition, property coverage provides information on how well the design has been functionally exercised during simulation. Still uncovered properties may serve as search goals to formal tools that can generate test sequences to stimulate the design and reach that goal. Using these test sequences in simulation (with the assertions enabled) helps to bring verification to completion. Assertion and coverage properties on internal signals and interfaces of design blocks should ideally be inserted by the designers because they are the ones with intimate knowledge of the implementation details. Such assertions also have the role of “active” comments that document the implementation. Assertion and coverage properties on external interfaces of the design are usually provided by the verification engineers. They reflect the function rather than the implementation of the design. Interface protocols are generally the main target of assertion-based checks. The end-to-end behavior is often more convenient to verify using testbench code that may involve score-boarding techniques (possibly supplemented with assertions). Regardless where deployed, assertions and coverage for typical behaviors can be packaged in modules or interfaces for reuse as part of a basic SystemVerilog Assertion-based checker library such as the one provided with the Synopsys VCS simulator. These checkers deal with lower-level generic properties that can be seen in many designs, including various handshakes, mutual exclusion, basic arbitration, time separation of signal transitions, and signal validity windows. Checkers for standard protocols such as PCI Express, AMBA AHB, and MII/MAC each encompass a complete set of checks and coverage points for the protocol. These are packaged as reusable SystemVerilog Assertion IP, preferably in one or more SystemVerilog interfaces. The instantiation of the checker within the external DUT interface as recommended in the SystemVerilog Verification Methodology Manual [VMM] facilitates the observation of the DUT and interactions with the testbench program. In this paper, we discuss guidelines for implementing SystemVerilog Assertion IP. There are existing guidelines for constructing similar IP using other temporal languages such as OpenVera Assertions [OVA]. However, only SystemVerilog provides complex data types and excellent methods of interaction between assertions, the design, and the testbench. The architecture of checker IP can thus benefit from assertion and coverage control, feedback to the testbench, and uniform reporting. It is essential that the IP be reusable with formal and hybrid formal tools (such as Magellan), both as assertions on the behavior of the DUT and as assumptions (constraints) on the environment around the DUT. This supports the “assume-guarantee” method of formal analysis. As shown in Fig. 1, properties on interface signals are used as assertions to verify the block on one side of the interface and as assumptions to verify the block on the other side. Fig. 1 Use of assertions and assumptions In this example, an interrupt signal running from Block A to Block B can be asserted no more frequently that every five clock cycles. When verifying Block A in formal analysis (or in simulation), this rule is treated as an assertion to guarantee that the block asserts the interrupt signal correctly. However, Block B is verified formally subject to the assumption that this rule is met and that the interrupt signal behaves correctly. Requirements The development of reusable SystemVerilog Assertion IP must take into account many factors, among them:
GUIDELINES Encapsulation The choices are module, interface, or program. We recommend encapsulation in one or more interface constructs (as in Fig. 2) for the following reasons:
Global controls, such as the overall enabling of assertions, coverage, and simulation-only properties, are implemented by macro symbols and using `ifdef - `endif constructs. In the Synopsys checker library, we define the following macro symbols for all checkers: ASSERT_ON : includes assertions COVER_ON : includes cover statements ASSERT_GLOBAL_RESET : applies a reset to all checkers, consistent with the Open Verification Library [OVL] SVA_CHECKER_FORMAL : excludes non-synthesizable assertions Configuration that is specific to an instance of a checker must be selectable using interface parameters. These include variable sizing, detailed property selection, assume vs. assert, etc. The parameters are used in conditional and loop statements within generate blocks. We use the following parameters in all checkers: severity_level : reaction to assertion failures category : classification of the checker coverage_level : selection of coverage items assert_name : checker name no_next_fail : block next failures after a failure msg : user text message assume : select assume rather than assert The example in Fig. 2 illustrates the encapsulation and parameters of a Media Independent Interface [MII] checker. The attribute (* sva_checker *) identifies this interface to be a checker. This allows tools to treat it in the appropriate way for reporting purposes. The parameter mii_TX_max_retries specifies the maximum number of retransmissions that the DUT is to support before dropping the frame. The parameter coverage_level allows the user to enable individual cover points in addition to the global control using the macro COVER_ON. (* sva_checker *) interface mii_sva_checker (reset_n, TX_CLK, TX_EN, TX_ER, TXD, COL, CRS, RX_CLK, RX_DV, RX_ER, RXD, TX_FrameError, RX_FrameError, TX_FrameCover, RX_FrameCover); parameter int severity_level = 0; parameter int category = 0; parameter msg = ""; parameter bit no_next_fail = 1; parameter int coverage_level = -1; parameter bit mii_TX_assume = 1'b0; parameter bit mii_RX_assume = 1'b0; parameter int mii_TX_max_retries = 10; localparam assert_name = "mii_sva_checker"; input logic reset_n, TX_CLK, TX_EN, TX_ER; input logic COL, CRS, RX_CLK, RX_DV, RX_ER; input logic [3:0] TXD, RXD; output mii_sva_error_size_type TX_FrameError, RX_FrameError, TX_FrameCover, RX_FrameCover; . . . endinterface : mii_sva_checker Fig. 2 MII Checker interface and parameters Assertions and Assumptions Encapsulation by signal direction: If the protocol to be verified is multi-agent, for example multiple masters and slaves on an ARM AHB bus, we recommend encapsulating the checker of each agent type into a separate interface. Each such checker verifies the outputs of the agent, while using the inputs in the enabling conditions of the assertions. This has two advantages:
Use in random simulation: If the checker is used as assumptions (constraints) on the DUT environment in hybrid formal tools, the properties should not refer only to past values of DUT input signals. This is because such tools employ random simulation constrained by these sequential assumptions, and constraining past values may lead to an empty solution space (a dead end) during the simulation. 1 Protocol hierarchy: If the protocol is layered, such as is the case for PCI Express, it is preferable to follow this layered structure in the checker as well. It makes it easier to write the checker and to use the layers independently when verifying protocol layers separately. This means that some aspects of data checking may require partial assembly of the data into buffers for checking a property. However, if the checker is used as constraints it is useful to change the direction of data flow in the checker so that the data property can constrain the values and then drive them under lower–level assumptions to the DUT input ports. A simplified version of such a checker is shown in the example at the end of this paper. Interaction with the Testbench Each checker must operate either as an independent monitor, for instance with formal tools, or as part of a testbench. In the latter case, the checker is part of a testbench monitor and it is not necessary to duplicate the verification of the interface protocol. However, the testbench monitor must be aware of failures detected by the assertion-based checker. The SystemVerilog language provides a number of mechanisms for this interaction. The following system is adopted in the SystemVerilog VMM methodology:
Additionally, upon failure of an assertion, the flags can be used to disable all other assertions and coverage until the testbench clears the flags. This feature is enabled using a parameter of the checker, for example no_next_fail in Fig. 2. Fig. 3 Ethernet core testbench and checkers A portion of the checker code is shown in Fig. 4: mii_sva_tx_error_type is an enumerated type that sets the corresponding bit of the failure in the TX_FrameError vector using the TX_SetFrameError task. In each TX property, the signal TX_resetting contains a reduction OR over all the error flags used within the disable iff construct in all assertions and coverage statements. Therefore, once any assertion fails, all other assertions that should evaluate afterwards are disabled until the flags are clear. wire TX_BlockFail = no_next_fail && Fig. 4 Disabling assertions by failures(|TX_FrameError); wire TX_resetting = TX_BlockFail || !reset_n; task TX_SetFrameError( input mii_sva_tx_error_type i); TX_FrameError = i | TX_FrameError; endtask : TX_SetFrameError . . . property mii_1_p(resetting); disable iff (resetting) COL |-> CRS; endproperty : mii_1_p mii_TX_1: assert property (mii_1_p(TX_resetting)) else begin sva_std_error(“failure explanation”); TX_SetFrameError(MII_TX_NO_CRS_W_COL); End . . . Reporting For easy interpretation of results and information management, it is important to maintain uniform reporting formats and controls. The SystemVerilog VMM introduces a class and methods for reporting all messages in a uniform way. The user can easily select severity levels, decide which messages should be passed to the user and which should be filtered out, and define the global format of all messages. Since SystemVerilog integrates testbench constructs (such as classes) and design constructs (such as modules and interfaces), the log class is also used in checkers for outputting all messages. However, the checkers may also be used outside the SystemVerilog VMM context; so the Synopsys library also provides reporting using a $display statement, similar to the OVL approach. For this reason, message output is hidden in tasks in the sva_std_task.h file that is referenced with a `include in each checker. The task call sva_std_error(“failure explanation”) in 0 is an example of using this approach. The default structure of the task is to report via the VMM log is shown in Fig. 5, but the user can edit the file and change it to another form, such as an OVL-style message. vmm_log log_inst = // inst. in checker Fig. 5 VMM log instance and error reporting tasknew("mii_sva_checker", $psprintf("%m")); task sva_std_error( input string err_msg = "");//user message vmm_error(log_inst, $psprintf("Failure : category : %0d %s", category, err_msg)); endtask Coverage Coverage in the checkers can be collected using cover property, covergroup, or a combination thereof. Furthermore, sampling in a covergroup can be triggered by a SystemVerilog sequence or by an event sent from the action block of a cover property. We discourage using assert property for functional coverage because it has a different intent and provides a different kind of information. Cover properties are generally useful to detect the occurrence of specific sequences of events, but are a poor mechanism for covering data or delay values (although it can be done within generate loops in a rather inefficient way). For example, to detect (cover) the fact that a collision occurred on the last nibble of an MII TX frame, one can use the cover property in Fig. 6. mii_TX_cov_COL_last: cover property ( @(posedge TX_CLK) 1’b1 ##1 ((!TX_resetting && TX_EN) throughout ($rose(TX_EN) ##0 COL[->1])) ##1 !TX_EN ) ; Fig. 6 MII TX coverage on collision at last nibble However, covering different lengths of a frame is better achieved using a cover group as shown in 0. In this code, cnt is a local variable that counts the number of nibbles in the frame, and store_TX_Lngth is a task that converts the count to octets and stores it in a static variable TX_FrameLngth. This variable is sampled by the covergroup instance when the sequence frame_length_s is triggered. int TX_FrameLngth = 0; task store_TX_Lngth(int x); TX_FrameLngth = x; endtask : store_TX_Lngth covergroup length_cg @frame_length_s; coverpoint TX_FrameLngth; option.per_instance = 1; type_option.strobe = 1; endgroup : length_cg sequence frame_length_s; int cnt; @(posedge TX_CLK) 1’b1 ##1 ( (not_resetting && !COL) throughout ($rose(TX_EN, cnt=1) ##0 (TX_EN, cnt++)[*0:$]) ) ##1 (!TX_EN, store_TX_Lngth(cnt)) endsequence : frame_length_s length_cg mii_TX_frame_length = new (); Fig. 7 Coverage of MII TX frame lengths CHECKER EXAMPLE The example in Fig. 8 is a fragment of a hierarchical checker showing how layers of a protocol can be implemented as layers in the checker. The checker can be used both as assertions and assumptions (constraints). // interface linking PL and DL layers Fig. 8 Hierarchical protocol checker// assembles / disassembles “packets” // to / from DL layer interface PL_DL_link(rst, clk, ack, data ); parameter bit ack_assume = 0, req_assume = 0; input logic rst, clk, ack; inout [7:0] data; // variables for the DL layer logic [1:0][7:0] packet; logic cnt = 0; // only 2 octets default clocking p_clk @(posedge clk); // count 2 data octets always @p_clk cnt <= rst ? 0 : p_clk.ack ? !cnt : cnt; generate if (ack_assume) begin : drive // disassemble packet into octets assign data = rst ? 0 : ack ? (cnt ? packet[1] : packet[0]) : 0; // stable packet during disassembly assume_data_2: assume property ( disable iff (rst) (1’b1 ##1 !$fell(cnt)) |-> $stable(packet) ); end : drive else begin : verify // assertion on data, assemble packet always @p_clk packet[cnt] <= p_clk.rst ? 0 : p_clk.ack ? p_clk.data : packet[cnt]; end : verify endgenerate endinterface // DL layer checker with properties of // “data link” layer interface DL_checker parameter bit ack_assume = 0; ( PL_DL_link PL_DL ); property p_data_1; @(posedge PL_DL.p_clk) disable iff (PL_DL.rst) (1’b1 ##1 $fell(PL_DL.cnt)) |-> (data_property); endproperty generate if (ack_assume) begin : drive // assumption on data adt1: assume property (p_data_1); end : drive else begin : verify // assertion on data adt1: assert property (p_data_1) else checker_error(“data_property failed”); end : verify endgenerate endinterface interface MultilevelChecker ( rst, clk, req, ack, data ); parameter bit ack_assume = 0; input logic rst, clk, req, ack; inout [7:0] data; // properties of req property p_req1; // stable req @(posedge clk) disable iff (rst) $rose(req) |-> req[*1:$] ##0 ack; endproperty property p_req2; // req Return-to-Zero @(posedge clk) disable iff (rst) ack |-> ##1 ((!req)[*1:5]); endproperty // properties of ack property p_ack1; // pulse only @(posedge clk) disable iff (rst) ack |-> ##1 !ack; endproperty property p_ack2; // no ack w/o req @(posedge clk) disable iff (rst) !req |-> !ack; endproperty property p_ack3; // ack latency @(posedge clk) disable iff (rst) $rose(req) |-> ##[1:5] ack; endproperty generate if (ack_assume) begin : drive_ack aa1: assume property (p_ack1); aa2: assume property (p_ack2); aa3: assume property (p_ack3); end : drive_ack else begin : verify_ack aa1: assert property (p_ack1) else checker_error(“...”); aa2: assert property (p_ack2) ...; aa3: assert property (p_ack3) ...; end : verify_ack if (req_assume) begin : drive_req ar1: assume property (p_req1); ar2: assume property (p_req2); end : drive_req else begin : verify_req ar1: assert property (p_req1) ...; ar2: assert property (p_req2) ...; end : verify_req endgenerate // interface instance to DL layer PL_DL_link #(ack_assume) PL_DL_link_i(rst, clk, ack, data); // DL layer checker DL properties DL_checker #(ack_assume) DL_checker_i (PL_DL_link_i); endinterface Consider the following point-to-point protocol: Device Master asserts req and holds it until ack is received from Slave. At that point, data is placed by Slave on 8-bit wide data and is valid for one clock cycle—the duration of ack. req must stay high until and including ack is asserted, and then req must be deasserted for at least one cycle. ack cannot be asserted unless req is asserted. The data can be of any length and may, on each of its components, satisfy some higher-level properties. In addition, data should be made available to the testbench if so desired. For simplicity, we limit data to 2 octets and its value must satisfy some property data_property. The multi-layer protocol checker in Fig. 8 has the following structure. The top-level interface deals with the low-level protocol, called here PL (for physical layer), and contains assertions on req and ack. The top-level interface contains an instance of another interface that does the packet assembly into (for assertions on data) or disassembly (for assumptions on data) from a variable called packet. The choice is controlled by the parameters ack_assume and req_assume: ack_assume determines whether properties of ack and data should be assumed or asserted, while req_assume determines whether properties of req should be assumed or asserted. The interface PL_DL_link presents the two pertinent pieces of information (variables packet and cnt) to the next protocol layer, which is called here DL (for data-link layer). The top-level interface contains an instance of the data-link layer interface. The assembled data in packet and synchronization variable cnt can be accessed by the testbench by providing reference to the checker interface instance. The example in Fig. 8 is missing the log class instance, coverage statements (such as the req–ack latencies), control over enabling assertions and coverage, reset options, and other details. Nevertheless, it illustrates the flexibility of SystemVerilog for designing protocol checker IP. CONCLUSIONS We have outlined the main concerns when designing SystemVerilog Assertion IP, provided examples of guidelines governing the design of such IP, and illustrated a hierarchical checker by using a small example. Future work will include resolving IP protection and other issues related to the distribution of this kind of verification IP. References [LRM] SystemVerilog 3.1a Language Reference Manual, Accellera, May 2004. [MII] IEEE Standard 802.3-2002, IEEE, March 2002. [OVA] E. Cerny, S. Dudani, “Authoring Assertion IP using OpenVera Assertion Language,” Proceedings of the International Workshop on IP-Based System-on-Chip Design, October 2002. [OVL] Open Verification Library Assertion Monitor Reference Manual, Accellera, June 2003. [SVA] T. Fitzpatrick, “Assertions in SystemVerilog: A Unified Language for More Efficient Verification,” http://www.synopsys.com/products/simulation/assert_sverilog_wp.pdf, October 2003. [VMM] J. Bergeron, E. Cerny, A. Hunter, A. Nightingale, SystemVerilog Verification Methodology Manual, to be published in 2005. 1 A tool may be able to handle constraints over past values, but possibly at the cost of reduced performance. |
Home | Feedback | Register | Site Map |
All material on this site Copyright © 2017 Design And Reuse S.A. All rights reserved. |