www.dbebooks.com - Free Books & magazines
Creating Assertion-Based IP
Series on Integrated Circuits and Systems Serie...
17 downloads
626 Views
2MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
www.dbebooks.com - Free Books & magazines
Creating Assertion-Based IP
Series on Integrated Circuits and Systems Series Editor:
Anantha Chandrakasan Massachusetts Institute of Technology Cambridge, Massachusetts
Creating Assertion-Based IP Harry D. Foster and Adam C. Krolnik ISBN 978-0-387-36641-8 Design for Manufacturability and Statistical Design: A Constructive Approach Michael Orshansky, Sani R. Nassif, and Duane Boning ISBN 978-0-387-30928-6 Low Power Methodology Manual: For System-on-Chip Design Michael Keating, David Flynn, Rob Aitken, Alan Gibbons, and Kaijian Shi ISBN 978-0-387-71818-7 Modern Circuit Placement: Best Practices and Results Gi-Joon Nam and Jason Cong ISBN 978-0-387-36837-5 CMOS Biotechnology Hakho Lee, Donhee Ham and Robert M. Westervelt ISBN 978-0-387-36836-8 SAT-Based Scalable Formal Verification Solutions Malay Ganai and Aarti Gupta ISBN 978-0-387-69166-4, 2007 Ultra-Low Voltage Nano-Scale Memories Kiyoo Itoh, Masashi Horiguchi and Hitoshi Tanaka ISBN 978-0-387-33398-4, 2007 Routing Congestion in VLSI Circuits: Estimation and Optimization Prashant Saxena, Rupesh S. Shelar, Sachin Sapatnekar ISBN 978-0-387-30037-5, 2007 Ultra-Low Power Wireless Technologies for Sensor Networks Brian Otis and Jan Rabaey ISBN 978-0-387-30930-9, 2007 Sub-Threshold Design for Ultra Low-Power Systems Alice Wang, Benton H. Calhoun and Anantha Chandrakasan ISBN 978-0-387-33515-5, 2006 High Performance Energy Efficient Microprocessor Design Vojin Oklibdzija and Ram Krishnamurthy (Eds.) ISBN 978-0-387-28594-8, 2006 Abstraction Refinement for Large Scale Model Checking Chao Wang, Gary D. Hachtel, and Fabio Somenzi ISBN 978-0-387-28594-2, 2006 A Practical Introduction to PSL Cindy Eisner and Dana Fisman ISBN 978-0-387-35313-5, 2006 Thermal and Power Management of Integrated Systems Arman Vassighi and Manoj Sachdev ISBN 978-0-387-25762-4, 2006 Continued after index
Harry D. Foster • Adam C. Krolnik
Creating Assertion-Based IP
Harry D. Foster Mentor Graphics Plano, TX USA
Adam C. Krolnik LSI Logic Corporation Allen, TX USA
Library of Congress Control Number: 2007937035 ISBN 978-0-387-36641-8
e-ISBN 978-0-387-68398-0
Printed on acid-free paper. © 2008 Springer Science+Business Media, LLC All rights reserved. This work may not be translated or copied in whole or in part without the written permission of the publisher (Springer Science+Business Media, LLC, 233 Spring Street, New York, NY 10013, USA), except for brief excerpts in connection with reviews or scholarly analysis. Use in connection with any form of information storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or hereafter developed is forbidden. The use in this publication of trade names, trademarks, service marks and similar terms, even if they are not identified as such, is not to be taken as an expression of opinion as to whether or not they are subject to proprietary rights. 9 8 7 6 5 4 3 2 1 springer.com
Dedications
Dedicated to the most wonderfully magical person in my life—Jeanne. And to Elliott & Stephanie, Lance, and Hannah. Always remember, in this world of automation, there is no substitute for thinking. -Harry Cindy, Seth, Nicholas, Sarah and Jesus the Christ. -Adam
Chapter 0
TABLE OF CONTENTS
CHAPTER 0
Chapter 1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1 Assertion-Based IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.1 Stakeholders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.1.2 Levels of abstraction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.1.3 Reuse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.2 Properties and assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.2.1 Languages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.2.2 Libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.2.3 Simulation vs. formal verification . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.3 Who should read this book? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.4 Book organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.5 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Chapter 2 Definitions and Terminology . . . . . . . . . . . . . . . . . . . 19 2.1 Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.1.1 Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.1.2 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.1.3 Interconnect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.1.4 Channels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.1.5 Analysis ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.2 Verification component description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.3 Verification component organization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.4 Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.5 Acronyms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 2.6 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Chapter 3 The Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3.1 Guiding principles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 3.2 Process steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 3.3 Assertion-based IP architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 3.3.1 Module-based assertion IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.3.2 SystemVerilog interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Table of Contents
vii
3.3.3 Analysis ports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 3.3.4 Interface-based assertion IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.4 Guidelines and conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.5 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Chapter 4 Bus-Based Design Example . . . . . . . . . . . . . . . . . . . . . 59 4.1 Bus-based design overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.2 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Chapter 5 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 5.1 Simple generic serial bus interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 5.1.1 Block diagram interface description . . . . . . . . . . . . . . . . . . . . . . . . . . 64 5.1.2 Overview description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 5.1.3 Natural language properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 5.1.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 5.1.5 Encapsulate properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 5.2 Simple generic nonpipelined bus interface . . . . . . . . . . . . . . . . . . . . . . . . . 75 5.2.1 Block diagram interface description . . . . . . . . . . . . . . . . . . . . . . . . . . 76 5.2.2 Overview description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 5.2.3 Natural language properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5.2.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5.2.5 Encapsulate properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 5.3 Simple generic pipelined bus interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 5.3.1 Block diagram interface definition . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 5.3.2 Overview description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 5.3.3 Natural language properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 5.3.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 5.3.5 Encapsulate properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 5.4 Interface monitor coverage example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 5.5 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Chapter 6 Arbiters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 6.1 Arbitrations schemes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 6.1.1 Fair arbitration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 6.1.2 Specific arbiter properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 6.1.3 Fixed priority . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 6.1.4 Credit-based weighted priority . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 6.1.5 Dynamic priority . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 6.1.6 Interleaving request . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 6.2 Creating an arbiter assertion-based IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 6.2.1 Block diagram interface description . . . . . . . . . . . . . . . . . . . . . . . . . 134 6.2.2 Overview description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 6.2.3 Natural language properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 6.2.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 viii Creating Assertion-Based IP
6.2.5 Encapsulate properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 6.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
Chapter 7 Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 7.1 Simple generic memory controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 7.1.1 Block diagram interface description . . . . . . . . . . . . . . . . . . . . . . . . . 147 7.1.2 Overview description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 7.1.3 Natural language properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 7.1.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 7.1.5 Encapsulate properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 7.2 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
Chapter 8 Datapath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 8.1 Multiport register file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 8.1.1 Block diagram interface description . . . . . . . . . . . . . . . . . . . . . . . . . 179 8.1.2 Overview description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 8.1.3 Natural language properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 8.1.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 8.1.5 Encapsulate properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 8.2 Data queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 8.2.1 Block diagram interface description . . . . . . . . . . . . . . . . . . . . . . . . . 199 8.2.2 Overview description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 8.2.3 Natural language properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203 8.2.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204 8.2.5 Encapsulate properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 8.3 Data error correction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 8.3.1 Block diagram interface description . . . . . . . . . . . . . . . . . . . . . . . . . 216 8.3.2 Overview description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216 8.3.3 Natural language properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 8.3.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 8.3.5 Encapsulate properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 8.4 Data compression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225 8.4.1 Block diagram interface description . . . . . . . . . . . . . . . . . . . . . . . . . 226 8.4.2 Overview description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227 8.4.3 Natural language properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228 8.4.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 8.4.5 Encapsulate properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 8.5 Data decompression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 8.5.1 Block diagram interface description . . . . . . . . . . . . . . . . . . . . . . . . . 240 8.5.2 Overview description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 8.5.3 Natural language properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 8.5.4 Assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 8.5.5 Encapsulate properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251 8.6 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
Table of Contents
ix
Appendix A Quick Tutorial For SVA . . . . . . . . . . . . . . . . . . . . 253 A.1 SVA fundamentals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253 A.1.1 Immediate assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254 A.1.2 Concurrent assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255 A.1.3 Resets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 A.1.4 Action blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 A.2 SystemVerilog sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258 A.2.1 Consecutive repetition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260 A.2.2 Goto repetition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261 A.2.3 Nonconsecutive repetition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261 A.2.4 Declared named sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262 A.3 Property declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 A.4 Sequence and property operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264 A.4.1 AND . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264 A.4.2 Sequence intersection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265 A.4.3 OR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265 A.4.4 Boolean throughout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 A.4.5 Sequence within . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 A.4.6 Sequence ended . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267 A.4.7 Sequence first_match . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267 A.4.8 Implication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268 A.5 SVA system functions and task . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268 A.5.1 Sampled value functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269 A.5.2 Useful functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270 A.5.3 System tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271 A.6 Dynamic data within sequences . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272 A.7 SVA directives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273 A.8 Useful named property examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
Appendix B Complete OVM/AVM Testbench Example . . . . . . . . . . . . . . . . . . . . . . . . . 275 B.1 OVM/AVM Example Source Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276 B.1.1 top module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 B.1.2 tb_clock_reset module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 B.1.3 pins_if interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279 B.1.4 tb_monitor module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280 B.1.5 tb_env class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284 B.1.6 tb_tr_pkg package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 B.1.7 tb_transaction class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287 B.1.8 tb_status class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290 B.1.9 tb_transactor package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292 B.1.10 tb_stimulus class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292 B.1.11 tb_driver class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293 B.1.12 tb_responder class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 x Creating Assertion-Based IP
B.1.13 tb_coverage class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 B.2 OVM/AVM high-level reference guide . . . . . . . . . . . . . . . . . . . . . . . . . . 301 Bibliography......................................................................................................305 Index..................................................................................................................309
Table of Contents
xi
CHAPTER 0
FOREWORD The design of systems in the 21st century is necessarily one of decomposition of product requirements into functional subsystems. Often these subsystems are implemented with a mix of commercial and in-house design IP. In order to fit into a modern design flow, each design IP should be accompanied by a verification IP (VIP) and a verification plan IP (VPIP). If an electronic system level (ESL) design flow is employed, the VIP and VPIP are employed twice in the flow: during post-partitioning verification and implementation verification. In post-partitioning verification, the behavior of the abstract hardware and software models is demonstrated to conform to their functional specifications. During implementation verification, the behavior of the RTL and embedded production software is demonstrated to conform with the behavior of their corresponding abstract models, as well as with implementation requirements captured in design specifications. Hence, the role of VIP and VPIP is becoming increasingly important, requiring rigorous development processes. Verification IP is available across a matrix of choices, ranging from dynamic to static verification IP, to languagespecific and language-neutral verification components, to application-specification property sets. Dynamic verification IP runs in a simulation environment that exercises the design under verification (DUV), checks that its response conforms with the functional and design specifications, and measures verification progress against metrics defined in its associated verification plan. Static verification IP foregoes the simulation environment in favor of using formal analysis to demonstrate that a design does not violate any of its declared properties. Language-specific verification components—such as Cadence eVCs—and language-neutral verification components—such as Mentor 0-In CheckerWare—bundle the stimulus generation, Foreword
xiii
checking, and coverage measurement aspects into an application-specific verification environment. An application-specific property set, such as Synopsys AHB AIP, includes all of the properties that any implementation must satisfy and is usually suitable for both formal analysis as well as simulation. This book by Harry Foster and Adam Krolnik reduces to process the creation of one of the most valuable kinds of VIP: assertion-based VIP. Assertion-based VIP is trailblazing a path toward property-based design, where a property set is written and assembled from a functional specification, partitioned abstract models are synthesized from the properties, and RTL and software components are synthesized from the abstract models. Today, as an essential element of the verification environment, assertions succinctly capture design requirements without the need for error-prone reference models and comparison logic. The authors address the process of designing and implementing simulation- and assertion-based VIP by first defining their terminology in chapter two and then introducing the steps to be followed for this VIP development in chapter three. In each of the subsequent chapters, the influence of a particular kind of design IP on its VIP counterpart is addressed: busbased designs in chapter four, interfaces in chapter five, arbiters in chapter six, controllers in chapter seven, and finally, datapaths in chapter eight. The Open Verification Methodology (OVM) is employed throughout, enabling VIP structured in this fashion to interoperate with all other OVM VIP and to properly run on all SystemVerilog simulators. I am honored to be given the pleasure of introducing you, the reader, to this long-awaited book. Harry, Adam, and I shared the burden of verifying a MIMD minisupercomputer in the last century. Out of that experience and many others, you will benefit from the authors' years of experience in the application of assertions for design verification. This book will serve as a valuable reference for years to come.
Andrew Piziali, Sr. Design Verification Engineer Co-Author, ESL Design and Verification: A Prescription for Electronic System Level Methodology Author, Functional Verification Coverage Measurement and Analysis
xiv Creating Assertion-Based IP
CHAPTER 0
PREFACE Within the last decade, we have seen significant interest in assertion-based techniques, which have moved beyond the bounds of academic discussions into the realm of industry application. With the recent standardization of assertion and property languages, such as the IEEE Property Specification Language (PSL) and SystemVerilog Assertions (SVA), we have also seen the development of new assertion-based design and verification technologies, which have opened new EDA markets by providing automated solutions to many verification challenges (for example, debugging, coverage specification, and intent validation). As interest in assertion-based techniques has grown, a myriad of books have emerged that focus predominately on teaching syntax and semantics for these new assertion language standards. Often, the examples provided in these books focus on implementation-level assertions. That is not to say it is worthless to add implementation-level assertions to a design. On the contrary, plentiful industry-published data and project statistics tout their benefits. Yet, one of the criticisms we received after completing our Assertion-Based Design [Foster et al., 2004] book, was that our assertion patterns and cookbook examples tended to focus predominately on implementation-level assertion examples. Although this was helpful for the design engineer (it provided an aid to learning how to write embedded RTL assertions), it was of less value to the verification engineer who was interested in validating higher-level or black-box behaviors. Hence, the focus of this book is to bring the assertion discussion up to a higher level and introduce a Preface
xv
process for creating effective, reusable, assertion-based IP, which easily integrates with the user’s existing verification environment (that is, testbench infrastructure). We believe that assertion-based IP is much more than a comprehensive set of related assertions. It is a full-fledged reusable and configurable verification component, which is used to detect both interesting and incorrect behaviors. Upon detecting interesting or incorrect behavior, the assertion-based IP alerts other verification components within a simulation environment, which are responsible for taking appropriate action. The guiding principles we are promoting in this book when creating an assertion-based IP monitor are: • modularity—assertion-based IP should have a clear separation between detection and action • clarity—assertion-based IP should be written initially focusing on capturing intent (versus optimizations) No doubt, our application of these principles will seem fairly radical and even foreign to most people familiar with assertion techniques. Yet, our belief is that modularity facilitates reusable verification components and simplifies maintenance. We are promoting a clear separation of detection from action when creating assertion-based IP so we do not restrict its use. This enables support for many different types of verification components and testbench architectural features. For example, by clearly separating detection from action, the testbench can be constructed to support error injection without having to modify the assertion-based IP. When the assertion-based IP detects an error, it can alert other verification components within the environment, which can then take an appropriate action. This arrangement facilitates reuse. The concepts presented in this book are drawn from the authors’ experience developing assertion-based IP, as well as general assertion-based techniques. We hope this text will foster discussion and debate, and ultimately contribute to the growing body of work related to new assertion-based techniques and verification IP. xvi Creating Assertion-Based IP
Open Verification Methodology The Open Verification Methodology (OVM) is the first, true, system-level-to-RTL verification methodology that allows you to apply leading-edge verification technologies to designs at multiple levels of abstraction, using multiple languages. The OVM provides libraries of base classes and modules in open-source form and uses TLM interfaces as the communication mechanism between verification components. Although the focus of this book is not specifically on the details of the OVM, throughout the text we do demonstrate how modern assertion-based IP can be constructed to communicate with other verification components within a contemporary testbench. Specifically, our assertion-based IP communication mechanism is created using the Advanced Verification Methodology (AVM) [Glasser et al., 2007], which is a subset of the newly formed OVM. As the new OVM begins to mature, naming conventions might vary slightly from the examples we provide. However, you will see that we actually need very few OVM base-class library function calls to establish communication between our assertion-based IP and other verification components, and AVM backwards compatibility is currently a goal of the new OVM.
Acknowledgements The idea for this book actually started after the completion of our Assertion-Based Design book, and has taken on many different forms (or different books) during the course of its evolution. The authors wish to thank Cindy Eisner, Dana Fisman, and Erich Marschner, who made many recommendations on a very different version of this book, which they would likely not even recognize today. In addition, the authors wish to thank Kenneth Larsen, Joe Richards, and Vibarajan Viswanathan, who reviewed various versions of the manuscript and provided invaluable feedback. The authors especially thank Andrew Piziali for not only writing the Foreword, but also providing a very detailed review of the manuscript, and participating in many enlightening lunch-time and E-mail discussions on its Preface
xvii
evolution. Finally, we wish to thank the Mentor Graphics VTECH team for providing an indepth understanding of transaction-level modeling and contemporary testbenches. Many of the architect symbols, definitions, and testbench concepts presented in Chapter 2 are derived from the Advanced Verification Methodology Cookbook developed by the VTECH team. Inspiration never happens in a vacuum. Over the course of our careers there have been many people who directly or indirectly influenced our thoughts related to the topic of assertion-based IP. Hence, we wish to thank the following people for their inspiration over the years: Johan Alfredsson, Yann Antonioli, Roy Armoni, Brian Bailey, Lionel Bening, Janick Bergeron, Eduard Cerny, Edmund Clarke, Claudionor Coelho, Abhishek Datta, Rich Edelman, Cindy Eisner, E. Allen Emerson, Tom Fitzpatrick, Dana Fisman, Mark Glasser, John Havlicek, Alan Hu, Norris Ip, Jan Johnson, Kenneth Larsen, Lawrence Loh, Erich Marschner, Johan Mårtensson, Carl Pixley, Andrew Piziali, Duaine Pryor, Dave Rich, Joe Richards, Adam Rose, Vigyan Singhal, Sundaram Subramanian, Mike Turpin, Moshe Vardi, Vibarajan Viswanathan, and Ping Yeung. Finally, a very special thanks to Jeanne Foster for providing high-quality editing suggestions throughout this project.
xviii Creating Assertion-Based IP
C
H A P T E R
INTRODUCTION
CHAPTER 1
A quick skimming of this book might lead you to think that this is just another book about assertion-based verification. Or perhaps you might think this is a book about assertion patterns that will provide a knowledge base of property set examples for many common design components. But this book is really about process, specifically, a systematic process for creating reusable assertion-based verification components, which we refer to as assertion-based IP. Assertion-based IP is one form of verification components often lumped under the broader term verification intellectual property (VIP). The unique characteristic of assertion-based IP is that it is a reusable property set that takes advantage of assertion and coverage directives and easily integrates with other verification components within a verification environment. Reuse is achieved across multiple design implementations and multiple verification processes. The general relationship of assertion-based IP to VIP will be discussed in Chapter 2, “Definitions and Terminology.”
1.1 Assertion-Based IP assertionbased IP is more than a comprehensive set of related assertions
Figure 1-1 illustrates a typical assertion-based IP verification component within a contemporary transactionlevel simulation environment. A key observation is that our assertion-based IP is much more than a comprehensive set of related assertions. It is a full-fledged reusable and Chapter 1, “Introduction”
1
configurable verification component, which is used to detect both interesting and incorrect behaviors. Upon detecting interesting or incorrect behavior, the assertion-based IP alerts other verification components (connected to its analysis ports), which are responsible for taking appropriate action. Figure 1-1
A typical network of verification components
Driver
DUV
Responder
coverage AssertionBased Monitor
Coverage Collector
error status
Stimulus Generator
Guiding principles
Test Controller
The guiding principles we embrace when creating an assertion-based IP monitor are: • modularity—assertion-based IP should have a clear separation between detection and action • clarity—assertion-based IP should be written initially focusing on capturing intent (versus optimizations) Modularity facilitates reusable verification components and simplifies maintenance. By clearly separating detection from action the assertion-based IP is not restricted in the way it must be used. This enables support for many different
2 Creating Assertion-Based IP
types of verification components and testbench architectural features (such as error injection within a simulation environment). In section 1.2.3 “Simulation vs. formal verification,” we will talk more about our philosophy of focusing on capturing intent versus optimizations when creating assertion-based IP. Why assertion-based IP? Certainly
imperative forms of verification IP (that is, procedural code) delivered as an executable module is not a new concept. However, one problem with this form of verification IP is that it becomes necessary to identify the verification target point to achieve reuse across different tools (which is often not possible).
For instance, Example 1-1 demonstrates a portion of some procedural code used to check that two grant signals are never active at the same time. Example 1-1
Imperative check for mutual exclusive grants
module my_verification_IP (. . . ) always @(posedge clk) begin . . . if (grant[0] & grant[1])) $display (“Mutex violation for grant[0] and grant[1]”); . . . end endmodule
This code could not be synthesized into a hardware monitor since there is not a clear verification target point (that is, not every $display would be a candidate target for hardware monitoring). Similarly, a formal tool would not know the verification target point for its proof.
Chapter 1, “Introduction”
3
.
Example 1-2
Declarative check for mutual exclusive grants
module my_verification_IP (. . . ) . . . property p_mutex (g0, g1); @(posedge clk) !g0 & !g1; endproperty assert property (p_mutex(grant[0],grant[1])); . . . endmodule
The previous example could be re-coded with a declarative assert directive, as illustrated in Example 1-2. Hence, assertion-based IP has a clear advantage over traditional forms of verification IP monitors because they enable efficient reuse across a wide range of verification tools. Assertions enable new verification technologies
Within the last decade, we have seen significant interest in assertion-based techniques, which have moved beyond the bounds of academic discussions into the realm of industry application. With the recent standardization of assertion and property languages, such as the IEEE Property Specification Language (PSL) [IEEE 1850-2005] and SystemVerilog Assertions (SVA) [IEEE 1800-2005], we have seen the development of new assertion-based design and verification technologies, which have opened new EDA markets by providing automated solutions to many verification challenges (for example, debugging, coverage specification, and intent validation).
Industry focus has predominately been on implementation -level assertions
With the growing interest in assertion-based techniques, a myriad of books have emerged that focus predominately on teaching syntax and semantics for these new assertion language standards. Often the examples provided in these books focus on implementation-level assertions. That is not to say it is worthless to add implementation-level assertions to a design. On the contrary, plentiful industry-published data and project statistics tout their benefits [Foster et al., 2004] .
4 Creating Assertion-Based IP
Moving the assertion discussion up to a higher level
One of the criticisms we received after completing our Assertion-Based Design [Foster et al., 2004] book, was that our assertion patterns and cookbook examples tended to focus predominately on implementation-level assertion examples. Although this was helpful for the design engineer (it provided an aid to learning how to write embedded RTL assertions) it was of less value to the verification engineer who was interested in validating higher-level or black box behaviors. Hence, the focus of this book is to bring the assertion discussion up to a higher level and introduce a process for creating effective, reusable, assertion-based IP, which easily integrates with the user’s existing verification environment (that is, testbench infrastructure).
1.1.1 Stakeholders Our personal experience has demonstrated repeatedly that assertions added at any level of hierarchy (or abstractions) will clearly benefit verification by reducing debugging time while clarifying design intent. Certainly multiple stakeholder within the design and verification process can potentially contribute to the assertion development process—thus reducing ambiguities while improving observability. Figure 1-2 illustrates a typical design refinement process through various levels of abstraction and the stakeholders associated with each level. Adoption of assertions in the industry, at the time of this writing, has predominately occurred within the block and module level, which we refer to as implementation assertions. This phenomenon is partially due to the lack of effective guidelines for assertion use at higher levels of design hierarchy (or abstraction) and confusion about which stakeholders should contribute to the assertion development process.
Chapter 1, “Introduction”
5
Figure 1-2
Stakeholders and levels of abstraction System Specification
System Functional Model Architect System TLM Model
System BCA Model
System RTL Model
Designer
Verification Engineer
Units
Blocks
Modules
Architects, verification engineers, and designers all contribute to the assertion development process
Although the architect could contribute to the assertion development effort by defining global properties (derived from the architecture and micro-architectural specification) that must hold across multiple possible implementations, the design engineer contributes by writing internal white-box assertions derived from the implementation. In addition, the verification engineer contributes by developing assertions specifying correct interface behavior between units and between blocks. The verification engineer also contributes by developing black-box, end-to-end assertions across design components.
6 Creating Assertion-Based IP
Understand what you intend to specify before you write your assertions
Although it is easy to identify the various stakeholders and roles they play in the assertion development process, these stakeholders often lack a process for systematically developing their assertions. In fact, a classic mistake many engineers make when first adopting assertion-based techniques is to jump into coding assertions too soon— without fully understanding the behavior they are trying to specify. This ad hoc approach leads to incomplete (or incorrect) property sets and non-reusable assertion-based IP. We address this problem by introducing a systematic process in which we create a natural language list of properties prior to coding the assertions, which is a fundamental step in our process for creating assertion-based IP.
1.1.2 Levels of abstraction When applying assertion-based techniques, it is important to first understand where they can be effectively applied. For example, Figure 1-2 illustrates a typical design refinement process. System specification refined to a system functional model
The flow begins with a system specification, which is typically a natural language properties document. There has been recent interest in executable forms of system specification, such as UML [Martin 2002]. The first refinement of the system specification is often a system function model to explore the proposed algorithm, which is often written in C or C++. At this point, hardware-software partitioning and architectural mapping decisions have not been made.
System transactionlevel model
The system transaction-level model (TLM) is generally an untimed (or partially timed) model, and it is created after architectural mapping. This model is often used for firmware development, system and architectural performance analysis, and software development. The TLM is often further refined into a bus cycle-accurate (BCA) model as architectural decisions begin to gel.
Chapter 1, “Introduction”
7
RTL model
RTL refinement occurs next, and the system is partitioned into multiple units. Each unit is partitioned into blocks. Each block is partitioned into modules consisting of RTL code.
Natural language list of properties
Certainly a natural language list of properties can (and should) be developed at all levels of abstraction. However, today’s assertion language standards lack the proper formalism necessary to express properties at all levels of abstraction illustrated in Figure 1-2. For example, within an untimed transaction-level model, one concurrent transaction might overlap with a different transaction. That is, it could begin and end in the middle of a different untimed transaction. Existing assertion language standards lack the semantics (and syntax) necessary to express assertions related to this type of transaction behavior. Researchers have proposed solutions to the TLM assertion specification problem, which might result in semantic and syntactical extensions to existing standards [Ecker et al., 2006]. However, successful assertion specification today in an industry setting occurs at and below the BCA abstraction level, illustrated in Figure 1-2. We will demonstrate how to develop assertion-based IP that can be reused within a transaction-level testbench using existing standards by introducing what is essentially an abstraction converter between a timed RTL model and untimed transaction environment.
1.1.3 Reuse Design and verification productivity drive reuse adoption
Many forces at play contribute to a gap between what we can manufacture (silicon capacity) and what we have time to design. Furthermore, these forces contribute to the gap between what we can design and what we have time to verify. Intellectual property (IP) offers the promise of filling these gaps by increasing both design and verification productivity. In fact, recent studies, such as the International Technology Roadmap for Semiconductors (ITRS) [ITRS 2003], indicate that 75 percent of design productivity improvement will come from IP reuse and 25 percent from
8 Creating Assertion-Based IP
improved EDA tools, flow, or methodologies. A key factor in both design and verification economics is IP reuse. Yet, without systematic processes for development, IP is often delivered with poor quality, and it is difficult to use or incompatible with other design and verification components. While industry guidelines for design reuse have been published [Keating and Bricaud 2002] few guidelines for verification IP reuse exist [Glasser et al., 2007] [Wilcox 2004] [Bergeron et al., 2006], and certainly (to the best of our knowledge) no guidelines exist specifically focused at assertion-based IP. Hence, we believe that a systematic process and set of guidelines for creating effective reusable assertion-based IP are sorely needed. And that is the focus of this book.
1.2 Properties and assertions The recent flurry of interest in assertion-based techniques has prompted considerable published work on the subject. Often, the authors of these publications interchange the terms property and assertion, which leads to confusion. For our discussion, we informally define a property as follows: Definition•1.1
Property—a statement of expected behavior. For example, the statement, grant[0] and grant[1] are mutually exclusive is an example of a property, which is actually a partial specification for an arbiter. Notice that we have not stated how we intend to use this property during verification. For example, we might choose to use this property as a constraint specification, which is a requirement on the environment, and assume the property during verification. In this case, we want to eliminate traces that violate our constraint. Or, we might choose to use this property as a coverage specification, and cover the property such that the verification tool notifies us at the particular point in which the coverage specification holds on a trace. Or, we might choose to use the property as a design
Chapter 1, “Introduction”
9
requirement specification and assert that the property holds on all traces of a design during verification. We informally define an assertion as follows: Definition•1.2
Assertion—an implementation of a property that is evaluated or executed by a tool to validate design intent. We use an assertion as a target during verification (that is, a checker for simulation or a proof obligation for formal) to help us identify and isolate unexpected behavior.
1.2.1 Languages To evaluate our natural language property grant[0] and grant[1] are mutually exclusive during verification, we must express the property in a machine executable form (that is, an assertion language). Two alternative languages we can chose from to express this property are: SVA, which is an assertion sublanguage within IEEE 1800-2005 Std SystemVerilog or PSL, which is the IEEE 1850-2005 Std Property Specification Language. SVA and PSL
In this book, we have chosen SVA to demonstrate our examples. We could express our examples just as easily in PSL—allowing reuse of our property set between simulation and formal verification, and you might chose to create your property set using PSL [Eisner and Fisman 2006]. However, we also want to demonstrate other aspects of creating reusable verification IP (such as proper handling of error injection), which requires explicit communication between an assertion (or cover directive) and other verification components within a simulation environment. This communication is easily accomplished by using assertion and coverage action blocks along with analysis ports defined within our assertion-based IP interface. The following chapters discuss details about connecting assertion-based IP with multiple verification components.
10 Creating Assertion-Based IP
1.2.2 Libraries While selecting a property and assertion language standard is an integral step for adopting assertion-based techniques, it is not the entire solution. Methodology is equally important to effectively applying ABV. In fact, without enforcing a consistent methodology across multiple design and verification engineers (for example, a consistent means of reporting errors, enabling or disabling assertions, and assigning actions performed upon error detection), the ad hoc use of assertions can end up being unmanageable and disruptive to the overall verification process. Assertion libraries provide a means for ensuring that a consistent ABV methodology is employed across an entire project’s verification flow. Furthermore, libraries provide an ideal means to encapsulate configurable property sets that can be preverified, which makes them ideal for assertionbased IP reuse. The Open Verification Library
As we discussed in the previous section, assertion specification can occur at multiple levels of hierarchy and multiple levels of abstraction. The same holds true for libraries used to create assertion-based IP. For example, the Accellera OVL [Accellera OVL 2007] incorporates a consistent and systematic means of specifying RT-level implementation properties structurally through a common set of library elements (that is, assertion monitors). The OVL library elements act like a template that lets designers express a broad class of assertions in a common, familiar RTL format. Furthermore, the OVL capitalizes on the various emerging property and assertion language standards by delivering multiple flavors of the library, implemented either in pure Verilog ‘95, PSL, or SVA. This versatility is important to design IP providers who must deliver design IP code to a diverse group of customers, each with their own restrictions in terms of language support [Foster et al., 2006a]. The OVL enables the IP provider to instantiate an assertion checker as a module within the design IP, and then allows the users to link in an appropriate library that will work best with the verification infrastructure and environment. For example, returning to our natural language Chapter 1, “Introduction”
11
property grant[0] and grant[1] are mutually exclusive example, the IP provider would instantiate an OVL checker into its RTL as demonstrated in Example 1-3. Example 1-3
Instantiated OVL checker
assert_always mutex_grants (clk, reset_n, !(grant[0] & grant[1]));
Example 1-4 illustrates the various supported implementations of the OVL assert_always monitor. The test_expr (in Example 1-4) is a formal parameter that receives the actual parameter !(grant[0] & grant[1]) (from Example 1-3) when assert_always is called. Example 1-4
assert_always OVL implementation example
`include "std_ovl_defines.h" `module assert_always (clk, reset_n, test_expr); parameter severity_level = `OVL_ERROR; parameter property_type = `OVL_ASSERT; parameter msg="VIOLATION"; parameter coverage_level = `OVL_COVER_ALL; input clk, reset_n, test_expr; `ifdef OVL_VERILOG `include "./vlog95/assert_always_logic.v" `endif // OVL_VERILOG `ifdef OVL_SVA `include "./sva31a/assert_always_logic.sv" `endif // OVL_SVA `ifdef OVL_PSL `include "./psl11/assert_always_psl_logic.v" `endif // OVL_PSL `ifdef OVL_PSL //Do not add "`endmodule" as this will be added in the included file `else `endmodule `endif
Without having to create and supply multiple versions of the IP supporting all the different assertion languages, the IP provider ships its IP with OVL monitors instantiated, and
12 Creating Assertion-Based IP
the customer selects an appropriate version of the OVL to link into its verification environment. For example, the SVA flavor of the OVL assert_always can contain the code Example 1-5
SVA flavor of OVL (assert_always_logic.sv)
`ifdef ASSERT_ON assert_always: assert property( @( posedge clk) disable iff ( !reset_n) (test_expr) else sva_checker_error(""); `endif
Higher level libraries
In this book, we demonstrate a process for creating complex library verification components we call assertion-based IP. Unlike the OVL, our examples target behaviors at higher levels of the design hierarchy and a higher level of abstraction (for example, a comprehensive set of assertions that specify the proper behavior of an SDRAM memory controller). Certainly, these more complex assertion-based IP verification components can be constructed by encapsulating a set of OVL with additional modeling code inside a single monitor. However, we have chosen to take advantage of SVA’s action blocks to demonstrate the assertion-based IP examples in this book. Action blocks allow us to control the separation of detection from action by taking advantage of analysis ports (discussed in Chapter 3, “The Process”) within the assertion-based IP architecture.
1.2.3 Simulation vs. formal verification Our assertionbased IP development philosophy
With the advent of the OVL standard, the industry saw its first alternative solution for “specifying once” and then leveraging assertion specification over multiple verification processes—such as traditional simulation and formal verification tools [Foster and Coelho 2001]. In fact, this is one of the value propositions you might have heard from various EDA vendors promoting assertion-based techniques. While this is certainly true for many simpler Chapter 1, “Introduction”
13
white-box implementation assertions, it has been our experience that higher-level, black-box forms of assertions describing behavior across a block, often require abstractions or advanced coding techniques to achieve a complete formal proof. These advanced techniques are beyond the scope of this book. Our philosophy toward creating assertion-based IP is that you should first focus on capturing the intent of the design in a clear set of assertions suitable for simulation, and then optimize any of the problematic assertions you encounter only if you experience problems while attempting proof—and only if there is a clear return on effort to achieve a complete proof. You can certainly reuse assertions written for simulation as bughunting targets with formal verification without optimization.
1.3 Who should read this book? We have created this book for many different audiences. For example: • The novice in assertion-based techniques, who might be an electronic engineering student learning about design and verification and interested in learning from practical examples • The academic instructor, who is looking for real assertion examples beyond the typical toy circuit examples • The experienced design or verification engineer who is considering adapting ABV on a future project and wants to evaluate processes • The design or verification manager whose goal is to improve the capability maturity of their organization through state-of-the-art industry best practices and processes • The system architect who wishes to improve on communicating design intent to other stakeholders within a project team
14 Creating Assertion-Based IP
• The verification IP provider who is looking for a systematic process to develop assertion-based IP
1.4 Book organization Creating Assertion-Based IP is organized into chapters that you can read sequentially for a full understanding of our topic. Or you might wish to focus on a particular area of interest and visit the remaining chapters to supplement your understanding. We have allowed repetitions to support the readers who prefer to browse for information as well as readers who intend to read cover-to-cover and then revisit sections of particular personal relevance. Highlights of the contents follow. Chapter 2, “Definitions and Terminology”
Chapter 2, “Definitions and Terminology” provides definitions of terms and acronyms used throughout the book and a verification IP framework in terms of verification components found in a contemporary testbench.
Chapter 3, “The Process”
Chapter 3, “The Process” introduces a systematic set of steps recommended for creating assertion-based IP. In addition, this chapter discusses project-specific concerns for effective assertion-based IP integration.
Chapter 4, “Bus-Based Design Example”
Chapter 4, “Bus-Based Design Example” sets a framework for our discussion in the remaining chapters of the book by introducing a typical SoC bus-based design example, which consists of various common functional design components. We then demonstrate our assertion-based IP creation process on many of these design components in the remaining chapters of the book.
Chapter 5, “Interfaces”
Chapter 5, “Interfaces” demonstrates our assertion-based IP creation process for various bus interfaces. On-chip bus protocols form the foundation for many of today’s design reuse strategies. This chapter discusses assertion-based IP for serial, nonpipelined, and pipelined-bus interfaces.
Chapter 1, “Introduction”
15
Chapter 6, “Arbiters”
Chapter 6, “Arbiters” demonstrates our assertion-based IP creation process for various arbiters. Arbiters are critical components in systems containing shared resources. For example, for a bus-based design where more than one bus master might drive a shared bus, arbitration prevents dropping or corrupting data transported through the bus.
Chapter 7, “Controllers”
Chapter 7, “Controllers” demonstrates our assertion-based IP creation process on typical controllers. While busses serve as the framework for platform-based SoC designs, it is controllers that provide the operational brains. Controllers span the spectrum of design, from lower-level control units embedded in complex design components (such as a simple one-hot encoded control unit), to very complex controllers (such as a graphics controller, memory controller, and an embedded CPU’s cache control unit).
Chapter 8, “Datapath”
Chapter 8, “Datapath” demonstrates our assertion-based IP creation process on blocks involving datapaths. In this book, we have not limited our discussion to written assertions that are only amenable to formal verification. Hence, we demonstrate our assertion-based IP creation process on both data transportation and data transform blocks (many of which are not good candidates for formal verification using model-checking).
Appendix A, “Quick Tutorial For SVA”
Appendix A, “Quick Tutorial For SVA” provides a basic introduction to SystemVerilog Assertions. Specifically, we discuss the constructs we use in this book.
Appendix B, “Complete OVM/AVM Testbench Example”
Appendix B, “Complete OVM/AVM Testbench Example” provides a complete transaction-level testebench with an assertion-based IP example. In addition, it provides a basic introduction to the Open Verification Methodology (OVM) base-class library components we reference in this book.
16 Creating Assertion-Based IP
1.5 Summary This chapter presented the motivation for creating assertionbased IP and the stakeholders involved in the process. A unique characteristic of assertion-based IP is that it is a reusable property set, which takes advantage of assertion and coverage directives and easily integrates with other verification components in a verification environment. Reuse is achieved across multiple design implementations and multiple verification processes (for example, simulation and formal verification).
Chapter 1, “Introduction”
17
C
H A P T E R
DEFINITIONS AND TERMINOLOGY
CHAPTER 2
We exist in a multilanguage world where our success often depends on our ability to communicate intent and eliminate ambiguities. Yet even within a common discipline, such as engineering, the problem and solution space is often quite varied (due to historical, cultural, and technical reasons), which leads to a difference in terminology that results in misunderstandings. To ensure you (the reader) are on the same page with us (so to speak), we have included this chapter, which provides definitions for the terminology we use throughout the remainder of the book. Chapter organization
This chapter is divided into two main sections. The first section builds a framework for our discussion by introducing common verification components found within contemporary simulation environments. Understanding how the various verification components potentially interact and the communication channels required to connect these components is critical as we architect our assertion-based IP solution. The second section of this chapter provides a set of definitions for many terms used throughout this book. In addition, we spell out a list of common acronyms related to our topic. In this section, we discuss architectural aspects of a contemporary transaction-level testbench. We chose the Chapter 2, “Definitions and Terminology”
19
Advanced Verification Methodology [Glasser et al., 2007], which is a subset of the newly formed OVM, as the basis for our discussion because the source code for the OVM library is openly available and can be freely downloaded at http:// www.mentor.com/go/cookbook. Assuredly, there are other testbench base-class libraries available, and we encourage you to choose one that you feel comfortable with when creating your assertion-based IP. The general ideas, processes, and techniques we present in this book for creating assertion-based IP are easily extended to the specific implementation details of other verification environment methodologies, such as the VMM [Bergeron et al., 2006] and the eRM [2005].
2.1 Notation Connecting separate verification components through well defined interface is a key tenet of our assertion-based IP methodology, and is reflected in the example notation we use throughout the book. Hence, in this section we define the graphical notation, which includes: components, interfaces, interconnects, channels, and analysis ports.1
2.1.1 Components A component is an instantiable object in SystemVerilog, such as a module, interface, program block, or class. For our discussion, we represent a component as a box, as illustrated in Figure 2-1. Components often have free running threads. Sometimes, the location of threads in a design or testbench is important to understanding the architecture of the design. To show a 1. Notation discussion based on Advanced Verification Methodology (AVM) concepts. See [Glasser et al., 2007]. 20 Creating Assertion-Based IP
component that has one or more threads we use a circular arrow, as illustrated in Figure 2-2. Figure 2-1
Component symbol
Figure 2-2
A component with a thread
2.1.2 Interfaces Interfaces are the externally visible connections to components. All of a component’s behavior is accessible and visible only through its interfaces. First, is the familiar pin interface, as illustrated in Figure 2-3. Figure 2-3
A component with a pin interface
The black boxes on the right side of the component represent pins. Whereas pin interfaces move data represented at the timed bit level between components, transaction interfaces move high level untimed (or partially timed) data between components. Chapter 2, “Definitions and Terminology”
21
Figure 2-4 represents two variations of transaction interfaces: a port and an export. The component on the left has a transaction port and the component on the right has an export. An export makes an interface visible, and a port is a requirement to connect to an export. A good way to think about transaction ports is as a set of unresolved function calls that are resolved by exports. Ports and exports are complements of each other; ports connect to exports. You cannot connect an export to an export, nor a port to a port. Figure 2-4
A component with a transaction-level interface
port
export
The port/export notation identifies the flow of control between components. Since a port interface calls functions on an export, flow of control moves from ports to exports.
2.1.3 Interconnect Just like with traditional schematics, we use lines between interfaces to show the interconnection amongst components. The addition of arrow heads allows us to represent data flow. Figure 2-5
Pin-level data flow A
B
Arrows between pins show the direction that data flows between components. The figure above shows, from top to 22 Creating Assertion-Based IP
bottom, flow from A to B, bi-directional between A and B, and flow from B to A. Figure 2-6
Transaction data flow A
B
put configuration
A
B
get configuration
Figure 2-6 illustrates two configurations, each with the same transaction interfaces, but with different data flow. In both configurations, a function in B is invoked by A. A initiates activity in B. A is the initiator and B is the target. In the top configuration, A moves data to B. This is called a put operation. In the bottom configuration, A moves data from B back to itself. This is called a get operation.
2.1.4 Channels Transaction level components often communicate through channels. A channel is a component that defines the semantics of the communication. One of the most common channel usage is a FIFO. FIFOs are used to throttle communication between two transaction level components. To show this in a netlist, we show a small box between components to represent the FIFO. A FIFO, as with other communication channels, exports an interface. However, in the interest of keeping the diagram uncluttered, the circles on the channel exports are optional and often omitted.
Chapter 2, “Definitions and Terminology”
23
Figure 2-7 shows two components, each with their own thread, and each with a transaction port that connects to an intervening channel. Component A puts transactions into the FIFO channel and component B gets transactions from the same channel. The data flow arrows in addition to the transaction ports tell us which components are doing gets and which are doing puts. A has a thread, a transaction port (as opposed to an export), and an arrow leading away from it. That tells us that A is putting transactions into the channel. B also has a thread and a transaction port, but the data flow arrow is leading into the component instead of away from it. That tells us that B is getting transactions from the channel. Figure 2-7
Two components communicating through a FIFO A
B fifo
`
2.1.5 Analysis ports Analysis ports (illustrated in Figure 2-8) are a kind of transaction-level port used for communicating analysis information (for example, coverage data or assertion error status) between components. The symbol for an analysis port is a diamond. Analysis ports are connected to a component with an analysis interface. This could be an analysis FIFO or a component with an analysis interface. Figure 2-8
Analysis port connected to an analysis interface Analysis port A
B
Analysis interface
24 Creating Assertion-Based IP
2.2 Verification component description Table 2-1 provides a list and description for many common verification components found in a contemporary verification environments. Table 2-1 Contemporary testbench verification components Name
Description
Control simulation controller
The simulation controller is the decision maker within the testbench. It completes the loop from stimulus generator through driver, monitor, scoreboard, and coverage collector.
Analysis coverage collector scoreboard
A coverage collector is responsible for recording observed behaviors defined in an associated coverage model. A scoreboard is one type of verification component used to determine the end-to-end correctness of the DUV. Input stimulus entering the DUV is stored inside the scoreboard, which will then be used/transformed for comparison against the DUV’s output response.
Environment stimulus generator
scenario generator
master
A stimulus generator is a verification component that generates stimulus. For example, in a contemporary testbench, a stimulus generate might generate either directed or random transaction-level stimulus. Hence, it would contain the algorithms and constraints used to generate stimulus. A scenario generator is a form of stimulus generator that generates a stream of a directed sequence of transactions that is intended to perform a specific welldefined function on the DUV. A master is a bi-directional verification component that sends requests and receives responses. A master initiates activity and may use a response to determine the next course of action.
Chapter 2, “Definitions and Terminology”
25
Name
Description
slave
A slave is a bi-directional verification component. Slaves reply to requests and receive responses, they do not initiate activity.
Transactors driver
responder
monitor
A driver verification component converts the untimed or partially timed transaction-level stimulus into timed pin-level activations on the DUV. A responder verification component is similar to a driver. It connects to a bus and will drive timed pinlevel activity on the bus. The responder responds to activity on the bus rather than initiating it. A monitor is a passive verification component. It observes the pins of the DUV and converts the timed pin-level activity into a stream of transactions for use by other components within a testbench.
2.3 Verification component organization Today’s hardware designs are modular in their composition and can be viewed as a network of various design components. Similarly, contemporary testbenches are also modular in their composition and can be viewed as a network of various verification components. In fact, it is this property of modularity, in terms of well-defined behavior and interfaces for these various components, that simplifies the construction, debugging, and maintenance of today’s complex systems. Network of verification components
Concentric testbench organization. Figure 2-9 illustrates an example of various common verification components found in a typical testbench. In our example, you will note that our testbench architecture is in multiple layers. The bottom layer is the register transfer level design under verification (DUV). All pin-level activity at this layer is timed (that is, either coarse-grain cycle accurate timing or fine-grain signal accurate timing).
26 Creating Assertion-Based IP
Figure 2-9
A typical network of verification components
scoreboard
coverage
abstraction conversion layer monitor
controller
master/ stim gen
driver
monitor
DUV
responder
slave
Above the RTL layer (labeled abstraction conversion layer in Figure 2-9) is a set of verification components known as transactors, which are used to convert untimed (or partially timed) streams of transactions into pin-level timed activity—and vice versa. All verification components above the abstraction conversion layer are at the transaction layer. Hierarchy of verification components
Hierarchical testbench organization. As an alternative, and for the purpose of our discussion, we view the architecture layers of a testbench as a hierarchy of verification components organized into sets of similar functionality as illustrated in Figure 2-10.
Modularity benefits
By organizing the testbench verification components into sets of well-defined functionality, and establishing clear channels of communication and interfaces between the layers we are able to achieve our goal of modularity, which has the following benefits: •
Enables reuse of verification components
•
Facilitates quick enhancements and maintenance through localization
•
Simplifies debugging
Chapter 2, “Definitions and Terminology”
27
Figure 2-10
untimed transaction level
analysis
coverage collectors performance analyzer scoreboards golden models
untimed or partially timed transaction level
environment
stimulus generators masters slaves constraints
transactions pins
transactors
drivers monitors responders
pins
DUV
pin level design
Protocolspecific
test controller
Design-specific
control
Testbenchspecific
untimed transaction level
Architectural layers and component types
Control layer
The control layer contains a simulation controller verification component, which provides the main thread of a test and orchestrates the activity. Typically a simulation controller receives information from analysis components, which it then uses to determine when to complete or terminate a test and send information to environmental components. Transactors, such as monitors, can pass status to the simulation controller upon detecting errors, and the simulation controller will then take appropriate actions.
Analysis layer
The analysis layer contains a set of components that receive information about what is going on in the testbench and uses
28 Creating Assertion-Based IP
that information to determine the correctness or completeness of the test. Common analysis layer verification components include scoreboards and coverage collectors. Environment layer
The environment layer contains a set of verification components necessary to operate the DUV, such as stimulus and scenario generators as well as constraints.
Transactor layer
The transactors layer contains a set of verification components that convert a stream of untimed or partially timed transactions into timed pin-level activity or vice versa. Transactors are typically characterized by having at least one pin-level interface and at least one transactionlevel interface. Assertion-based IP (monitors) fall into this testbench architectural layer. There are obviously other ways to view and organize the architectural layers within a testbench, for example [Bergeron et al., 2006] [eRM 2005], and you might have your own ideas for testbench architectural organization. Certainly we could spend hours debating the merits of various testbench organizations and architectural views. That is not the goal of this chapter. Our goal is to present an organizational view to set a framework for discussion throughout the remainder of the book. The ideas we present on how to effectively integrate assertion-based IP with other verification components are applicable (or easily extended) to alternative testbench architectural views. Operational vs. analysis organization. To conclude our discussion on ways to view and organize verification components within a testbench, we partition the set of verification components into two domains, operational and analysis, as illustrated in Figure 2-11. The operational domain contains the set of verification components that operate on the DUV. The analysis domain contains the set of components that watch and analyze the operation.
Chapter 2, “Definitions and Terminology”
29
Figure 2-11
Operational and analysis testbench domains
Analysis Domain Analysis Interfaces
Control and Configuration Interfaces
Analysis Ports Operational Domain
Analysis ports
Data moves from the operational domain to the analysis domain in a way that does not interfere with the operation of the DUV while it preserves event timing. You will see in our examples in the upcoming chapters that we accomplish this by means of a special communication channel called an analysis port. An analysis port is a unique transaction port in which a publisher (for example, an assertion-based IP module) broadcasts data to one or more subscribers (for example, both a coverage collector and a scoreboard). What is unique about this approach is that the publisher (and subscriber) need neither knowledge about the testbench architecture nor details about any of the verification components that are subscribing to its analysis port data. This preserves the localization property of modular design and makes the verification component truly reusable and interoperable. Essentially, the connection between the verification components is achieved by having each subscriber (that is, other verification components within the testbench) register with a particular publisher’s (that is, in our case an assertion-based IP monitor) analysis port, which creates a list of subscriber interfaces for broadcasting data. Chapter 3, “The Process” demonstrates how to create analysis ports as a SystemVerilog class, which is implemented as part of the Open Verification Methodology (OVM) base-class library. Additional details about the OVM base-classes we use in our examples are covered in Appendix B, “Complete OVM/AVM Testbench Example.”
30 Creating Assertion-Based IP
2.4 Definitions In this section, we define the terminology common to the discipline of assertion-based IP. This section should be referenced whenever an unfamiliar word or phrase is encountered in this text. For a comprehensive list of terms, we recommend Taxonomy for the Development and Verification of Electronic Systems [Bailey et al., 2005]. In addition, Functional Verification Coverage Measurement and Analysis provides an excellent list of terms that are common to the language of coverage [Piziali 2004]. Finally, ESL Design and Verification provides an excellent taxonomy and definition of terms [Bailey et al., 2007]. Whenever possible, we have made an effort to align our definition of terms with these sources. Table 2-2 Definition of terms Terminology
Definition
abstraction
Describing an object using a model where some of the low-level details are ignored.
assertion
The implementation of a property evaluated or executed by a tool (see also property).
assumption
An environmental constraint.
arbiter
A design or verification component that manages shared resources.
assertion coverage
Observing an assertion evaluated or executed, passing or failing, and recording the possible paths of evaluation through the assertion.
assertionbased IP
A passive verification component, consisting of declarative properties and potentially additional procedural code, that monitors either bus interface activity or a design component end-to-end behavior.
behavior
A model of a system at any level of abstraction that potentially includes timing information.
Chapter 2, “Definitions and Terminology”
31
Terminology
Definition
black box
A term used to define the amount of visibility internals of a block have and thus the amount of control an engineer has over that block for the purposes of verification. In the case of black box, no internals are visible. The opposite of black box is white box.
bus functional model (BFM)
A verification component that specifically focused on generating stimulus and checking results for correct bus signal activity. A BFM imitates correct bus signal characteristics.
constraint
Rules definition relationships between signals within a design; they can be combinatorial or sequential/temporal and can be used in pseudo-random generation and formal methods.
controller
A state-machine used to generate control signals that either activate or terminate various components within a design.
corner case
One or more data values or sequential events that, in combination, lead to a substantial change in design behavior. A corner case is often an exceptional (rare) condition that is difficult to predict.
coverage
A measure of how thoroughly a design has been exercised during verification to minimize the probability that latent errors remain.
coverage model
An abstract representation of device behavior composed of attributes and their relationships. Relationships may be either data or temporal in nature.
datapath
A datapath is hardware that either performs data processing operations (that is, transforms data) or buffers the flow of data (that is, transports data). It is one of two types of modules used to represent a digital system, the other being a control unit.
design component
A reusable implementation (that is, a model) of a well defined set of high-level or low-level functionality.
32 Creating Assertion-Based IP
Terminology
Definition
dynamic Demonstrating that a design conforms to its funcverification tional specification through execution.
This form of verification relies on the progression of time to allow the design’s internal logic to propagate through the design-specific values placed on the design’s inputs at a given time. This algorithm requires some specific mechanism (for example, simulation, emulation, or prototype) to run the design and a specific methodology to apply stimulus and check the output of the design (verification environment) to verify correctness of the design. intellectual A block of code that describes an aspect of a system, property including its hardware, software, or the verification (IP) environment, which is reused between multiple
designs or parts of a design. A complete IP for reuse should include its specification, verification plan, verification environment (dynamic and static components) and RTL implementation. model
A way of capturing certain behavioral aspects of a system. A model normally involves the application of some amount of abstraction such that adequate performance can be obtained at a desired level of accuracy.
platformA reuse-intensive design style for embedded systems based design where large portions of the design are based on pre-
designed and preverified SoC design components. This is an integration oriented design approach that emphasizes systematic reuse for developing complex products based upon platforms and compatible hardware and software virtual components that are intended to reduce development risks, costs, and time to market.
Chapter 2, “Definitions and Terminology”
33
Terminology
Definition
property
A statement of an expected behavior. For example, a liveness property says that something should eventually happen and is often called an eventuality. A safety property says that something should never happen and is often called an invariant. Liveness and safety properties define valid or invalid paths through the state space of a design.
protocol
A set of rules governing communication between design components.
protocol checker
A verification component (either procedural-based or assertion-based) that checks a set of rules of an interface and determines if violations of defined, acceptable behavior have occurred.
requirement
A requirement is: • A condition or capability needed by a user to solve a problem or achieve an objective. • A condition or capability that must be met or possessed by a system or a system component to satisfy a contract, standard, specification, or other formally imposed document. • A documented representation of a condition or capability as defined above.
static The process of demonstrating that a design conforms verification to its functional specification through comparative
analysis and proof, as opposed to design execution. transaction
A transaction is an agreement, communication, or movement carried out between separate entities or objects. For example, a single transfer of control or data between two entities. Within a transaction-level testbench, a transaction is initiated via a function call (for example, my_object.read() ).
verification An individual unit (that is, module or object) that are component used as building blocks in the construction of a verifi-
cation environment (such as a simulation testbench).
34 Creating Assertion-Based IP
Terminology
Definition
verification An umbrella term for a reusable verification unit that IP (VIP) is also the unique property of one party but may be
licensed to another party (or can also be owned and used by a single party alone). Examples include BFM, assertion-based IP, protocol checker. white box
A term used to define the amount of visibility and or control an engineer has into a block for the purposes of verification. In this case all internals are visible. The opposite of this is black box.
2.5 Acronyms Table 2-3 provides a list of common acronyms related to the field of assertion-based IP, many of which are used throughout the book. Table 2-3 Acronyms Acronym
Meaning
AHB
AMBA Advanced High-performance Bus
AMBA™
Advanced microcontroller bus architecture
APB
AMBA Advanced Peripheral Bus
AVM
Advanced Verification Methodology
BFM
Bus functional model
DUV
Design under verification
EDA
Electronic design automation
eRM
e reuse methodology
FIFO
First in first out
FSM
Finite state machine
HDL
Hardware description language
Chapter 2, “Definitions and Terminology”
35
Acronym HVL 2
High-level verification language
I C
Inter-Integrated Circuit
IP
Intellectual property
OVL
Open Verification Library
OVM
Open Verification Methodology
PSL
Property Specification Language
RTL
register transfer-level
SDRAM
Synchronous dynamic random access memory
SoC
System on chip
SVA
SystemVerilog Assertions
TLM
Transaction-level model
VIP
Verification IP
VMM
Verification Methodology Manual
2.6 Summary We created this chapter to establish a common language between us (the authors) and you (the reader). The first section built a framework for our discussion by introducing common verification components found within contemporary simulation environments (such as the OVM). We believe that a solid understanding of how various verification components potentially interact and the communication channels required to connect these components is critical to properly architect an assertionbased IP solution. The second part of this chapter provided a set of definitions for many terms used throughout this book. In addition, we spelled out a list of common acronyms related to our topic.
36 Creating Assertion-Based IP
C
H A P T E R
THE PROCESS
CHAPTER 3
With the emergence of assertion and property language standards, design teams are investigating assertion-based verification techniques and finding that there is generally value in applying these techniques [Foster et al., 2004]. In spite of this general acceptance, there is a huge disconnect between attempting to write a collection of embedded implementation assertions and creating a comprehensive reusable assertion-based IP verification component. For example, if you attempt to create assertion-based monitors for a complex bus interface or a memory controller without approaching the task systematically and planning each step, then it is likely that the quality of your results will be poor. In this chapter, we introduce a systematic set of steps to help you effectively create your assertion-based IP. Next, we focus on the process of implementing a SystemVerilog module-based assertion monitor. Using a SystemVerilog interface or module-based component (versus a class-based component) is necessary when implementing an assertionbased monitor since general temporal assertions are not allowed within a SystemVerilog class. Nonetheless, as we demonstrate, you can successfully create a testbench that combines both module-based and class-based components. To support this framework, we discuss a class-based communication mechanism, which is used to connect and then establish communication between these module-based and class-based components. Chapter 3, “The Process”
37
3.1 Guiding principles As we mentioned in the introduction, the guiding principles we embrace when creating an assertion-based IP monitor are: • modularity—separate detection from action • Facilitates reusable verification components • Simplifies maintenance • Supports advanced features such as error injection
• clarity—target your assertions for simulation • Initially focus on capturing intent • Do not get distracted by formal verification optimizations
Modularity facilitates reusable verification components and simplifies maintenance. A clear separation between assertion (and coverage) detection from action does not restrict assertion-based IP use. For example, as demonstrated later in this chapter, an assertion-based monitor might detect that a bus protocol violation occurred. Then the assertion-based monitor would pass on error status information (via an analysis port) to other verification components within the testbench. Using an analysis port, the assertion-based monitor does not need to know the details of the testbench architecture (or even who is connected to its analysis port). Hence, this framework supports advanced testbench features such as error injection. For example, one component within the testbench might have injected an error into a transaction, and it is expecting an error condition to be detected. Once it is alerted of this condition by the assertionbased monitor, it can then take an appropriate action. Specification versus implementation assertions
We are not promoting that you separate detection from action for every assertion in your design. Certainly, this technique would be impractical if applied to the thousands of implementation assertions embedded in an RTL design. However, we are promoting this approach for the case when you create a reusable assertion-based verification IP component that must communicate with other verification components within a testbench.
38 Creating Assertion-Based IP
3.2 Process steps The systematic steps we follow are based on the work of [Foster et al., 2006b]. However, there are differences in the process we present in this chapter. Dasgupta [2006] also presents a similar process, yet his work was narrowly focused on the testplanning process targeted at formal verification, which often requires introducing advanced strategies (and abstractions) to get the set of assertions to converge during a formal proof. In this chapter, the process we present is focused on creating reusable verification components for simulation. Our steps, illustrated in Figure 3-1, are as follows: Step 1
Create a block diagram and interface description. Create a block diagram and table that describes the details for the design’s design component interface signals that must be referenced (monitored) when creating the set of assertions and coverage items. You will use this list to determine completeness of the requirement checklist during the review process.
Step 2
Create an overview description. Briefly describe the key characteristics of your design’s design component. You do not have to make the introduction highly detailed, but it should highlight the major functions and features. Waveform diagrams are useful for describing temporal relationships for temporal signals.
Step 3
Create a natural language list of properties. In a natural language, list all properties for your design’s design component. We recommend you create a table to capture your list of properties. For each property, use a unique label identifier for each property that helps map the assertions back to the natural language properties.
Step 4
Convert natural language properties into formal properties. In this step, convert each of the natural language properties into a set of SVA (or PSL) assertions or coverage properties, using any additional modeling required for describing the intended behavior.
Chapter 3, “The Process”
39
Step 5
Figure 3-1
Encapsulate assertions inside a module or interface. In this step, we turn our set of related properties into a reusable verification component (an assertion-based monitor). We add analysis ports to our monitor for communication with other simulation verification components, providing a clear separation between assertion detection and testbench action. Assertion-based IP creation process steps
Create Block Diagram
Describe Behavior
Capture Requirements
Formalize Requirements
Encapsulate Properties
Scope of our discussion
We illustrate each of these assertion-based IP creation steps in the following chapters. Obviously, in terms of creating a final VIP product, there are many other deliverables beyond the assertion-based monitor itself (for example, documentation, quality goals, testing strategy). Each one of these important topics requires careful attention—and books could (and should) be written to address each of these topics. However, we have decided to limit the scope of our discussion in this book to the process of creating the assertion-based monitor verification component. We believe
40 Creating Assertion-Based IP
that this is a fundamental skill that should be mastered before attempting to productize VIP.
3.3 Assertion-based IP architecture In this section, we discuss architectural aspects of creating assertion-based IP verification components. We chose the Advanced Verification Methodology [Glasser et al., 2007], which is a subset of the newly formed OVM, base-class library to demonstrate our assertion-based IP creation process. We chose the OVM for our examples because the source code for the OVM library is openly available and can be downloaded at http://www.mentor.com/go/cookbook. Assuredly, there are other testbench base-class libraries available, and we encourage you to choose one that you feel comfortable with when creating your assertion-based IP. The general ideas, processes, and techniques we present in this book are easily extended to other available testbench base-class libraries. Module versus interface assertionbased IP
We begin by demonstrating how to create assertion-based IP, which is based on a module implementation, and we introduce many key concepts during this discussion (such as interfaces and analysis ports). Once we introduce all the key concepts, we discuss an alternative form of creating assertion-based IP, which is based on a SystemVerilog interface implementation. In general, we favor the interface implementation (for protocol style assertion-based IP); however, not all assertion-based IP is limited to specifying only interfaces or protocols (that is, some assertion-based IP specifies end-to-end behavior and some assertion-based IP involves multiple design components). Furthermore, there are situations where a design project might require modulebased forms of assertion-based IP (or have project restrictions on interface content). Hence, we show you how to do both.
Chapter 3, “The Process”
41
class versus module verification components
Class-based versus module-based components. Today, you will find that many modern programming languages are based on object-oriented programming concepts. Even hardware design and verification languages, such as SystemVerilog, support object-oriented capabilities using the class construct. However, what distinguishes an objectoriented program from other programming approaches is its organization. For example, an object-oriented program is a collection of interacting objects (that is, instances of classes)—where each object has its own data space and set of functionality. The object’s data is accessed by a collection of methods, which are really functions that serve as an object’s interface. In a way, you can think of each instance of a Verilog module as an object, which has its own data space, set of functionality, and well-defined interface. However, one notable difference between a module and a class is that a module is static in its existence (that is, it cannot be created dynamically during a program’s execution), and does not support type parameterization or inheritance. Inheritance is a powerful technique that lets you achieve productivity and uniformity when creating various program objects—such as different verification components within a testbench. For example, you can use a base class defined within the OVM library to derive (through inheritance) your own customized verification component. This approach takes advantage of all the existing functionality within an OVM base class (that is, without having to recreate it), which improves your productivity. In addition, accessing common methods across various base classes contained within the OVM library minimizes the learning curve involved in creating and maintaining verification components.
connecting module-based components to class-based components
Mixed verification environment. A question often arises: “Should I use classes or modules to create verification components?” The answer is—it depends. Modules are more natural for the HDL user. On the other hand, classes are more flexible with their support of inheritance for
42 Creating Assertion-Based IP
customization, they are easier to randomize than modules, and they allow flexible instantiation. While class-based verification components have a number of advantages over module-based verification components, there are still situations that require module-based verification components or justify their use. For example, to manage project resources, you might be forced to use some legacy module-based verification components from a previous project—or even purchase some new modulebased third-party verification IP. Hence, the ability to mix existing module-based verification components with newly developed class-based verification components is critically important to many projects. Another example where mixed class-based and modulebased verification components are required is the use of assertions within a testbench. That is, SystemVerilog does not allow temporal assertions within a class. Hence, it is necessary to capture our assertions within a module (or SystemVerilog interface), and then integrate our modulebased monitor with other class-based components contained within our testbench. Regardless of whether you choose to implement a classbased or module-based verification component, we can use the same communication mechanism (implemented as a class) to communicate between these mixed implementation techniques. For example, a class can be passed as inputs to a module, which then can be used to connect module-based verification components to class-based verification components. Testbench organization with assertionbased IP
Figure 3-2 illustrate the organization of a testbench that includes an assertion-based monitor, which contains an RTL design (that is, the DUV) with pin-level, timed-bus interfaces. The testbench in this example consists of a set of verification components that communicate with each other (via classes) using untimed transactions. Untimed transactions (for example, a read or a write request to a specified address) are sent to a driver transactor, whose role
Chapter 3, “The Process”
43
is to convert a stream of untimed transactions into pin-level timed activity. Figure 3-2
Assertion-based monitor interfaces
Driver
DUV
Responder
coverage AssertionBased Monitor
Coverage Collector
error status
Stimulus Generator
Test Controller
3.3.1 Module-based assertion IP The assertion-based monitor in our example is a modulebased transactor. Its role is to monitor the bus pin-level activity, and identify protocol violations and interesting sequences to be used for coverage. All bus protocol violations are reported to the testbench’s simulation controller (for appropriate action) through a status analysis port (illustrated by the diamond-shaped connector in Figure 3-2), which is a parameterized class used to transport a status class transaction. In addition, the timed pin-level 44 Creating Assertion-Based IP
activity is converted back into an untimed transaction and is passed to the coverage collector through a different analysis port.
3.3.2 SystemVerilog interface To establish a connection between our assertion-based monitor, driver transactor, and the DUV, we create a SystemVerilog interface, as illustrated in Figure 3-3. A SystemVerilog interface encapsulates the communication between multiple blocks. Hence, a SystemVerilog interface forms a single connection description that supports bus definition reuse across multiple verification components and the DUV. This means that if you make a change to the interface definition, it is reflected automatically across all bus components that share the same interface structure. Figure 3-3
SystemVerilog interface
Driver
DUV
AssertionBased Monitor
For instance, in Example 3-1 we demonstrate an interface for the design illustrated in Figure 3-3. For this case, our assertion-based monitor would reference the interface signals (for example, sel or en) via the direction defined by the monitor_mp named modport.
Chapter 3, “The Process”
45
Example 3-1
Encapsulate signals inside a SystemVerilog interface
interface tb_bus_if( input clk , input rst ); parameter int DATA_SIZE = 8; parameter int ADDR_SIZE = 8; bit sel; bit en; bit write; bit [DATA_SIZE-1:0] wdata; bit [DATA_SIZE-1:0] rdata; bit [ADDR_SIZE-1:0] addr; modport driver_mp ( input clk , rst , output sel , en , write , addr , output wdata , input rdata ); modport duv_mp input input input output );
( clk , rst , sel , en , write , addr , wdata , rdata
modport monitor_mp ( input clk , rst , input sel , en , write , addr , input wdata , input rdata ); endinterface
Top-level interface instantiation and modulebased reference
Example 3-2 demonstrate how a SystemVerilog interface is instantiated in the top module of a testbench. The interface monitor_mp modport is passed as an argument into our assertion-based monitor example, which then establishes a connection between the signal defined within the SystemVerilog interface and the monitor.
46 Creating Assertion-Based IP
Example 3-2
Instantiated SystemVerilog interface and monitor
module top; . . . tb_bus_if #( .DATA_SIZE(8), .ADDR_SIZE(8)) nonpiped_bus_if ( .clk( clk_rst_bus.clk ), .rst( clk_rst_bus.rst) ); assertion_monitor tb_monitor( .monitor_mp( nonpiped_bus_if.monitor_mp ) ); . . . endmodule
Example 3-3 demonstrates how the SystemVerilog interface monitor modport is referenced inside a module-based component, such as our assertion-based monitor example. Example 3-3
Interface modport references inside assertion monitor
module assertion_monitor ( interface.monitor_mp monitor_mp ); parameter DATA_SIZE parameter ADDR_SIZE
= 8; = 8;
bit [ADDR_SIZE-1:0] bus_addr; bit [DATA_SIZE-1:0] bus_wdata, bus_rdata; bit bus_write; . . . always @(posedge monitor_mp.clk) begin bus_addr bus_wdata bus_rdata bus_write . . . end . . . . endmodule class-based reference of an interface
= = = =
monitor_mp.addr; monitor_mp.wdata; monitor_mp.rdata; monitor_mp.write;
Thus far, we have seen how an interface is used (that is, referenced) inside a module-based component. To complete the connection of our interface, the signals must be accessible to class-based transactor components, such as the driver shown in Figure 3-2. For our testbench class-based components, we encapsulate them within a special class known as an environment class (see Appendix B, Chapter 3, “The Process”
47
“Complete OVM/AVM Testbench Example” for details), which is then instantiated within our top-level module (see env in Example 3-4). Our SystemVerilog interface is passed into the environment class constructor (new), as illustrated in Example 3-4. Example 3-4
Passing a SystemVerilog interface into a class
module top; . . . // System Verilog Interface for clock and reset clk_rst_if clk_rst_bus(); // Module-based driver for clock and reset clock_reset clock_reset_gen( clk_rst_bus ); // Environment class to encapsulate class-based testbench components tb_env env; // System Verification Interface tb_bus_if #( .DATA_SIZE(8), .ADDR_SIZE(8)) nonpiped_bus_if ( .clk( clk_rst_bus.clk ), .rst( clk_rst_bus.rst) ); // Module-based assertion monitor assertion_monitor tb_monitor( .monitor_mp( nonpiped_bus_if.monitor_mp ) ); // Module-based DUV duv my_duv ( .driver_mp( nonpiped_bus_if.driver_mp ), . . . ); initial begin // Environment class where SystemVerilog interface is passed into // its constructor env = new( nonpiped_bus_if, . . .); . . . end endmodule
Example 3-5 illustrates the relevant details for our environment class and specifically shows the SystemVerilog interface argument for the environment class 48 Creating Assertion-Based IP
constructor (new), which is assigned to our testbench classbased driver virtual interface (in the connect method)— effectively completing the connection between the driver, DUV, and assertion-based monitor. Example 3-5
Environment class constructor
class tb_env extends ovm_env; protected tb_driver p_driver; . . . virtual tb_bus_if #( .DATA_SIZE(8), .ADDR_SIZE(8) ) p_nonpiped_bus; function new( virtual tb_bus_if #(.DATA_SIZE(8),.ADDR_SIZE(8)) nonpiped_bus, . . . ); p_nonpiped_bus = nonpiped_bus; p_driver = new("drive", this); . . . endfunction function void connect; p_driver.m_bus_if = p_nonpiped_bus; // connect to transactor . . . endfunction . . . endclass
For completeness, Example 3-6 sketches out the skeleton of the driver class-based component. The m_bus_if is a virtual interface within the driver transactor. The actual interface was assigned to this virtual interface handle within the environment class as previously shown in Example 3-5. This assignment completes our connection between the module-based assertion monitor, module-based DUV, and class-based testbench driver.
Chapter 3, “The Process”
49
.
Example 3-6
Class-based driver transactor
class tb_driver extends ovm_threaded_component; virtual tb_bus_if #( .DATA_SIZE( 8 ), .ADDR_SIZE ( 8 ) ) m_bus_if; . . . . endclass
3.3.3 Analysis ports Within a contemporary testbench, a key observation is that analysis traffic (such as coverage and assertion status) is fundamentally different from operation traffic (such as design data and control information). Hence, for our assertion-based IP architecture, we introduce analysis ports, which enables us to provide a bridge between the operational domain of our testbench, which includes monitoring the DUV, and the analysis domain of our testbench. Figure 3-4
Assertion-based monitor analysis ports coverage AssertionBased Monitor
Coverage Collector
status
Test Controller
50 Creating Assertion-Based IP
An analysis port as implemented in the OVM as a classbased object broadcasts transactions to zero or more listeners within the testbench. In fact, the key benefit of an analysis port is that the assertion-based IP does not need to know the architecture details of the overall testbench to function correctly. For our case, this makes the assertionbased IP truly reusable. For example, in certain testbench architectures, the assertion-based IP might be connected to a coverage collector—while in other architectures the same assertion-based IP might be connected to a scoreboard. Still, there might be other testbench architectures where the assertion-based IP is connected to both a coverage collector and a scoreboard. To introduce the general concepts of an analysis port, we illustrate the OVM analysis port organization in Figure 3-5. This organization consists of a publisher object and a set of subscriber objects. Each subscriber verification component registers itself with the publisher verification component (such as our assertion-based IP monitor). When the publisher has some new data to publish, it notifies all the registered subscribers. Figure 3-5
Analysis port organization
subscriber[2]
subscriber[1]
subscriber[1]
analysis_if
analysis_if
analysis_if
write(tr)
write(tr)
write(tr)
analysis_port
publisher sub[0] sub[1] sub[2]
monitor write(tr)
At the beginning of simulation, each subscriber registers itself with the publisher and the publisher maintains a list of subscribers. The OVM provides the infrastructure and utilities that automatically handle the connection of the Chapter 3, “The Process”
51
subscribers to a publisher at the beginning of simulation, so the user does not have to implement these lower-level details. For additional information, Appendix B, “Complete OVM/AVM Testbench Example” provides an overview of the ovm_analysis_port class, and you can find more details in [Glasser et al., 2007]. During normal operation, the verification component that owns the analysis port calls write(), passing in a transaction object (for example, coverage information or assertion status information). The analysis port then passes a copy of the transaction object to each subscriber. To help illustrate how the analysis port is used (and connected) within a testbench, we begin by modifying our simple assertion-based monitor example previously shown in Example 3-3 to include an error status analysis port. Example 3-7
An OVM parameterized analysis port of type tb_status
module assertions( interface.monitor_mp monitor_mp ); ovm_analysis_port #(tb_status) status_ap = new("status_ap", null); . . . tb_status status; . . . property p_valid_inactive_transition; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) ( bus_inactive) |=> (( bus_inactive) || (bus_start)); endproperty a_valid_inactive_transition: assert property (p_valid_inactive_transition) else begin status = new(); status.set_err_trans_inactive(); status_ap.write(status); end . . . endmodule
An ovm_analysis_port is a parameterized class. For our example (shown in Example 3-7) it is parameterized to be a tb_status class, which we defined in Example 3-8. If the 52 Creating Assertion-Based IP
assertion a_valid_inactive_transition in Example 3-7 evaluates false, then a status object is created. Next, the specific error condition is flagged by calling the appropriate error status method. For example, in our case, the tb_status class err_trans_inactive method is called. Finally, the status object is passed to the analysis port where it is then broadcast to any (and all) registered subscribers. Note that in terms of assertion status, we never call the analysis port if the assertion evaluates true. Calling an analysis port at every assertion clock edge creates too much activity within a testbench. Example 3-8
IA tb_status class
class tb_status extends ovm_transaction; typedef enum { ERR_TRANS_RESET , ERR_TRANS_INACTIVE , ERR_TRANS_START , ERR_TRANS_ACTIVE , ERR_TRANS_ERROR , ERR_STABLE_SEL , ERR_STABLE_ADDR , ERR_STABLE_WRITE , ERR_STABLE_WDATA } bus_status_t; bus_status_t bus_status; . . . . function void set_err_trans_inactive; bus_status = ERR_TRANS_INACTIVE; endfunction . . . . endclass
3.3.4 Interface-based assertion IP We demonstrated in the previous sections how to create and connect assertion-based IP based on a module implementation. In this section, we demonstrate how to create and connect assertion-based IP based on a Chapter 3, “The Process”
53
SystemVerilog interface implementation. As we previously mentioned, delivering interface-based assertion IP is the preferred method. However, there are cases (such as end-toend assertion-based IP or project interface restrictions) that require module-based assertion IP. Example 3-9 demonstrates assertion-based IP delivered in an interface implementation. Example 3-9
Encapsulate analysis ports inside interface
interface tb_bus_if( input clk , input rst ); . . . ovm_analysis_port #(tb_coverage) cov_ap = new(“cov_ap”, null); property p_burst_size; int psize; @(posedge clk) ((bus_inactive), psize=0) ##1 ((bus_start, psize++, build_transaction(psize)) ##1 (bus_active))[*1:$] ##1 (bus_inactive); endproperty cover property (p_burst_size); function void build_transaction(int psize); tb_coverage_tr tr; tr = new(); if (bus_write) begin tr.set_write(); tr.data = bus_wdata; end else begin tr.set_read(); tr.data = bus_rdata; end tr.burst_count = psize; tr.addr = bus_addr; cov_ap.write(tr); endfunction endinterface
The SystemVerilog interface contains all required bus interface assertion and coverage properties. We have added analysis ports to the SystemVerilog interface (compared with the simpler interface shown in Example 3-1). We 54 Creating Assertion-Based IP
demonstrate how to connect to these analysis ports in the following examples. Example 3-9 demonstrates a SystemVerilog interface with a coverage property. In this case, the coverage property reconstructs a multicycle bus sequence into a transaction, which then broadcasts to an analysis port. The transaction contains the type of bus operation (for example, a read or a write), the address, and the data value. In addition, the transaction contains the particular burst transaction sequence number. Example 3-10 demonstrates the top level module for our testbench. Example 3-10
Passing an interface with assertions into a class
module top; . . . // System Verilog Interface for clock and reset clk_rst_if clk_rst_bus(); // Module-based driver for clock and reset clock_reset clock_reset_gen( clk_rst_bus ); // Environment class to encapsulate class-based testbench components tb_env env; // System Verification Interface tb_bus_if #( .DATA_SIZE(8), .ADDR_SIZE(8)) nonpiped_bus_if ( .clk( clk_rst_bus.clk ), .rst( clk_rst_bus.rst) ); // Module-based DUV duv my_duv ( .driver_mp( nonpiped_bus_if.driver_mp ), . . . ); initial begin // Environment class where SystemVerilog interface is passed into // its constructor env = new( nonpiped_bus_if, . . .); . . . end endmodule
Chapter 3, “The Process”
55
Essentially, the only difference between this top-level module and the top-level module in Example 3-4 is that we no longer instantiate an assertion monitor into the top module of the testbench (since our assertions are now encapsulated within the SystemVerilog interface). Example 3-11 demonstrates how a class-based coverage collector component subscribes to an analysis port contained within a SystemVerilog interface, which was passed into the environment class constructor. Recall that all the testbench class-based components are instantiated within an environment class. Example 3-11
Environment class constructor with analysis ports
class tb_env extends ovm_env; protected tb_cov_colr . . .
p_cov_col;
ovm_analysis_port #(tb_coverage) p_if_cov_ap = new(“cov_ap”, null); function new( virtual tb_bus_if #(.DATA_SIZE(8),.ADDR_SIZE(8)) nonpiped_bus_if, . . . ); p_if_cov_ap = nonpiped_bus_if.cov_ap; . . . // instantiate a class-based coverage collector p_cov_col = new("cov_col", this); endfunction function void connect; // coverage collect subscribes to interface analysis port p_if_cov_ap.register(p_cov_col.analysis_export); . . . endfunction . . . endclass
56 Creating Assertion-Based IP
3.4 Guidelines and conventions We highly recommend that you adopt a set of SVA naming conventions associated with declaration names and directive labels, so that when your verification IP consumers read your SystemVerilog assertions, they are able to quickly distinguish internal verification IP names from HDL names. For our examples, we have adopted the naming convention guidelines defined in Table 3-1. Table 3-1 SVA naming convention guidelines for verification IP Convention
Use
p_name
p_ is a prefix for property declaration names
s_name
s_ is a prefix for sequence declaration names
a_label
a_ is a prefix for assert directive labels
m_label
m_ is a prefix for assume directive labels (for formal)
c_label
c_ is a prefix for cover directive labels
3.5 Summary In this chapter, we introduced a set of guiding principles for creating assertion-based IP. Specifically, we discussed the importance of modularity and clarity when creating assertion-based IP. We then presented a systematic set of steps to help you effectively create your assertion-based IP. To demonstrate how to architect your assertion-based IP to achieve a clear separation between assertion (and coverage) detection from action, we sketched out both a module-based and interface-based assertion IP implementation using the OVM base-class library elements. The concepts we presented in this chapter are easily extended to other baseclass libraries that might be available to you.
Chapter 3, “The Process”
57
C
H A P T E R
BUS-BASED DESIGN EXAMPLE
CHAPTER 4
To set a framework for our discussion, this chapter introduces a typical SoC bus-based design example that consists of various common design components. Each of the following chapters demonstrate the assertion-based IP creation process on many common design components found in our bus-based design example. Why did we choose an SoC bus-based design example? Our goal is to tie the process of creating assertion-based verification IP to a real design example that you might encounter on a daily basis. With the increased pressure on time-to-market, rapidly changing requirements, and increasing design and manufacturing costs, SoC bus-based design methodologies have recently emerged as a means to address many of the shortcomings of traditional design approaches. For example, SoC bus-based design methodologies allow you to integrate new features relatively quickly by selecting third-party IP. Multiple IP design components are often interconnected using standard interfaces combined with bus-based design techniques.
Chapter 4, “Bus-Based Design Example”
59
4.1 Bus-based design overview Figure 4-1 illustrates a typical SoC bus-based design example consisting of various functional design components, which are all interconnected using a common bus. For example, an arbiter component manages the resource use of Bus A to ensure that only one CPU bus master initiates a data transfer at a time. In general, you can implement any arbitration scheme, such as high priority or fair access, depending on the SoC application requirements. Our example also contains a bridge component, which connects the SoC's internal Pipelined Bus to a Nonpipelined Bus, and a second bridge component, which connects the SoC's internal Nonpipelined Bus to a Serial Bus. The bridge manages the transport of data between these buses. Nearly all SoC designs today have multiple busses, which generally consists of four major functional design components directly or indirectly connected to these busses. Hence, it makes sense for us to organize our chapter discussions into the following set of common design components: •
Interfaces (see Chapter 5, “Interfaces”)
•
Arbiters (see Chapter 6, “Arbiters”)
•
Controllers (see Chapter 7, “Controllers”)
•
Datapath (see Chapter 8, “Datapath”)
Figure 4-1
A typical SoC bus-based design
CPU
UART
Pipelined Bus
Nonpipelined Bus Bridge
Arbiter
Memory Controller
Serial Bus Bridge
Timer
The following chapters demonstrate how to create assertionbased IP for various classes of interfaces, arbiters, 60 Creating Assertion-Based IP
controllers, and datapath components that are typically found in a SoC bus-based design. We apply the process and other methodological guidelines introduced in Chapter 3, “The Process” to create assertion-based IP. There are many process similarities for creating assertion-based IP across each of the various types of design components discussed in the following chapters. However, there are some unique process steps required for specific types of design components, which we cover in their appropriate chapters.
4.2 Summary In this chapter we introduced a typical SoC bus-based design example that consists of various common design components. Each of the following chapters demonstrate the assertion-based IP creation process on many of the common design components found in our bus-based design example.
Chapter 4, “Bus-Based Design Example”
61
C
H A P T E R
INTERFACES
CHAPTER 5
On-chip busses and standard interfaces serve as the framework for platform-based SoC designs, effectively providing the mortar that binds IP blocks together. In fact, on-chip bus protocols such as the ARM AMBA Advanced High-performance Bus (AHB) [AMBA 1999] protocol and the Open Core Protocol (OCP) [OCP 2003] form the foundation for many of today’s design reuse strategies. This chapter demonstrates our process of creating assertionbased IP for the three generic bus interfaces illustrated in Figure 5-1:
Figure 5-1
•
Simple serial bus interface
•
Simple nonpipelined bus interface
•
Simple pipelined bus interface
Standard interfaces for three common bus protocols
CPU
UART
Pipelined Bus
Nonpipelined Bus Bridge
Arbiter
Memory Controller
Serial Bus Bridge
Timer
Chapter 5, “Interfaces”
63
In this chapter, you will note that we are following a standard development pattern previously defined in Chapter 3. Each section within this chapter was defined to stand on its on. Hence, you might noticed repetitive text in portions of the chapter.
5.1 Simple generic serial bus interface This section introduces a simple generic serial bus interface example, which is loosely based on a subset of the Inter-IC Bus (I2C) protocol [I2C 2000]. We ask you to focus on the techniques for creating interface assertions. By using our generic, simple serial bus protocol design example, we hope that you will be able to set aside the details of particular standards that could otherwise overwhelm you and distract you from the learning objectives. After understanding the process we present, you should be able to extend these ideas to create assertion-based verification IP for other proprietary and real standard serial bus interfaces.
5.1.1 Block diagram interface description Figure 5-2 illustrates a simple serial bus-based design. The simple serial bus protocol we present provides good support for communication with multiple peripheral components that are accessed intermittently, while being extremely modest in their hardware resource needs. It is a lowbandwidth protocol that allows linking multiple devices through a simple built-in addressing scheme.
64 Creating Assertion-Based IP
Figure 5-2
A simple serial bus design
SBD SBC I/F Peripheral #1
Peripheral #2
...
Peripheral #N
Controller
As illustrated in Figure 5-2, our serial bus-based design consists of a two-bit serial bus—a serial data (SBD) bit and serial clock enable (SBC) bit (see Table 5-1). Together, these signals support a serial protocol transmission of eightbit bytes of data, seven-bit device addresses, and control bits over the two-bit serial bus. There is no need for a bus select signal or arbitration logic for our serial bus protocol, making it inexpensive and simple to implement in hardware Table 5-1 Serial bus signal description. Name
Description
SBD
Serial bus data signal
SBC
Serial bus clock signal
5.1.2 Overview description The design component that initiates a bus transaction is the master, which normally controls the clock signal. The design component addressed by the master is a slave. During a transaction, the slave component can pause the master by using a technique known as clock stretching (that is, the slave forces SBC low until it is ready to continue). Each serial bus slave component is assigned a predefined bus address. The master transmits the address of the slave it intends to communicate with onto the bus at the beginning Chapter 5, “Interfaces”
65
of every transaction. Each slave monitors the bus and responds only to its own address. Figure 5-3
A simple serial bus transaction R A / C W K
S MSB
LSB
A C K MSB
Address
LSB
A C K MSB
Data
F
LSB
Data
Simple serial bus transaction Figure 5-3 illustrates a typical transaction for our simple serial bus protocol. The master begins the transaction by issuing the start command (S), along with a unique sevenbit slave component address.1 Then, the bit transmitted after the address specifies the type of transaction (that is, R/W). When the master sets R/W to zero, data will be written to the slave. When the master sets R/W to one, data will be read from the slave. Next, the selected slave transmits an ACK, indicating the receipt of the previous byte. The transmitter (either the slave or master depending on the type of transaction) then starts the transfer of a byte of data a bit at a time, beginning with the MSB. After completing the data transfer, the receiver issues an ACK. This data transmit pattern can be repeated, if required, to transmit a contiguous stream of data, without needing to transmit a new address before each data byte. After a transaction completes, the master issues a finish command (F).2
1. Our simple serial bus commands start (S), ACK, and finish (F) are defined by a unique rising or falling edge phase relationship between the SBD when SBC is high (similar to I2C). The details are not important for our discussion. We can assume that our bus interface contains the required circuitry to detect these unique phase relationships, and then decode an appropriate bus command. 2. Our serial bus differs from a real standard serial bus protocol, such as the I2C, in that out simple example does not support NAK, restart, global call, and other features. Our goal is to present a simple example to illustrate the assertion-based IP development process. 66 Creating Assertion-Based IP
5.1.3 Natural language properties Our first task when creating a set of interface assertions for our simple serial bus is to identify a comprehensive list of natural language properties. You might ask the question where do the properties come from? In general, properties can be extracted from your proprietary or standard serial bus specification. Table 5-2, although certainly not a comprehensive list of properties for a real serial bus, is a representative list of properties that we will use to demonstrate our process. You might note that the data integrity class of properties is missing from this list. This class of properties is discussed separately in Chapter 8. Table 5-2 Simple serial bus properties Assertion name
Summary
Handshaking properties a_bus_reset
After a reset, the SBD and SBC signals must be driven high
a_no_double_start
A new start command cannot be issued until a finish command is issued1
a_no_finish_before_start
A single finish command can only be issued after a start.
Valid serial transaction property a_valid_transfer_size
A serial transaction consists of a start, 7bits of address, R/W command, ACK, followed by a sequence of 8-bit transfers with an ACK, and a finish
1. Our simple example, unlike the I2C serial bus, does not support a restart. Hence, we have added this property to illustrate how to write an assertion to check for illegal back-to-back events.
Chapter 5, “Interfaces”
67
Figure 5-4
SystemVerilog interface
Driver
DUV
AssertionBased Monitor
5.1.4 Assertions To create a set of SystemVerilog assertions for our simple nonpipelined bus, we begin defining the connection between our assertion-based monitor and other potential components within the testbench (such as a driver transactor and the DUV). To accomplish this task, we create a SystemVerilog interface, as illustrated in Figure 5-4 (see page 68). Example 5-1 demonstrates an interface for our serial bus example. For this case, our assertion-based monitor references the interface signals in the direction defined by the monitor_mp named modport.
68 Creating Assertion-Based IP
.
Example 5-1
Encapsulate bus signals inside a SV interface
interface tb_serial_bus_if( input clk , input rst ); bit bit bit bit bit
SBC; SBD; start; finish; ack;
modport driver_mp ( . . . ); modport duv_mp ( . . . ); modport monitor_mp ( input sclk , rst , input SBC , SBD , input start , finish , ack ); endinterface
In addition to pin-level interfaces (which monitor the bus) we need to define the analysis port interface, as illustrated in Figure 5-5, which broadcasts an error status transaction upon detecting a serial bus error. Example 5-2 defines the error status transaction class, which is an extension on an ovm_transaction base class. There are a few standard utility methods that must be defined for the ovm_transaction, which include a transaction copy and compare method (see Appendix B, “Complete OVM/AVM Testbench Example” for additional details). The tb_status_sb class in Example 5-2 includes an enum that identifies the various types of serial bus errors and a set of methods to uniquely identify the specific error it detected.
Chapter 5, “Interfaces”
69
Figure 5-5
A simple serial bus transaction
Test Controller
error status
AssertionBased Monitor
SBD SBC
minimize an assertionbased monitor’s knowledge of a particular simulation architecture
By introducing an error status transaction object into our assertion-based monitor, which is broadcast through an analysis port upon detecting an error, our monitor does not need to know any details on how a particular simulation architecture represents an error condition within its environment. This makes the assertion-based monitor truly reusable across many different simulation architectures.
clocking and reset
We are now ready to write our assertions for the serial bus properties defined by Table 5-2 (see page 67). For our examples, we introduce the signals sclk and rst, which are not part of the serial bus definition. However, to simplify our example, we assume that these signals (or similar signals) exist within the bus interface for the various design components attached to the bus.
70 Creating Assertion-Based IP
Example 5-2
Error status transaction class
class tb_status_sb extends ovm_transaction; typedef enum { ERR_BUS_RESET , ERR_NO_DOUBLE_START , ERR_NO_FINISH_BEFORE_START , ERR_VALID_DATA_SIZE } bus_status_t; bus_status_t bus_status; // Standard OVM transaction methods here, not important for our // discussion. Reference Section B.1.7 (see page 287) for more // details on these standard methods . . . // Error status transaction methods function void set_err_bus_reset; bus_status = ERR_BUS_RESET; endfunction function void set_err_no_double_start; bus_status = ERR_NO_DOUBLE_START; endfunction function void set_err_no_finish_before_start; bus_status = ERR_NO_FINISH_BEFORE_START; endfunction function void set_err_valid_transfer_size; bus_status = ERR_VALID_TRANSFER_SIZE; endfunction endclass serial bus reset assertion
Our first assertion states that after a reset, the serial bus signals SBD and SBC must be driven high. Hence, we can express this property in SVA as shown in Assertion 5-1. If an error is detected, an error status transaction object (previously declared within the body of the assertion-based monitor) is constructed within the action block (that is, else clause) of the assertion. Then the set_err_bus_reset method is called to identify the specific serial bus error. Finally, the error status object is broadcast out through an analysis port (using the write() method) to any verification component that had previously subscribed to the assertion-based monitor’s analysis port (see section 3.3.3 "Analysis ports" on page 50 for details).
Chapter 5, “Interfaces”
71
.
Assertion 5-1
Serial bus reset condition
. . . tb_status_sb status; // see Example 5-3 on page 75 for more details . . . property p_bus_reset; @(posedge monitor_mp.sclk) $fell(monitor_mp.rst) |-> (monitor_mp.SBD==1'b1 & monitor_mp.SBC==1'b1); endproperty a_bus_reset: assert property (p_bus_reset) else begin status = new(); status.set_err_bus_reset(); status_ap.write(status); end
On the surface, you might argue that all this extra coding in the action block is unnecessary complexity! If this was an assertion embedded in the design, we would agree with you. However, our goal is to create a reusable verification component that must interact with other verification components within a testbench. Hence, adding an error status analysis port to our monitor and then having the assertions report errors through this analysis port helps us achieve our goal of reuse. Table 5-2 defines our second assertion (a_no_double_start), which is shown in Assertion 5-2. Although the start signal is not defined as part of our serial bus protocol, to simplify our example, we assume it exists inside the serial interface and is asserted when the interface logic decodes a serial bus start command (S). Likewise, the finish signal asserts when the logic decodes the serial bus finish command (F) and a serial bus acknowledge (ACK).
72 Creating Assertion-Based IP
.
Assertion 5-2
No double start property
property p_no_double_start; @(posedge monitor_mp.sclk) disable iff (monitor_mp.rst) monitor_mp.start |=> (~monitor_mp.start throughout monitor_mp.finish[->1]); endproperty a_no_double_start: assert property (p_no_double_start)else begin status = new(); status.set_err_no_double_start(); status_ap.write(status); end
For our third property, no finish before start, we must handle a boundary condition that ensures finish is not asserted until the first occurrence of start after a reset. Hence, in Assertion 5-3 we have added the extra a_no_finish_until_start_init property to handle this boundary condition. .
Assertion 5-3
No finish before start property
// Boundary condition property p_no_finish_until_start_init; @(posedge monitor_mp.sclk) $fell(monitor_mp.rst) |-> ~monitor_mp.finish throughout monitor_mp.start[->1]; endproperty a_no_finish_until_start_init: assert property (p_no_finish_until_start_init) else begin status = new(); status.set_err_no_finish_until_start(); status_ap.write(status); end // Normal condition property p_no_finish_before_start; @(posedge monitor_mp.sclk) disable iff (monitor_mp.rst) monitor_mp.finish |=> (~monitor_mp.finish throughout monitor_mp.start[->1]); endproperty a_no_finish_before_start: assert property (p_no_finish_before_start) else begin status = new(); status.set_err_no_finish_until_start(); status_ap.write(status); end
Chapter 5, “Interfaces”
73
Since we are really checking the same property, we did not introduce a separate error condition as part of the error status transaction enum (although we could have). Refer to Example 5-2 on page 71. Table 5-2 defines our fourth assertion (a_valid_transfer_size). We can express a SystemVerilog assertion as shown in Assertion 5-4. Assertion 5-4
Valid transfer size property
property p_valid_transfer_size; @(posedge monitor_mp.sclk) disable iff (monitor_mp.rst) monitor_mp.start |=> // repeat minimum of two times for address followed by data phase ($rose(monitor_mp.SBC)[=8] ##1 monitor_mp.ack)[*2:$] ##1 (monitor_mp.finish); endproperty a_valid_data_size: assert property (p_valid_transfer_size) else begin status = new(); status.set_err_valid_transfer_size(); status_ap.write(status); end
As previously discussed, the serial bus interface start and signals are not defined by the serial bus protocol. They exist inside the serial interface and are asserted when the interface logic decodes a serial bus start command (S) or a finish command (F). Likewise, the serial bus interface ack signal is asserted when the interface logic decodes a serial bus ack command (ACK). In addition, for our SystemVerilog example in Example 5-4, the serial bus interface contains a posedge clock (sclk) and an active high reset (rst). The master (and slave) interface’s internal data sample signal is asserted upon synchronizing the interface’s internal clock (sclk) with the bus’s SBC signal, which represents a valid time to sample the bus data. finish
74 Creating Assertion-Based IP
5.1.5 Encapsulate properties In this step, we turn our set of related serial bus interface assertions (and any defined coverage properties, not demonstrated in this example) into a reusable assertionbased monitor verification component. We add analysis ports to our monitor for communication with other simulation verification components as demonstrated in Chapter 3, “The Process.” Example 5-3
Encapsulate properties inside a module or interface
import ovm_pkg::*; import tb_tr_pkg::*; // tb_status_sb class definition module serial_bus_mon( interface.monitor_mp monitor_mp ); ovm_analysis_port #(tb_status_sb) status_ap = new("status_ap", null); . . . tb_status_sb status; // error status object . . . // Any required modeling code here (to model environment) . . . // All assertions and coverage properties here . . . endmodule
5.2 Simple generic nonpipelined bus interface This section introduces a simple, generic nonpipelined bus interface example, which is loosely based on a subset of the AMBA Advanced Peripheral Bus (APB) protocol [AMBA 1999]. Our goal in this section is to demonstrate the process of creating an assertion-based IP verification component versus teaching you all the details about a particular industry protocol standard. We ask you to focus on the techniques for creating interface assertions. By using our generic, simple nonpipelined bus protocol design, we hope that you will be able to set aside the details of particular standards that could otherwise overwhelm you and distract you from the learning objectives. After understanding the process we present, you Chapter 5, “Interfaces”
75
should be able to extend these ideas to create assertionbased verification IP for other proprietary and standard nonpipelined bus interfaces. If you are interested in seeing specific property examples for the AMBA protocol (verses our generic examples), there have been numerous papers and books published that provide these details (a few examples include [Marschner 2002] [Ruah et al., 2005] [Susantol and Melham 2003]). In addition, there are commercial AMBA VIP solutions available for purchase.
5.2.1 Block diagram interface description Figure 5-6 illustrates a block diagram for a simple nonpipelined bus design. For our example, all signal transitions relate only to the rising edge of the bus clock (clk). Table 5-3 provides a summary of the bus signals for our simple nonpipelined bus interface example. Figure 5-6
Simple nonpipelined bus design clk rst sel en write Master
Slave addr rdata wdata
76 Creating Assertion-Based IP
Table 5-3 Simple nonpipelined bus signal description Name
Description
clk
All bus transfers occur on the rising edge of clk An active high bus reset These signals indicate that a slave has been selected. Each slave has its own select (for example, sel[0] for slave 0). However, for our simple example, we assume a single slave Strobe for active phase of bus When high, write access When low, read access Address bus Read data bus driven when write is low Write data bus driven when write is high
rst sel
en write addr[7:0] rdata[7:0] wdata[7:0]
Figure 5-7
Simple nonpipelined bus conceptual states
no transfer
INACTIVE sel == 0 en == 0 setup START no transfer
sel == 1 en == 0 transfer
setup
ACTIVE sel == 1 en == 1
5.2.2 Overview description We use a conceptual state-machine, illustrated in Figure 5-7, to describe the operation of the nonpipelined bus (for a
Chapter 5, “Interfaces”
77
single slave). Essentially, our state-machine maps bus control values to conceptual states. After a reset (that is, rst==1’b1), our simple nonpipelined bus is initialized to its default INACTIVE state, which means both sel and en are de-asserted. To initiate a transfer, the bus controls moves into the START state, where the master asserts a slave select signal, sel, selecting a single slave component. To further simplify our example, we assume that there is only a single slave (sel). The bus only remains in the START state for one clock cycle, and will then move to the ACTIVE state on the next rising edge of the clock. The ACTIVE state lasts a single clock cycle for the data transfer. Then, the bus will move back to the START state if another transfer is required, which is indicated when the selection signal remains asserted. Alternatively, if no additional transfers are required, the bus moves back to the INACTIVE state when the master deasserts the slave’s select and bus enable signals. The address (addr[7:0]), write control (write), and transfer enable (en) signals are required to remain stable during the transition from START to ACTIVE states. However, it is not required that these signals remain stable during the transition from ACTIVE back to the START state.
Basic write operation Figure 5-8 illustrates a basic write operation for our simple nonpipelined bus interface involving a bus master and a single slave. At clock one, since both the slave select (sel) and bus enable (en) signals are de-asserted, our bus controls are in an INACTIVE state, as we previously defined in our conceptual state-machine (see Figure 5-6) and illustrated in Figure 5-8. The state variable in Figure 5-8 is actually a conceptual state of the bus with respect to the bus controls, not a physical state implemented in the design.
78 Creating Assertion-Based IP
Figure 5-8
Non-burst write transaction 0
1
2
3
4
write sel en addr
ADDR 1
wdata
DATA 1
state
INACTIVE
START
ACTIVE
INACTIVE
The first cycle of the transfer is called the START cycle, which the master initiates by asserting one of the slave select lines. For our example, the master asserts sel, and this event is detected by the rising edge of clock two. During the START cycle, the master places a valid address on the bus and in the next cycle, places valid data on the bus. This data will be written to the currently selected slave component. The data transfer (referred to as the ACTIVE cycle) actually occurs when the master asserts the bus enable signal. In our case, it is detected on the rising edge of clock three. The address, data, and control signals all remain valid throughout the ACTIVE cycle. When the ACTIVE cycle completes, the bus master deasserts the bus enable signal (en), and thus completes the current single-cycle write operation. If the master has finished transferring all data to the slave (that is, there are no more write operations), then the master de-asserts the slave select signal (that is, sel). Otherwise, the slave select signal remains asserted, and the bus returns to the START cycle to initiate another write operation. It is not necessary for the address data values to remain valid during the
Chapter 5, “Interfaces”
79
transition from the ACTIVE cycle back to the START cycle.
Basic read operation Figure 5-9 illustrates a basic read operation for our simple bus interface involving a bus master and slave zero (sel). Figure 5-9
Non-burst read transaction 0
1
2
3
4
write sel en ADDR 1
addr
DATA 1
rdata
state
INACTIVE
START
ACTIVE
INACTIVE
Just like the write operation, since both the slave select (sel) and bus enable (en) signals are de-asserted at clock one, our bus is in an INACTIVE state, as we previously defined in our conceptual state machine (see Figure 5-6). The timing of the address, write, select, and enable signals are all the same for the read operation as they were for the write operation. In the case of a read, the slave must place the data on the bus for the master to access during the ACTIVE cycle, which Figure 5-9 illustrates at clock three. Like the write operation, back-to-back read operations are permitted from a previously selected slave. However, the bus must always return to the START cycle after each ACTIVE cycle completes.
80 Creating Assertion-Based IP
5.2.3 Natural language properties Prior to creating a set of SystemVerilog interface assertions for our simple nonpipelined bus, we must identify a comprehensive list of natural language properties. We begin by classifying the properties into categories, as shown in Table 5-4. The first category relates to properties associated with bus state transitions, while the remaining categories relate to properties associated with specific bus interface signals. Although the set of properties found in this table is not necessarily comprehensive, it is representative of a real set of properties and sufficient to demonstrate our process. Table 5-4 Simple nonpipelined bus interface properties Assertion Name
Summary
Bus legal state a_state_reset_inactive
INACTIVE is the initial state after reset
a_valid_inactive_transition
INACTIVE or START follows INACTIVE
a_valid_start_transition
ACTIVE state follows START
a_valid_active_transition
INACTIVE or START follows ACTIVE
a_no_error_state
Bus sel and en must be in a valid state
Bus select From START to ACTIVE, sel is stable
a_sel_stable
Bus address a_addr_stable
From START to ACTIVE, addr is stable
Bus write control From START to ACTIVE, write is stable
a_write_stable
Bus data a_wdata_stable
From START to ACTIVE, wdata is stable
5.2.4 Assertions To create a set of SystemVerilog assertions for our simple nonpipelined bus, we begin defining the connection between our assertion-based monitor and other potential Chapter 5, “Interfaces”
81
components within the testbench (such as a driver transactor and the DUV). To accomplish this task, we create a SystemVerilog interface, as illustrated in Figure 5-10. Figure 5-10
SystemVerilog interface
Driver
DUV
AssertionBased Monitor
Example 5-4
Encapsulate bus signals inside a SV interface
interface tb_nonpipelined_if( input clk , input rst ); parameter int DATA_SIZE = 8; parameter int ADDR_SIZE = 8; bit sel; bit en; bit write; bit [DATA_SIZE-1:0] wdata; bit [DATA_SIZE-1:0] rdata; bit [ADDR_SIZE-1:0] addr; modport driver_mp ( . . . ); modport duv_mp ( . . . ); modport monitor_mp ( input clk , rst , input sel , en , write , addr , input wdata , input rdata ); endinterface
82 Creating Assertion-Based IP
Example 5-4 (see page 82) demonstrates an interface for our nonpipelined bus example. For this case, our assertionbased monitor references the interface signals in the direction defined by the monitor_mp named modport. In addition to pin-level interfaces (that monitor the bus) we need a means for our nonpipelined bus assertion-based monitor to communicate with various analysis verification components within the testbench. Hence, we introduce an error status analysis port within the monitor, as Figure 5-11 illustrates. In addition to assertion error status, coverage events provide an important piece of analysis data that requires its own analysis port. We discuss coverage with respect to creating assertion-based IP separately in Section 5.4. Figure 5-11
Error status and coverage analysis ports coverage AssertionBased Monitor
Coverage Collector
error status
Test Controller
Upon detecting a bus error, the analysis port broadcasts the error condition (using an error status transaction object) to other verification components. Example 5-5 defines the error status transaction class, which is an extension on an ovm_transaction base class. The tb_status_nb class includes an enum that identifies the various types of nonpipelined bus errors and a set of methods to uniquely identify the specific error it detected.
Chapter 5, “Interfaces”
83
Example 5-5
Error status class
class tb_status_nb extends ovm_transaction; typedef enum { ERR_TRANS_RESET , ERR_TRANS_INACTIVE , ERR_TRANS_START , ERR_TRANS_ACTIVE , ERR_TRANS_ERROR , ERR_STABLE_SEL , ERR_STABLE_ADDR , ERR_STABLE_WRITE , ERR_STABLE_WDATA } bus_status_t; bus_status_t
bus_status;
// Standard OVM transaction methods here, not important for our // discussion. Reference Section B.1.7 (see page 287) for more // details on these standard methods . . . // Error status transaction methods function void set_err_trans_reset; bus_status = ERR_TRANS_RESET; endfunction function void set_err_trans_inactive; bus_status = ERR_TRANS_INACTIVE; endfunction function void set_err_trans_start; bus_status = ERR_TRANS_START; endfunction function void set_err_trans_active; bus_status = ERR_TRANS_ACTIVE; endfunction function void set_err_trans_error; bus_status = ERR_TRANS_ERROR; endfunction . . . endclass
To simplify writing our assertion (and to increase the clarity), we create some modeling code (see Example 5-6) to map the sel and en bus control signals to the conceptual bus states (see Figure 5-7). We then write a set of assertions to detect protocol violations by monitoring illegal bus state transitions related to these conceptual states.
84 Creating Assertion-Based IP
Example 5-6
Modeling to map control signals to conceptual states
module tb_nonpipelined_mon( interface.monitor_mp monitor_mp ); ovm_analysis_port #(tb_status_nb) status_ap = new(“status_ap”, null); . . . parameter DATA_SIZE = 8; parameter ADDR_SIZE = 8; tb_status_nb status; // Used to decode bus control signals bit bit bit
[ADDR_SIZE-1:0] bus_addr; [DATA_SIZE-1:0] bus_wdata; [DATA_SIZE-1:0] bus_rdata;
bit bit bit bit
bus_write; bus_sel; bus_en; bus_reset;
bit bit bit bit
bus_inactive; bus_start; bus_active; bus_error;
// Identify conceptual states from bus control signals always @(posedge monitor_mp.clk) begin bus_addr bus_wdata bus_rdata bus_write bus_reset
= = = = =
monitor_mp.addr; monitor_mp.wdata; monitor_mp.rdata; monitor_mp.write; monitor_mp.rst;
if (monitor_mp.rst) begin bus_inactive = 1; bus_start = 0; bus_active = 0; bus_error = 0; end else begin // decode sel and en to conceptual states bus_inactive = ~monitor_mp.sel & ~monitor_mp.en; bus_start = monitor_mp.sel & ~monitor_mp.en; bus_active = monitor_mp.sel & monitor_mp.en; bus_error = ~monitor_mp.sel & monitor_mp.en; end end . . . endmodule
Chapter 5, “Interfaces”
85
We are now ready to write assertions for the bus interface properties that Table 5-4 (see page 81) defines. The first property states that after a reset, the bus must be initialized to an INACTIVE state (which means the sel and en signals are de-asserted). Assertion 5-5 demonstrates a SystemVerilog assertion for this property. Assertion 5-5
Bus must reset to INACTIVE state property
property p_state_reset_inactive; @(posedge monitor_mp.clk) $fell(bus_reset) |-> (bus_inactive); endproperty a_state_reset_inactive: assert property (p_state_reset_inactive) else begin status = new(); status.set_err_trans_reset(); status_ap.write(status); end
Once again, you might argue that all this extra coding in the action block is unnecessary complexity! If this was an embedded assertion within the design, we would agree with you. However, our goal is to create a reusable verification component that must interact with other verification components within a testbench. Hence, adding an error status analysis port to our monitor, and then having the assertions report errors through this analysis port helps us achieve our goal of reuse. We can write assertions for all the bus legal state properties that Table 5-4 defines, as shown in Assertion 5-6 and defined by our conceptual state-machine. Assertion a_valid_inactive_transition specifies that if the bus is currently in an INACTIVE state, then the next state of the bus must be either INACTIVE again or a START state. Assertion a_valid_start_transition specifies that if the bus is currently in a START state, then on the next clock cycle, the bus must be in an ACTIVE state.
86 Creating Assertion-Based IP
Assertion a_no_active_to_active specifies that if the bus is in an ACTIVE state, then on the next clock cycle, the bus must be in either an INACTIVE or START state. Assertion 5-6
Assertions for bus legal state properties
property p_valid_inactive_transition; @(posedge monitor_mp.clk) disable iff (bus_reset) ( bus_inactive ) |=> (( bus_inactive) || (bus_start)); endproperty a_valid_inactive_transition: assert property (p_valid_inactive_transition) else begin status = new(); status.set_err_trans_inactive(); status_ap.write(status); end property p_valid_start_transition; @(posedge monitor_mp.clk) disable iff (bus_reset) (bus_start) |=> (bus_active); endproperty a_valid_start_transition: assert property (p_valid_start_transition) else begin status = new(); status.set_err_trans_start(); status_ap.write(status); end property p_valid_active_transition; @(posedge monitor_mp.clk) disable iff (bus_reset) (bus_active) |=> (( bus_inactive ) || (bus_start)); endproperty a_valid_active_transition: assert property (p_valid_active_transition) else begin status = new(); status.set_err_trans_active(); status_ap.write(status); end property p_valid_error_transition; @(posedge monitor_mp.clk) disable iff (bus_reset) (~bus_error); endproperty a_valid_error_transition: assert property (p_valid_error_transition) else begin status = new(); status.set_err_trans_error(); status_ap.write(status); end
Chapter 5, “Interfaces”
87
Finally, a_no_error_state shown in Assertion 5-6 specifies that only valid combinations of sel and en are permitted on the bus. Assertion 5-7
Stability properties
property p_sel_stable; @(posedge monitor_mp.clk) disable iff (bus_reset) bus_start) |=> $stable(bus_sel); endproperty a_sel_stable: assert property (p_sel_stable) else begin status = new(); status.set_err_stable_sel(); status_ap.write(status); end property p_addr_stable; @(posedge monitor_mp.clk) disable iff (bus_reset) (bus_start) |=> $stable(bus_addr); endproperty a_addr_stable: assert property (p_addr_stable) else begin status = new(); status.set_err_stable_addr(); status_ap.write(status); end property p_write_stable; @(posedge monitor_mp.clk) disable iff (bus_reset) (bus_start) |=> $stable(bus_write); endproperty a_write_stable: assert property (p_write_stable) else begin status = new(); status.set_err_stable_write(); status_ap.write(status); end property p_wdata_stable; @(posedge monitor_mp.clk) disable iff (bus_reset) (bus_start) && (bus_write) |=> $stable(bus_wdata); endproperty a_wdata_stable: assert property (p_wdata_stable) else begin status = new(); status.set_err_stable_wdata(); status_ap.write(status); end
88 Creating Assertion-Based IP
The remaining properties that Table 5-4 defines and Assertion 5-7 demonstrates, specify that the bus select, control, address, and data signals must remain stable between a bus `START state and the bus `ACTIVE state.
5.2.5 Encapsulate properties In this step, we turn our set of related nonpipelined bus interface assertions (and any coverage properties, which are demonstrated at the end of this chapter in Section 5.4) into a reusable assertion-based monitor verification component. We add analysis ports to our monitor for communication with other simulation verification components as Chapter 3, “The Process” demonstrates. Example 5-7
Encapsulate properties inside a module
import ovm_pkg::*; import tb_tr_pkg::*; // tb_status_nb class definition module tb_nonpipelined_mon ( interface.monitor_mp monitor_mp ); ovm_analysis_port #(tb_status_nb) status_ap = new(“status_ap”, null); ovm_analysis_port #(tb_coverage) coverage_ap = new(“coverage_ap”, null); tb_status_nb status; . . . // Any required modeling code here (to model environment) . . . // All assertions and coverage properties here . . . endmodule
5.3 Simple generic pipelined bus interface In Section 5.2, we demonstrated how to create an assertionbased IP verification component for a bus interface Chapter 5, “Interfaces”
89
supporting a simple, generic nonpipelined protocol. In this section, we introduce a more advanced pipelined bus that supports features such as a split transaction. For our discussion, we present a generic pipelined bus protocol, which is loosely based on a subset of the AMBA AHB protocol [AMBA 1999]. We ask you to focus on the techniques for creating interface assertions. By using our generic, simple pipelined bus protocol design, we hope that you will be able to set aside the details of particular standards that could otherwise overwhelm you and distract you from the learning objectives. After understanding the process we present, you should be able to extend these ideas to create assertion-based verification IP for other proprietary and standard pipelined bus interfaces. If you are interested in seeing specific property examples for the AMBA protocol (versus our generic examples), there have been numerous papers and books published that provide these details (a few examples include [Marschner 2002] [Ruah et al., 2005] [Susantol and Melham 2003]). In addition, there are commercial AMBA VIP solutions available for purchase.
5.3.1 Block diagram interface definition Figure 5-12 illustrates a generic pipelined bus design, and Table 5-5 provides descriptions of the bus signals. To simplify our example, we assume a single bus master. However, our example is easily extended to include multiple bus masters by introducing an arbiter into the pipelined bus design. Arbiters are discussed separately in Chapter 6, “Arbiters.”
90 Creating Assertion-Based IP
Figure 5-12
Simple pipelined bus design clk rst sel write split_done type ready Master
Slave status addr rdata wdata
Table 5-5 Pipelined bus interface signal descriptions Name
Description
clk
Bus clock. All transfers occur on rising edge of clk. Bus reset. An active high bus reset. Slave select. These signals indicate that a slave has been selected. Each slave has its own select (for example, sel[0] for slave 0). For our simple example, we assume a single slave. Write control. When high, write transaction. When low, read. Transfer type. Set to `START for the first transfer of a burst. Set to `CONT for subsequent transfers after a `START. When set to `BUSY, this indicates that the bus master plans to continue with a burst, but not immediately. When set to `IDLE, no data transfer is required.
rst sel
write type[1:0]
Chapter 5, “Interfaces”
91
Name
Description
status[1:0]
Slave status response. Set to `OK indicates that the transfer has completed successfully. Set to `ERROR indicates an error has occurred; the transfer was unsuccessful. Set to `SPLIT indicates that the transfer has not yet completed successfully—the bus master must retry the transfer when it is next granted access to the bus, per a slave request. This signal is low when the slave is performing a ‘SPLIT transaction (indicating not done). Otherwise, it is active high. Used by a slave to insert wait states. Address bus. Read data driven when write is low. Write data driven when write is high.
split_done
ready addr[31:0] rdata[31:0] wdata[31:0]
5.3.2 Overview description Pipelining bus transactions allows for higher transfer rates, often at a cost of an initial latency associated with the first transfer. That is, for certain types of transactions, the first access in a pipelined transfer requires several cycles, and subsequent transactions might only involve a single cycle. Figure 5-13 illustrates a two-phase pipelined bus transfer, where during the first phase (also known as the address phase), a bus master asserts an address A0 (and proper control signals) onto the bus. On the next clock cycle, the data phase begins. Here, as illustrated in Figure 5-13, either: 1 The master drives data D0 onto the wdata bus (for a
write transaction), which the selected slave samples at clock two. Or 2 The selected slave drives data D0 onto the rdata bus,
which the master samples at clock two.
92 Creating Assertion-Based IP
During the D0 data phase, the master asserts a new address A1 onto the bus to start another transaction. Hence, the pipelined bus’s newest address phase will overlap its previous data phase, with the exception of the first and last clock cycles. Figure 5-13
Two-phase pipelined bus transfer 0
1
2
3
A1
A0
addr
4
D0
wdata /rdata
D1
Figure 5-14 illustrates the same two-phase pipelined bus transfer illustrated in Figure 5-13, except in this case, the slave has inserted a wait state into the data transfer by deasserting the ready signal. This effectively stretches the D1 data phase. Figure 5-14
Pipelined bus transfer with wait state 0
addr wdata /rdata
1
A0
2
A1 D0
3
4
5
A2 D1
D2
ready
Inserting a wait state into a pipelined bus cycle presents a number of design challenges, which we do not need to consider for this simple nonpipelined bus example (previously described in Section 5.2). For example, the master in a pipelined bus must stall its pending bus cycle to respect the slave's wait response. Once the slave is ready to proceed, which it indicates by asserting its bus ready signal, the master must continue the transfer from the correct address where it left off prior to the wait.
Chapter 5, “Interfaces”
93
Master command type and slave status For our simple pipelined bus example, the master sets the bus command type to ‘IDLE to indicate that the master does not wish to perform a data transfer. A slave provides a zero wait state ‘OK status response to a master’s ‘IDLE type transfers and ignores the transfer. When the master sets the bus command type to `START, it indicates the start of a burst bus transaction. When the master sets the bus command type to `CONT (after a ‘START), it indicates the continuation of a burst bus transfer.3 The ‘BUSY transfer type indicates that the bus master is continuing with a burst of transfers, but the next transfer cannot take place immediately. Obviously, a bus command type of ‘BUSY (or ‘CONT) cannot occur after an ‘IDLE. When a master uses the ‘BUSY transfer type, the address and control signals must reflect the next transfer in the burst and remain stable on the cycle after the ‘BUSY. The slave must ignore the current transfer and provide a zero wait state ‘OK response, in the same way that it responds to ‘IDLE transfers. pipelined bus wait cycle
Figure 5-15 (see page 95) illustrates a two-beat write burst followed by a non-burst read. A wait cycle was inserted into the second beat of the write burst at clock four (that is, ready is de-asserted and type is not equal to ‘IDLE). Note that a slave must always set its status response to ‘OK when inserting a wait cycle into a transfer. For our simple example, the slave can issue various responses to a master's bus command by setting its status to `OK, `ERROR, or `SPLIT. If `SPLIT is asserted, the current master suspends the bus transfer. For our simple pipelined bus example, we assume that there is only a single master and a single slave and no arbitration is required. (Arbitration is discussed in Chapter 6.) The master then uses the split_done as an indication to continue the bus transfer (similar to a grant). However, for a more complex pipelined
3. Unlike the AMBA AHB, our simple pipelined bus example only supports undefined-length burst.
94 Creating Assertion-Based IP
bus protocol that supports multiple masters (such as the actual AMBA AHB), after a ‘SPLIT, the bus might become available for a different master’s bus transfer. The suspended bus master would then need to re-arbitrate for access to the bus when the slave indicates that it is ready to continue the transfer by asserting a signal similar to our simple example’s split_done. Figure 5-15
Pipelined bus burst and non-burst with wait cycle
0
addr
1
A0
wdata
2
A1 D0
3
4
5
A0 D1
sel write type
`START
`CONT
`START
`IDLE
`OK
`OK
`OK
ready status rdata
D0
Two-cycle response Only an ‘OK response can be given in a single cycle. The ‘ERROR and‘SPLIT responses require at least two cycles. A two-cycle response consists of the following events: 1 In the first cycle of the two-cycle response, the slave
drives type to indicate ‘ERROR or ‘SPLIT while deasserting ready to extend the transfer for an extra cycle. 2 In the second cycle of the two-cycle response, ready is
asserted to end the transfer, while type remains driven to indicate ‘ERROR or ‘SPLIT.
Chapter 5, “Interfaces”
95
If the slave needs more than two cycles to provide the ‘ERROR or ‘SPLIT response, then additional wait states may be inserted at the start of the transfer. During this time the ready signal is deasserted and the response must be set to ‘OK. The two-cycle response is required because of the pipelined nature of the bus. By the time a slave starts to issue either an ‘ERROR or ‘SPLIT response, the address for the following transfer has already been broadcast onto the bus. The twocycle response allows sufficient time for the master to cancel this address and drive type to ‘IDLE before the start of the next transfer. For the ‘SPLIT response, the following transfer must be cancelled because it must not take place before the current transfer has completed. However, for the ‘ERROR response, where the current transfer is not repeated, completing the following transfer is optional.
5.3.3 Natural language properties Our next step is to create a natural language list of properties for our pipelined bus, as shown in Table 5-6. It is generally helpful to organize the interface properties into separate sets. For example, either organize by functionality or unique interface signal, as shown in Table 5-6. Although the set of properties found in this table is not necessarily comprehensive, it is representative of a real set of properties and sufficient to demonstrate our process.4
4. Our simple pipelined bus interface example does not support all the features found in a real pipelined bus protocol, such as sequential burst (SEQ), protection, bus lock, and address wrapping, that are supported by the AMBA AHB protocol. 96 Creating Assertion-Based IP
Table 5-6 Pipelined bus interface properties Assertion name
Bus master type control a_type_reset a_type_idle a_type_error
a_type_split_done
Bus master address a_addr_stable_wait a_addr_stable_busy Bus master write control a_write_stable_wait a_write_stable_burst Bus master wdata a_wdata_stable_wait Bus slave ready a_ready_reset a_ready_idle_wait a_ready_busy_wait a_ready_not_selected a_ready_error_cycle
Summary
must be `IDLE after a reset. type of `BUSY or `CONT cannot be asserted following a type of `IDLE. For a slave status response of `ERROR, the master must assert `IDLE or continue the transfer. After a slave status response of `SPLIT, the master cannot set type to `CONT until after the slave asserts split_done. type
remains stable after the slave inserts a wait state. addr remains stable after a transfer type of `BUSY. addr
write remains stable after the slave inserts a wait state (that is, de-asserts ready). write remains stable during a burst transaction (type = `CONT).
Master must ensure wdata remains stable after slave inserts a wait state. Slave must assert ready after reset. Slave must assert ready when master sets type to `IDLE. Slave must assert ready when master sets type to `BUSY. Slave must assert ready when not selected. Slave must assert ready low on first cycle of error response (that is, when status set to `ERROR), followed by asserting ready high on the second cycle. Chapter 5, “Interfaces”
97
Assertion name
Summary
a_ready_max_wait
Slave must not insert more than 16 consecutive wait states (that is, active low ready).
Bus slave status (response) a_status_reset Slave must set status response to `OK after a reset. a_status_idle_busy_sel Slave must set status response to `OK when type is set to `IDLE, type is set to ‘BUSY, or slave not selected. Bus slave split_done a_split_done_reset Slave must assertion split_done after reset. a_split_done_valid Split done must only be de-asserted after a status of ‘SPLIT.
5.3.4 Assertions To create a set of SystemVerilog assertions for our simple pipelined bus, we begin defining the connection between the assertion-based monitor and other components (such as a driver transactor and the DUV), as Figure 5-16 illustrates. Figure 5-16
SystemVerilog interface
Driver
DUV
AssertionBased Monitor
98 Creating Assertion-Based IP
For instance, in Example 5-4 we demonstrate an interface for our pipelined bus example. For this case, our assertionbased monitor references the interface signals in the direction defined by the monitor_mp named modport. Example 5-8
Encapsulate bus signals inside SV interface
interface tb_pipelined_if( input clk , input rst ); parameter int DATA_SIZE = 8; parameter int ADDR_SIZE = 8; bit bit bit bit bit bit bit bit bit
[1:0] [1:0] [DATA_SIZE-1:0] [DATA_SIZE-1:0] [ADDR_SIZE-1:0]
sel; type; write; status; split_done; ready; wdata; rdata; addr;
modport driver_mp ( input clk , output sel , output wdata input rdata );
rst , type, write , addr , , , status , ready , split_done
modport duv_mp input input input output );
rst , type, write , addr , , , status , ready , split_done
( clk , sel , wdata rdata
modport monitor_mp ( input clk , rst , input sel , type, write , addr , input wdata , input rdata , status , ready , split_done ); endinterface
In addition to pin-level interfaces (that monitor the bus) we need a means for our pipelined bus assertion-based monitor to communicate with various analysis verification components within the testbench. Hence, we introduce an error status analysis port within our monitor, as illustrated in Chapter 5, “Interfaces”
99
Section . In addition to assertion error status, coverage events provide an important piece of analysis data that requires its own analysis port. We discuss coverage with respect to creating assertion-based IP separately in Section 5.4. Figure 5-17
Error status and coverage analysis ports coverage AssertionBased Monitor
Coverage Collector
error status
Test Controller
Upon detecting a bus error, the analysis port broadcasts the error condition (using an error status transaction object) to other verification components. Example 5-9 defines the error status transaction class, which is an extension on an ovm_transaction base class. The tb_status_pb class includes an enum that identifies the various types of pipelined bus errors and a set of methods to uniquely identify the specific error it detected.
100 Creating Assertion-Based IP
Example 5-9
Error status transaction class
class tb_status_pb extends ovm_transaction; typedef enum { ERR_TYPE_RESET , ERR_TYPE_IDLE , ERR_TYPE_ERROR , ERR_TYPE_SPLIT_RETRY , ERR_TYPE_SPLIT_DONE , . . . . , ERR_STATUS_BUSY , ERR_STATUS_SELECT , ERR_SPLIT_DONE_RESET , ERR_SPLIT_DONE_VALID } bus_status_t; bus_status_t bus_status; // Standard OVM transaction methods here, not important for our // discussion. Reference Section B.1.7 (see page 287) for more // details on these standard methods. . . . // Error status transaction methods function void set_err_type_reset; bus_status = ERR_TYPE_RESET; endfunction function void set_err_type_idle; bus_status = ERR_TYPE_IDLE; endfunction function void set_err_type_error; bus_status = ERR_TYPE_ERROR; endfunction . . . . function void set_err_split_done_valid; bus_status = ERR_SPLIT_DONE_VALID; endfunction . . . endclass
The first set of properties listed in Table 5-6 (see page 97) specifies the expected behavior of the bus transaction type command (type) with respect to a reset condition, idle state, wait state, and split transaction. We have grouped these related properties and demonstrated how to express them as SystemVerilog assertions in Assertion 5-8.
Chapter 5, “Interfaces”
101
Assertion 5-8
Bus transaction type command properties
property p_type_reset; @(posedge monitor_mp.clk) $fell(monitor_mp.rst) |-> (monitor_mp.type==`IDLE); endproperty a_type_reset: assert property (p_type_reset) else begin status = new(); status.set_err_type_reset(); status_ap.write(status); end property p_type_idle; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) (monitor_mp.type==`IDLE) |=> (monitor_mp.type!=`BUSY && monitor_mp.type!=`CONT); endproperty a_type_idle: assert property (p_type_idle) else begin status = new(); status.set_err_type_idle(); status_ap.write(status); end property p_type_error; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) ($rose(monitor_mp.ready) && (monitor_mp.status==`ERROR)) |=> (monitor_mp.type==`IDLE || $stable(monitor_mp.type)); endproperty a_type_error: assert property (p_type_error) else begin status = new(); status.set_err_type_error(); status_ap.write(status); end property p_type_split_done; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) (monitor_mp.status==`SPLIT) |=> (monitor_mp.type==‘IDLE) throughout split_done[->1]; endproperty a_type_split_done: assert property (p_type_split_done) else begin status = new(); status.set_err_type_split_done(); status_ap.write(status); end
On the surface, you might argue that all this extra coding in the action block is unnecessary complexity! If this was an
102 Creating Assertion-Based IP
embedded assertion within the design, we would agree with you. However, our goal is to create a reusable verification component that must interact with other verification components within a testbench. Hence, adding an error status analysis port to our monitor, and then having the assertions report errors through this analysis port helps us achieve our goal of reuse. For the bus master address properties that Table 5-6 (see page 97) defines, we can write a set of SystemVerilog assertions as demonstrated in Assertion 5-9. Note that, unlike the real AMBA AHB protocol, our simple pipelined bus example does not support a sequential burst (for example, an AMBA AHB transfer type of SEQ). .
Assertion 5-9
Bus master address properties
property p_addr_stable_wait; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) (~monitor_mp.ready & monitor_mp.status==‘OK & monitor_mp.type!=‘IDLE) |=> $stable(monitor_mp.addr); endproperty a_addr_stable: assert property (p_addr_stable_wait) else begin status = new(); status.set_err_addr_stable_wait(); status_ap.write(status); end property p_addr_stable_busy; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) (monitor_mp.type==‘BUSY) |=> $stable(monitor_mp.addr); endproperty a_addr_stable: assert property (p_addr_stable_busy) else begin status = new(); status.set_err_addr_stable_busy(); status_ap.write(status); end
Assertion 5-10 demonstrates a SystemVerilog assertion for the bus master write control property that Table 5-6 defines, which specifies a stable bus write control condition for our bus interface.
Chapter 5, “Interfaces”
103
.
Assertion 5-10
Bus master write command properties
property p_write_stable_wait; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) (~monitor_mp.ready & monitor_mp.status==‘OK & monitor_mp.type!=‘IDLE) |=> $stable(monitor_mp.write); endproperty a_write_stable_wait: assert property (p_write_stable_wait) else begin status = new(); status.set_err_write_stable_wait(); status_ap.write(status); end property p_write_stable_burst; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) (monitor_mp.type==`CONT) |-> $stable(monitor_mp.write); endproperty a_write_stable_burst: assert property (p_write_stable_burst) else begin status = new(); status.set_err_write_stable_burst(); status_ap.write(status); end
Assertion 5-11 demonstrates a SystemVerilog assertion for the bus wdata property that Table 5-6 defines. .
Assertion 5-11
Bus wdata properties
property p_wdata_stable_wait; @(posedge monitor_mp.clk) (~monitor_mp.ready & monitor_mp.status==‘OK & monitor_mp.type!=‘IDLE) |-> $stable(monitor_mp.wdata); endproperty a_data_stable_wait: assert property (p_data_stable_wait) else begin status = new(); status.set_err_data_stable_wait(); status_ap.write(status); end
Assertion 5-12 demonstrates a SystemVerilog assertion for the bus slave ready property that Table 5-6 defines.
104 Creating Assertion-Based IP
.
Assertion 5-12
Bus slave ready assertions
property p_ready_reset @(posedge monitor_mp.clk) $fell(monitor_mp.rst) |-> (monitor_mp.ready); endproperty a_ready_reset: assert property (p_ready_reset) else begin status = new(); status.set_err_ready_reset(); status_ap.write(status); end property p_ready_idle_wait; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) (monitor_mp.type==‘IDLE) |=> monitor_mp.ready; endproperty a_ready_idle_wait: assert property (p_ready_idle_wait) else begin status = new(); status.set_err_ready_idle_wait(); status_ap.write(status); end property p_ready_busy_wait; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) (monitor_mp.type==‘BUSY) |=> monitor_mp.ready; endproperty a_ready_busy_wait: assert property (p_ready_busy_wait) else begin status = new(); status.set_err_ready_busy_wait(); status_ap.write(status); end property p_ready_not_selected; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) (monitor_mp.sel==1’b0) |-> monitor_mp.ready; endproperty a_ready_not_selected: assert property (p_ready_not_selected) else begin status = new(); status.set_err_ready_not_selected(); status_ap.write(status); end // Continued on next page
Chapter 5, “Interfaces”
105
Assertion 5-12
Bus slave ready assertions
property p_ready_error_cycle; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) $rose(monitor_mp.status==‘ERROR) |-> ~monitor_mp.ready ##1 monitor_mp.ready && (monitor_mp.status==‘ERROR); endproperty a_ready_error_cycle: assert property (p_ready_error_cycle) else begin status = new(); status.set_err_ready_error_cycle(); status_ap.write(status); end property p_ready_max_wait; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) $fell(monitor_mp.ready) |=> ( ~monitor_mp.ready[*0:15] ##1 monitor_mp.ready); endproperty a_ready_max_wait: assert property (p_ready_max_wait) else begin status = new(); status.set_err_ready_max_wait(); status_ap.write(status); end
Assertion 5-13 demonstrates a SystemVerilog assertion for the bus slave status response properties that Table 5-6 defines. .
Assertion 5-13
Bus slaves status response properties
property p_status_reset; @(posedge monitor_mp.clk) $fell(monitor_mp.rst) |-> (monitor_mp.status==`OK); endproperty a_status_reset: assert property (p_status_reset) else begin status = new(); status.set_err_status_reset(); status_ap.write(status); end // Continued on next page
106 Creating Assertion-Based IP
Assertion 5-13
Bus slaves status response properties
property p_status_idle_busy_no_sel; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) ((monitor_mp.sel==1'b0) || (monitor_mp.type==`IDLE) || (monitor_mp.type==`BUSY)) |-> (monitor_mp.status==‘OK); endproperty a_status_idle_busy_no_sel: assert property (p_status_idle_busy_no_sel) else begin status = new(); status.set_err_status_idle_busy_no_sel(); status_ap.write(status); end
Assertion 5-14 demonstrates a SystemVerilog assertion for the bus split_done property that Table 5-6 defines. .
Assertion 5-14
Bus split_done properties
property p_split_done_reset; @(posedge monitor_mp.clk) $fell(monitor_mp.rst) |-> monitor_mp.split_done; endproperty a_split_done_reset: assert property (p_split_done_reset) else begin status = new(); status.set_err_data_stable_wait(); status_ap.write(status); end property p_split_done_valid; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) $fell(monitor_mp.split_done) |-> $past(monitor_mp.type==‘SPLIT); endproperty a_split_done_valid: assert property (p_split_done_valid) else begin status = new(); status.set_err_data_stable_wait(); status_ap.write(status); end
Chapter 5, “Interfaces”
107
5.3.5 Encapsulate properties In this step, we turn our set of related pipelined bus interface assertions (and any coverage properties) into a reusable assertion-based monitor verification component. We add analysis ports to our monitor for communication with other simulation verification components as Chapter 3, “The Process” demonstrates. Example 5-10
Encapsulate properties inside a module
import ovm_pkg::*; import tb_tr_pkg::*; // tb_status_pb class definition module tb_pipelined_mon ( interface.monitor_mp monitor_mp ); ovm_analysis_port #(tb_status_pb) status_ap = new(“status_ap”, null); ovm_analysis_port #(tb_coverage) coverage_ap = new(“coverage_ap”, null); tb_status_pb status; . . . // Any required modeling code here (to model environment) . . . // All assertions and coverage properties here . . . endmodule
5.4 Interface monitor coverage example Thus far in this chapter, we have demonstrated how to create a set of assertion to detect bus interface errors. In addition to assertions for error detection, assertion-based IP generally contains coverage properties that implement associated coverage models. Each model defines DUV behavior that must be observed to determine completeness. The recorded coverage data is analyzed during verification to gauge verification progress. In this section, we return to our nonpipelined bus interface example (previously discussed in Section 5.2) to 108 Creating Assertion-Based IP
demonstrate the process of adding coverage properties to assertion-based IP. The coverage property we demonstrate tracks the size of a bus read or write burst transaction for our nonpipelined bus interface. Figure 5-18
Assertion-based IP coverage analysis port
Driver
DUV
Responder
coverage AssertionBased Monitor
Coverage Collector
error status
Stimulus Generator
Test Controller
Like our pervious assertion examples, we recommend following the philosophy of separating coverage detection from action. This requires adding an analysis port similar to our assertion-based monitor, which is used to communicate between other testbench verification components (as Figure 5-18 illustrates). The coverage analysis port broadcasts a coverage transaction class with an extension on an ovm_transaction base class, as Figure 5-18 illustrates. The tb_coverage class, which Example 5-11 defines, includes an enum that identifies the various types of nonpipelined bus errors, and a set of
Chapter 5, “Interfaces”
109
methods to set the appropriate transaction type and capture the address and data values for analysis. Example 5-11
Coverage transaction class
class tb_coverage extends ovm_transaction; typedef enum {IDLE, WRITE, READ} bus_trans_t; rand bus_trans_t . . . .
bus_trans_type;
function void set_write(); bus_trans_type = WRITE; return ; endfunction function void set_read(); bus_trans_type = READ; return ; endfunction . . . endclass
Example 5-12 demonstrates our modified nonpipelined assertion-based monitor that includes the coverage analysis port and a coverage property to measure burst sizes. The psize local variable, defined within the sequence of the coverage property, increments each time a new data word is transferred for a given bus transaction burst. The burst size and type (for example, read or write), is passed to the build_tr function, which is responsible for broadcasting the burst size and type coverage information through the assertion-based monitor’s coverage analysis port.
110 Creating Assertion-Based IP
.
Example 5-12
Encapsulate coverage properties inside a module
import ovm_pkg::*; import tb_tr_pkg::*; // tb_status_nb class definition module tb_nonpipelined_mon ( interface.monitor_mp monitor_mp ); ovm_analysis_port #(tb_status_nb) status_ap = new(“status_ap”, null); ovm_analysis_port #(tb_coverage) coverage_ap = new(“coverage_ap”, null); tb_status_nb status; . . . // Add modeling code here as described in Section 5.2.4 . . . // All assertions and coverage properties here property p_burst_size; int psize; @(posedge monitor_mp.clk) ((bus_inactive), psize=0) ##1 ((bus_start, psize++, build_tr(psize)) ##1 (bus_active))[*1:$] ##1 (bus_inactive); endproperty cover property (p_burst_size); function void build_tr(int psize); tb_transaction tr; tr = new(); if (bus_write) begin tr.set_write(); tr.data = bus_wdata; end else begin tr.set_read(); tr.data = bus_rdata; end tr.burst_count = psize; tr.addr = bus_addr; coverage_ap.write(tr); endfunction endmodule
Chapter 5, “Interfaces”
111
5.5 Summary In this chapter, we demonstrated our process of creating assertion-based IP for the three generic bus interfaces Figure 5-1 illustrates: •
Simple serial bus interface
•
Simple nonpipelined bus interface
•
Simple pipelined bus interface
In addition, we demonstrated how to include coverage properties as part of your overall assertion-based IP development strategy. When creating assertion-based IP, we recommend that you separate the process of error (or coverage) detection from the process of taking action. For example, the decision about taking action when an assertion fires is not made within our module-based assertion monitor. When an assertion fires, it calls a method from the transaction error status class within the failing assertion’s action block to uniquely identify the error. Error status is then passed to the simulation controller via the module-based monitor’s analysis port. The simulation controller is responsible for taking an appropriate action or ignoring the error if it chooses. This clear separation between detection and action facilitates reuse for the module-based monitor and enables us to support advanced testbench features (such as error injection) without modifying the monitor.
112 Creating Assertion-Based IP
C
H A P T E R
ARBITERS
CHAPTER 6
Arbiters are probably one of the most widely studied components in software and hardware design and verification. In fact, you will find that an arbiter has traditionally served as the primary design example in many published technical papers and books [Kariniemi and Nurmi 2005] [Dasgupta 2006]. Hence, this chapter presents very little new information on the topic of specifying assertions for various arbitration schemes. Yet, arbiters are a fundamental component in systems containing shared resources, such as our bus-based design example illustrated in Figure 6-1 that must arbitrate a shared bus between multiple masters to prevent dropping or corrupting data transported through the bus. Thus, understanding how to create assertion-based IP for an arbiter, which easily integrates with other testbench components, is an essential topic of discussion. Figure 6-1.
Bus-based design with arbiter
CPU 1
UART
Pipelined Bus Arbiter
Nonpipelined Bus Bridge
CPU 2
Memory Controller
Timer
Chapter 6, “Arbiters”
113
This chapter begins with a review of various common arbitration schemes and associated properties. It then demonstrates the process of creating assertion-based IP for an arbiter component. In this chapter, you will note that we are following a standard development pattern previously defined in Chapter 3. Each section within this chapter was defined to stand on its on. Hence, you might noticed repetitive text in portions of the chapter.
6.1 Arbitrations schemes Arbitration schemes range from the unfair, fixed-priority scheme to the fair, round-robin scheme, as well as a combination of variable priority and fairness schemes. (See [Kariniemi and Nurmi 2005] for a comprehensive discussion on a range of arbitration schemes.) Although arbiters are used in multiple applications, their basic properties are generally straightforward and are easily expressed as SystemVerilog assertions. Consider the requirement for the arbiter illustrated in Figure 6-2, which states that a requesting client will eventually be served. In other words, we want to ensure that a client cannot be starved from access to a shared resource. Figure 6-2.
A simple two client arbiter
req[0]
gnt[0] Arbiter
req[1]
gnt[1]
As Assertion 6-1 shows, we can use SystemVerilog to express an assertion for client 0. This assertion states that
114 Creating Assertion-Based IP
every request (assertion of req[0]) must be followed at some time by a grant (assertion of gnt[0]). Assertion 6-1
A requesting client will eventually be served
property p_req_gnt; @(posedge clk) disable iff (rst) req[0] |-> ##[0:$] gnt[0]; endproperty a_req_gnt: assert property (p_req_gnt);
Figure 6-3 illustrates a trace on which Assertion 6-1 holds. Figure 6-3.
Trace on which Assertion 6-1 holds 0
1
2
3
4
5
6
7
8
9
req[0] gnt[0]
The next section refines our arbiter assertion to make it more precise and ensure that an arbiter servicing multiple clients is fair.
6.1.1 Fair arbitration To begin the discussion of how to create assertion-based IP for fair arbitration, we examine the simple two-client static fair arbiter that Figure 6-2 illustrates. For this simple example to be fair, a pending request for a particular client should never have to wait more than two arbitration cycles before it is serviced. Otherwise, the alternate client must have been unfairly issued a grant multiple times. This method of verifying fairness is based on the concept of finitary fairness introduced by [Alur and Henzinger 1998]. For client 0, which asserts a request req[0], we can use a forbidden sequence to express the fair requirement, as Chapter 6, “Arbiters”
115
Assertion 6-2 shows. This assertion states that we should never see a sequence where client 0 asserts a request (req[0]), and then no grant (gnt[0]) is issued within two arbitration cycles. For our example, we use the goto repetition operator [->2] to express two back-to-back arbitration cycles for client 1, and the throughout operator for the sequence where no grants are issued to client 0 during the two arbitration cycles for client 1. Assertion 6-2
Two-client fair arbitration assertion for client 0
property p_no_req_0_two_gnt_1; @(posedge clk) disable iff (rst) req[0] ##1 (!gnt[0] throughout (gnt[1])[->2]) |-> 0; endproperty a_no_req_0_two_gnt_1: assert property (p_no_req_0_two_gnt_1);
Figure 6-4 illustrates behavior on which Assertion 6-2 does not hold. Figure 6-4.
Trace on which Assertion 6-2 does not hold 0
1
2
3
4
5
6
7
8
9
req[0] gnt[0] gnt[1]
Assertion 6-3 shows a fair arbitration requirement for the case where client 1 asserts a request (req[1]). Assertion 6-3
Two-client fair arbitration assertion for client 1
property p_no_req_1_two_gnt_0; @(posedge clk) disable iff (rst) req[1] ##1 (!gnt[1] throughout (gnt[0])[->2]) |-> 0; endproperty a_no_req_1_two_gnt_0: assert property (p_no_req_1_two_gnt_0);
116 Creating Assertion-Based IP
For a large fair arbiter containing many clients (for example, an eight-client round-robin arbiter), we can simplify the process of writing a large set of assertions for each requestand-grant pair by using the SystemVerilog generate construct (see Assertion 6-4). Assertion 6-4
Eight-client fair arbitration assertion
property p_no_req_i_two_gnt_j (i,j); @(posedge clk) disable iff (rst) req[i] ##1 (!gnt[i] throughout (gnt[j])[->2]) |-> 0; endproperty generate begin genvar i, j; for (i = 0; i (~gnt[0] throughout (req[0])[->1]); endproperty a_init_no_gnt_before_req: assert property (p_init_no_gnt_before_req);
Figure 6-7 illustrates behavior on which Assertion 6-8 does not hold. Figure 6-7.
Trace on which Assertion 6-8 does not hold 0
1
2
3
4
5
6
7
8
9
req[0] gnt[0]
Assertion 6-9 shows how to write a SystemVerilog assertion for the general case in which an arbitration cycle associated with client 0 has completed in the past. Chapter 6, “Arbiters”
121
Assertion 6-9
General case for no grant prior to a request
property p_no_gnt_before_req; @(posedge clk) disable iff (rst) gnt[0] |=> (~gnt[0] throughout (req[0])[->1]); endproperty a_no_gnt_before_req: assert property (p_no_gnt_before_req);
Figure 6-8 illustrates behavior on which Assertion 6-9 does not hold. Notice the situation where a grant is issued at clock four, which ends the arbitration cycle started at clock one, and another grant is issued at clock seven without a pending request. Figure 6-8.
Trace on which Assertion 6-9 does not hold
v
0
1
2
3
4
5
6
7
8
9
req[0] gnt[0]
6.1.3 Fixed priority Section 6.1.1 described how to express assertions associated with fair arbiters. Fairness, however, is only one example in the class of static arbitration schemes commonly used in today’s designs. This section describes how to express assertions for fixed priority, which is one of the most common static arbitration schemes. An arbiter that follows a fixed priority scheme always issues a grant to a requesting, higher-priority client before it issues a grant to a lowerpriority client. Figure 6-2 illustrated a simple two-client arbiter. For our discussion, we will assume that client 0 has a higher priority over client 1, and there is a minimum one-cycle latency through the arbiter. 122 Creating Assertion-Based IP
Assertion 6-10 //
Client 0 has fixed priority over client 1
req[0] higher priority over req[1], gnt[0] before gnt[1]
property p_fixed_priority_client0; @(posedge clk) disable iff (rst) $rose(req[0]) ##1 (~gnt[0] throughout (gnt[1])[->1]) |-> 0; endproperty a_fixed_priority_client0: assert property (p_fixed_priority_client0);
Assertion 6-10 expresses a fixed priority assertion for our simple example assuming a latency of one cycle. This assertion states that whenever client 0 asserts a request (req[0]), then a grant (gnt[0]) must be issued to the higherpriority client 0 prior to issuing a grant (gnt[1]) to the lower-priority client 1. Figure 6-9 illustrates a behavior on which Assertion 6-10 does not hold. Figure 6-9.
Trace on which Assertion 6-10 does not hold
v
0
1
2
3
4
5
6
7
8
9
req[0] req[1] gnt[0] gnt[1]
In Figure 6-9, there is a pending request from client 1 that has yet to be serviced when a higher priority request comes from client 0. Hence, we should not see a grant issued for client 1 before a grant is issued for client 0 (assuming a onecycle latency through the arbiter for this example).
Chapter 6, “Arbiters”
123
Figure 6-10.
Eight-client fixed priority arbiter
v
req[0]
High Priority
gnt[0]
req[1]
gnt[1]
req[2]
gnt[2]
req[3]
Arbiter
gnt[3]
req[4]
gnt[4]
req[5]
gnt[5]
req[6]
gnt[6]
req[7]
Low Priority
gnt[7]
Let us assume we have a larger eight-client arbiter, where the lowest numbered port has the highest priority, and the priority decreases as the port number increases, as illustrated in Figure 6-10. We can simplify the process of writing a large set of assertions for each request and grant pair (as we did in Assertion 6-4) by using the SystemVerilog generate construct as shown in Assertion 6-11. .
Assertion 6-11
Eight-client fixed priority assertion
// Assume client i is higher priority to client j, when value j > i // E.g., client i=0 higher priority than client j=1, for value j > i property p_fixed_priority_i_j(i,j); @(posedge clk) disable iff (rst) (j > i) && $rose(req[i]) ##1 (~gnt[i] throughout (gnt[j])[->1]) |-> 0; endproperty generate begin genvar i, j; for (i = 0; i 0; endproperty generate begin genvar i, j; for (i = 0; i 0; endproperty generate begin genvar i, j; for (i = 0; i 0; endproperty generate begin genvar i, j; for (i = 0; i 0; endproperty generate begin genvar i, j; for (i = 0; i (monitor_mp.entry_valid==8’b0) & (monitor_mp.queue_full==1’b0) & (monitor_mp.queue_empty==1’b1); endproperty a_reset_state : assert property (p_reset_state) else begin status = new(); status.set_err_reset_state(); status_ap.write(status); end
Assertion 8-10, “Queue full” demonstrates our next two properties associated with a full queue. Assertion 8-10
Queue full
property p_no_overflow; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) monitor_mp.queue_full |-> !monitor_mp.push; endproperty a_no_overflow : assert property (p_no_overflow) else begin status = new(); status.set_err_no_overflow(); status_ap.write(status); end property p_queue_full; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) (monitor_mp.queue_full == &(monitor_mp.entry_valid)); endproperty a_queue_full : assert property (p_queue_full) else begin status = new(); status.set_err_queue_full(); status_ap.write(status); end
208 Creating Assertion-Based IP
Assertion 8-11, “Queue empty” demonstrates our next two properties associated with an empty queue. Assertion 8-11
Queue empty
property p_no_underflow; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) monitor_mp.queue_empty |-> !monitor_mp.pop; endproperty a_no_underflow : assert property (p_no_underflow) else begin status = new(); status.set_err_no_underflow(); status_ap.write(status); end property p_queue_empty; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) ( |(monitor_mp.entry_valid) != monitor_mp.queue_empty); endproperty a_queue_full : assert property (p_queue_full) else begin status = new(); status.set_err_queue_full(); status_ap.write(status); end
Assertion 8-12, “Correct entry_valid after push, pop, and clear” demonstrates our next two properties. The first property specifies that the entry_valid signal must have the correct number of bits set as a result of a combination of push, pop, and clear commands at any given cycle. The second property specifies that the set entry_valid bits must be contiguous, and shifted to the right. Assertion 8-12
Correct entry_valid after push, pop, and clear
property p_push_pop_clear_entry_valid; bit [3:0] lv_cnt; // count valid entries after push, pop, clear @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) (1, lv_cnt = ($countones(monitor_mp.entry_valid) + monitor_mp.push $countones(monitor_mp.clear | {7’b0,monitor_mp.pop}) ) |=> $countones(entry_valid)==lv_cnt); endproperty // Continued on next page
Chapter 8, “Datapath”
209
Assertion 8-12
Correct entry_valid after push, pop, and clear
a_push_pop_clear_entry_valid : assert property (p_ppush_pop_clear_entry_valid) else begin status = new(); status.set_err_push_pop_clear_entry_valid(); status_ap.write(status); end property p_contiguous_entry_valid; bit [3:0] lv_cnt; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) (1, lv_cnt = ($countones(monitor_mp.entry_valid) + monitor_mp.push $countones(monitor_mp.clear | {7’b0,monitor_mp.pop}) ) |=> &((8’b11111111 |(monitor_mp.entry_valid & monitor_mp.clear)); endproperty a_no_invalid_entry_clear : assert property (p_no_invalid_entry_clear) else begin status = new(); status.set_err_no_invalid_entry_clear(); status_ap.write(status); end
Assertion 8-14, “When no dequeue operation dataout is stable” demonstrates our next property.
210 Creating Assertion-Based IP
Assertion 8-14
When no dequeue operation dataout is stable
property p_stable_dataout; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) !monitor_mp.pop |=> $stable(monitor_mp.dataout)); endproperty a_stable_dataout : assert property (p_stable_dataout) else begin status = new(); status.set_err_stable_dataout(); status_ap.write(status); end
So far, our data queue properties have focused more on the control behavior details for our simple queue. From an endto-end (or black-box) perspective, probably the most important queue properties are related to data integrity. That is, data entering the queue, which has never been cleared, will eventually exit the queue uncorrupted in the correct order. When are assertions not appropriate?
Up to this point, we have expressed most of our properties purely through the set of assertion constructs found in SystemVerilog. However, not all design behaviors are easily expressed by only using assertion constructs. Some complex behavior often requires a combination of additional modeling code that is then combined with various SVA constructs to properly specify it. In addition, there are some design behaviors that are best specified only using modeling code. To illustrate what we are talking about, let’s examine the following simplified example. For this case, we will express a data integrity property on our simple queue. However, to simplify the problem further, we assume that our queue does not support the entry clear capabilities (that is, there is no clear[7:0] control and all items enqueued will eventually be dequeued). With this simplification, we write Assertion 8-15 to verify that data entering the queue always exits without corruption.
Chapter 8, “Datapath”
211
Assertion 8-15
Data integrity example with additional modeling
// Modeling code to identify specific push and pops reg [2:0] r_pop_tag, r_push_tag; always @(posedge monitor_mp.clk or negedge monitor_mp.rst_n) begin if (~monitor_mp.rst_n) begin r_pop_tag (~monitor_mp.mem_last thoughout monitor_mp.mem_valid[=3]) ##1 (monitor_mp.mem_last & monitor_mp.mem_valid) ; endproperty a_legal_last_valid_init : assert property (p_legal_last_valid_init) else begin status = new(); status.set_err_legal_last_valid(); status_ap.write(status); end // Continued on next page
222 Creating Assertion-Based IP
Assertion 8-17
ECC checker legal mem_last behavior
// Forbid a mem_last between four-beat transfer property p_legal_last_valid; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) monitor_mp.mem_last |=> (~monitor_mp.mem_last thoughout monitor_mp.mem_valid[=3]) ##1 (monitor_mp.mem_last & monitor_mp.mem_valid) ; endproperty a_legal_last_valid : assert property (p_legal_last_valid) else begin status = new(); status.set_err_legal_last_valid(); status_ap.write(status); end
Assertion 8-18, “ECC checker stability” demonstrates our next set of properties described in Table 8-6. Assertion 8-18
ECC checker stability
property p_corr_data_stable; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) !monitor_mp.corr_valid |-> $stable(monitor_mp.corr_data); endproperty a_corr_data_stable : assert property (p_corr_data_stable) else begin status = new(); status.set_err_corr_data_stable(); status_ap.write(status); end property p_errdetect_stable; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) !monitor_mp.corr_valid |-> $stable(monitor_mp.errdetect); endproperty a_errdetect_stable : assert property (p_errdetect_stable) else begin status = new(); status.set_err_errdetect_stable(); status_ap.write(status); end
Assertion 8-19, “ECC checker data integrity” demonstrates our final set of properties described in Table 8-6.
Chapter 8, “Datapath”
223
Assertion 8-19
ECC checker data integrity
property p_data_correct; logic [143:0] lv_indata; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) ($rose(~monitor_mp.rst_n) | monitor_mp.mem_last) ##1 (monitor_mp.mem_valid[->1], lv_indata[143:108] = monitor_mp.mem_data) ##1 (monitor_mp.mem_valid[->1], lv_indata[107:72] = monitor_mp.mem_data) ##1 (monitor_mp.mem_valid[->1], lv_indata[71:36] = monitor_mp.mem_data) ##1 (monitor_mp.mem_valid[->1], lv_indata[35:00] = monitor_mp.mem_data) ##1 monitor_mp.corr_valid[->1] |-> (monitor_mp.db_err | ( f_ecc(lv_indata) == monitor_mp.corr_data)); endproperty; a_data_correct: assert property (p_data_correct) else begin status = new(); status.set_err_data_correct(); status_ap.write(status); end
To verify the data integrity for our ECC Checker, it becomes necessary to create a function (f_ecc) as part of our assertion (shown in Assertion 8-19). This function accepts a 144-bit input data value and outputs a 128-bit data value (with single bit correction if necessary). The draw back to this approach is that the function f_ecc replicates the algorithm (that is, code) we are checking. A better approach that is totally independent of any specific ECC algorithm would be to write a data integrity assertion across both the ECC generator and the ECC checker design components, as illustrated in Figure 8-12 (see page 214). However, the disadvantage of this approach is that the assertion-based IP would no longer be dedicated to checking a single design components (since two design components would be required).
224 Creating Assertion-Based IP
8.3.5 Encapsulate properties In this step, we turn our set of related ECC checker assertions into a reusable assertion-based monitor verification component, as demonstrated in Example 8-9 on page 225. We add analysis ports to our monitor for communication with other simulation verification components, as Chapter 3 “The Process” demonstrates. Example 8-9
Encapsulate properties inside a module
import avm_pkg::*; import tb_tr_pkg::*; // tb_ecc_status class definition module tb_ecc_checker_mon ( interface.monitor_mp monitor_mp ); avm_analysis_port #(tb_ecc_status) status_ap = new(“status_ap”, null); avm_analysis_port #(tb_ecc_coverage) coverage_ap = new(“coverage_ap”, null); tb_ecc_status status; . . . // Any required modeling code here (to model environment) . . . // All assertions and coverage properties here . . . endmodule
8.4 Data compression Data compression, an important application in the areas of data transmission and data storage, is the process of encoding information using fewer bits. Compressing data that is to be stored or transmitted reduces storage and communication costs. In fact, when the amount of data to be transmitted is reduced, the effect is that of increasing the capacity of the communication channel. Similarly, when a file is compressed to half of its original size, the effect is equivalent to doubling the capacity of the storage medium.
Chapter 8, “Datapath”
225
In this section, we introduce a simple packet data compressor design component to demonstrate our assertionbased IP creation process. Our simple packet data compressor provides another example of a data transformation datapath component.
8.4.1 Block diagram interface description Figure 8-17 illustrates a block diagram for a simple packet data compressor design. For our example, all signal transitions relate only to the rising edge of the clock (clk). Table 8-7 provides a summary of the interface signals for our simple packet data compressor design example. Figure 8-17
A simple packet data compressor block diagram
datain[15:0]
dataout[7:0]
datain_valid start_packet rst_n
comppkt_ready Packet Data Compressor
comppkt_error
accept_beat
clk
Table 8-7 Interface description for packet data compressor
Name datain[15:0] datain_valid start_packet comppkt_ready comppkt_error
dataout[7:0] accept_beat
Description Input data to the compressor Enter input data into compressor First entry of a set to be compressed Compressed packet is ready to be read out Error signal asserted when an uncompressed packet is not fully read out before another uncompressed packet is ready to be transmitted The comppkt_error signal will be asserted coincident with a subsequent comppkt_ready Data sent out of compressor Output data has been accepted
226 Creating Assertion-Based IP
Name clk rst_n
Description Synchronous clock Asynchronous reset
8.4.2 Overview description Our simple data compressor accepts a packet of eight 16-bit data beats and sends out the packet compressed into four 8bit data beats. The compression operates on a 128-bit packet taking advantage of the limited ranges of the data fields within the packet. The details of the encoding algorithm are not important for our discussion and are not presented in this section. There are flow control signals that indicate when data is ready to be sent and when it has been picked up. In addition, the start_packet signal indicates the beginning transmission of an input packet that is to be compressed
Basic packet data compression operation The waveforms in Figure 8-18 and Figure 8-19 illustrate the typical operation for our simple packet data compressor. An input packet is composed of eight beats of data, where each beat (that is, 16-bit data transfer) is identified by asserting datain_valid. Once eight beats of data have been received, the compression operation occurs on the entire set, and the data is ready to be read after a minimum and maximum latency of two clock for our simple example. The signal comppkt_ready is then asserted to indicate that a compressed data packet is ready. The four compressed data packet beats are removed a single beat at a time when the accept_beat signal is asserted.
Chapter 8, “Datapath”
227
Figure 8-18
Packet data compression input operation 0
1
2
7
8
9
start_packet datain_valid D0
datain[15:0]
Figure 8-19
D1
D7
Packet data compression output operation 9
10
11
12
13
14
comppkt_ready accept_beat dataout[15:0]
d0
d1
d2
d3
comppkt_error
Error transfer operation Our simple packet data compressor supports slow output devices by allowing a previously compressed data packet to be dropped when a complete incoming packet has arrived prior to completing the transmission of a previously compressed packet. When this error condition occurs, the signal comppkt_error is asserted for a single cycle, which indicates that the receiving device should flush all partially received packet beats.
8.4.3 Natural language properties Prior to creating a set of SystemVerilog assertions for our simple packet data compressor design, we must identify a comprehensive list of natural language properties, as shown 228 Creating Assertion-Based IP
in Table 8-8. Although the set of properties found in this table is not necessarily comprehensive, it is representative of a real set of properties and sufficient to demonstrate our process. Table 8-8 Simple packet data compressor design properties Assertion name
Summary
a_reset_state
Initial state after reset is no compressed packets Eight and only eight uncompressed packets are received only after a
a_8_input_pkts_after_start
start_packet
a_4_output_pkts_after_ready
No new start_packet occurs until all input packets received Four and only four compressed packets are sent only after a comppkt_ready is asserted No new comppkt_ready are asserted until all output packets are sent, or a comppkt_error is asserted
a_input_to_output_or_error
After input packet of eight beats of uncompressed data is received, then either a compressed output packet will eventually be sent or a comppkt_error occurs
a_valid_pkt_error
The signal comppkt_error shall be asserted only when comppkt_ready rises (signaling new transaction) The signal comppkt_error shall be a one-cycle pulse
a_valid_pkt_error_pulse
Chapter 8, “Datapath”
229
Assertion name
Summary
a_valid_fast_packet
A subsequent compressed packet that is ready prior to completing the transmission of an existing compressed packet will force an abort on the currently transmitted compressed compacket, and comppkt_error will then be asserted The dataout bus must be stable when accept_beat is not asserted
a_dataout_stable a_compressed_data_integrity
The data from the input packet, once compressed, should be the same value as the output packet.
8.4.4 Assertions To create a set of SystemVerilog assertions for our simple packet data compressor design, we begin defining the connection between our assertion-based monitor and other potential components within the testbench (such as a driver transactor and the DUV). We accomplish this goal by creating a SystemVerilog interface, as Figure 8-20 illustrates. Figure 8-20
SystemVerilog interface
Driver
DUV
AssertionBased Monitor
230 Creating Assertion-Based IP
.
Example 8-10
Encapsulate signals inside an interface
interface tb_data_compressor_if( input clk , input rst_n ); bit [15:0] datain; bit datain_valid; bit start_packet; bit comppkt_ready; bit comppkt_error; bit [7:0] dataout; bit accept_beat; modport driver_mp ( input clk , rst , . . . . ); . . . modport duv_mp ( input clk , rst , . . . . ); modport monitor_mp ( input clk , rst , input datain, input datain_valid, input start_packet, input comppkt_ready, input comppkt_error, input dataout, input accept_beat ); endinterface
Example 8-10 on page 231 demonstrated an interface for our simple packet data compressor example. For this case, our assertion-based monitor would reference the interface signals via the direction defined by the monitor_mp named modport. In addition to pin-level interfaces, we need a means for our simple packet data compressor assertion-based monitor to communicate with various analysis verification components within the testbench. Hence, we introduce an error status analysis port within our monitor, as Figure 8-21 illustrates. In addition to assertion error status, coverage events are an important piece of analysis data that requires its own analysis port. We discuss coverage with respect to creating assertion-based IP separately in Section 5.4. Chapter 8, “Datapath”
231
Figure 8-21
Error status and coverage analysis ports coverage AssertionBased Monitor
Coverage Collector
error status
Test Controller
Upon detecting a data compression error, the analysis port broadcasts the error condition (using an error status transaction object) to other verification components. Example 8-11
Error status class
class tb_cmp_status extends avm_transaction; typedef enum { ERR_RESET_STATE, ERR_8_INPUT_PKTS_AFTER_START, ERR_4_OUTPUT_PKTS_AFTER_READY, ERR_INPUT_TO_OUTPUT_OR_ERROR, ERR_VALID_PKT_ERROR, ERR_VALID_PKT_ERROR_PULSE, ERR_VALID_FAST_PACKET, ERR_DATAIN_STABLE } cmp_status_t; cmp_status_t cmp_status; int err_client_num; function void set_err_reset_state; cmp_status = ERR_RESET_STATE ; endfunction function void set_err_8_input_pkts_after_start; cmp_status = ERR_8_INPUT_PKTS_AFTER_START ; endfunction // Continued on next page
232 Creating Assertion-Based IP
Example 8-11
Error status class
function void set_err_4_output_pkts_after_ready; cmp_status = ERR_4_OUTPUT_PKTS_AFTER_READY ; endfunction function void set_err_input_to_output_or_error; cmp_status = ERR_INPUT_TO_OUTPUT_OR_ERROR ; endfunction function void set_err_valid_pkt_error; cmp_status = ERR_VALID_PKT_ERROR ; endfunction function void set_err_valid_pkt_error_pulse; cmp_status = ERR_VALID_PKT_ERROR_PULSE ; endfunction function void set_err_valid_fast_packet; cmp_status = ERR_VALID_FAST_PACKET ; endfunction function void set_err_datain_stable; cmp_status = ERR_DATAIN_STABLE ; endfunction . . . endclass
The error status transaction class is defined in Example 811, which is an extension of avm_transaction base class. The tb_cmp_status class includes an enum that identifies the various types of data compressor errors and a set of methods to uniquely identify the specific error it detected. We are now ready to write assertions for our simple packet data compressor design properties defined in Table 8-8 (see page 229). Assertion 8-20, “Packet data compressor inactive after reset” on page 234 demonstrates our first property.
Chapter 8, “Datapath”
233
Assertion 8-20
Packet data compressor inactive after reset
property p_reset_state; @(posedge monitor_mp.clk) $rose(monitor_mp.rst_n) |-> monitor_mp.comppkt_ready==1’b0) & (monitor_mp.comppkt_error==1’b0) & (monitor_mp.dataout==8’b0); endproperty a_reset_state : assert property (p_reset_state) else begin status = new(); status.set_err_reset_state(); status_ap.write(status); end
Assertion 8-21, “Valid uncompressed input packet sequence” demonstrates our next property. Note that we wrote a separate assertion to handle the boundary case after reset. However, we chose to use the same error status method as the normal case to identify the violation. Assertion 8-21
Valid uncompressed input packet sequence
// Boundary case after reset property p_8_input_pkts_after_start_init; @(posedge monitor_mp.clk) $rose(monitor_mp.rst_n) |-> (!monitor_mp.datain_valid throughout monitor_mp.start_packet[->1]); endproperty a_8_input_pkts_after_start_init : assert property (p_8_input_pkts_after_start_init) else begin status = new(); status.set_err_8_input_pkts_after_start(); status_ap.write(status); end // Normal case property p_8_input_pkts_after_start; @(posedge monitor_mp.clk) monitor_mp.start_packet |-> monitor_mp.data_valid ##1 (~monitor_mp.start_packet throughout monitor_mp.datain_valid[=7]) ##1 (!monitor_mp.datain_valid & monitor_mp.start_packet); endproperty // Continued on next page
234 Creating Assertion-Based IP
Assertion 8-21
Valid uncompressed input packet sequence
a_8_input_pkts_after_start : assert property (p_8_input_pkts_after_start) else begin status = new(); status.set_err_8_input_pkts_after_start(); status_ap.write(status); end
Assertion 8-22, “Valid compressed output packet sequence” demonstrates our next property. Assertion 8-22
Valid compressed output packet sequence
// Boundary case after reset property p_4_output_pkts_after_ready_init; @(posedge monitor_mp.clk) $rose(monitor_mp.rst_n) |-> !(amonitor_mp.accept_beat | monitor_mp.comppkt_error) throughout monitor_mp.comppkt_ready[->1]; endproperty a_4_output_pkts_after_ready_init : assert property (p_4_output_pkts_after_ready_init) else begin status = new(); status.set_err_4_output_pkts_after_ready(); status_ap.write(status); end // Normal case property p_4_output_pkts_after_ready; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) $rose(monitor_mp.comppkt_ready) |-> ( (monitor_mp.comppkt_ready throughout monitor_mp.accept_beat[->1]) ##1 (!monitor_mp.comppkt_ready throughtout monitor_mp.accept_beat[=3]) ##1 (!monitor_mp.accept_beat & monitor_mp.comppkt_ready) ) or (monitor_mp.comppkt_error[->1] intersect monitor_mp.accept_beat[=0:4]); endproperty a_4_output_pkts_after_ready : assert property (p_4_output_pkts_after_ready) else begin status = new(); status.set_err_4_output_pkts_after_ready(); status_ap.write(status); end
Note that we wrote a separate assertion to handle the boundary case after reset. However, we chose to use the Chapter 8, “Datapath”
235
same error status method as the normal case to identify the violation. For the normal case, the assertion checks that once comppkt_ready is asserted, it remains asserted until either the first occurrence of accept_beat or when comppkt_error is asserted. In addition, this assertion checks that four and only four output packets are transferred (indicated by an active accept_beat), unless an error occurs (indicated by an active comppkt_error). Assertion 8-23, “Valid input to output control behavior” demonstrates our next property. This assertion accounts for the minimum and maximum latency of two clocks after the eight uncompressed input beats have been received and compressed output beats are ready. Assertion 8-23
Valid input to output control behavior
property p_input_to_output_or_error; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) monitor_mp.start_packet ##0 monitor_mp.datain_valid[->8] |-> ##2 monitor_mp.comppkt_ready; endproperty a_input_to_output_or_error : assert property (p_input_to_output_or_error) else begin status = new(); status.set_err_input_to_output_or_error(); status_ap.write(status); end
Assertion 8-24, “Valid packet error behavior” demonstrates our next property. Assertion 8-24
Valid packet error behavior
property p_valid_pkt_error; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) monitor_mp.comppkt_error |-> $rose(monitor_mp.comppkt_ready); endproperty a_valid_pkt_error : assert property (p_valid_pkt_error) else begin status = new(); status.set_err_valid_pkt_error(); status_ap.write(status); end
236 Creating Assertion-Based IP
Assertion 8-25, “Valid packet error pulse” demonstrates our next property. Assertion 8-25
Valid packet error pulse
property p_valid_pkt_error_pulse; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) monitor_mp.comppkt_error |=> !monitor_mp.comppkt_error; endproperty a_valid_pkt_error_pulse : assert property (p_valid_pkt_error_pulse) else begin status = new(); status.set_err_valid_pkt_error_pulse(); status_ap.write(status); end
Assertion 8-26, “Valid fast packet behavior” demonstrates our next property. Assertion 8-26
Valid fast packet behavior
property p_valid_fast_packet; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) ((monitor_mp.comppkt_ready[->2] intersect monitor_mp.accept_beat[=0:3]) or (monitor_mp.comppkt_ready[->2] intersect monitor_mp.accept_beat[->4])) |-> monitor_mp.comppkt_error; endproperty a_valid_fast_packet : assert property (p_valid_fast_packet) else begin status = new(); status.set_err_valid_fast_packet(); status_ap.write(status); end
Assertion 8-27, “Packet data compressor stable output data” demonstrates our next property.
Chapter 8, “Datapath”
237
Assertion 8-27
Packet data compressor stable output data
property p_dataout_stable; @(posedge monitor_mp.clk) !monitor_mp.accept_beat |-> $stable(monitor_mp.dataout)) endproperty a_dataout_stable : assert property (p_dataout_stable) else begin status = new(); status.set_err_dataout_stable(); status_ap.write(status); end
Data integrity property Our final property (a_compressed_data_integrity) involves data compression integrity. This is an example of a property that is generally too difficult to express using SVA constructs alone. We could introduce additional modeling code that assembles the eight beats of uncompressed data. Then, our modeling code would implement the compression algorithm to create a compressed output packet for comparison. We would then write a set of assertions that compare the four beats of compressed data read out of the compressor to the values calculated by the modeling code. This illustrates the point that assertions are not always the most efficient means of specifying and verifying data integrity properties in simulation. Alternative solutions involving verification components, such as a scoreboard, often provide a more effective approach to verifying data integrity properties. For an excellent overview of scoreboard verification component use, see [Glasser et al., 2007].
8.4.5 Encapsulate properties In this step, we turn our set of related packet data compressor assertions (and any coverage properties) into a reusable assertion-based monitor verification component. We add analysis ports to our monitor for communication
238 Creating Assertion-Based IP
with other simulation verification components as Chapter 3 “The Process” demonstrates. Example 8-12
Encapsulate properties inside a module
import avm_pkg::*; import tb_tr_pkg::*; // tb_cmp_status class definition module tb_data_compressor_mon ( interface.monitor_mp monitor_mp ); avm_analysis_port #(tb_cmp_status) status_ap = new(“status_ap”, null); avm_analysis_port #(tb_cmp_coverage) coverage_ap = new(“coverage_ap”, null); tb_cmp_status status; . . . // Any required modeling code here (to model environment) . . . // All assertions and coverage properties here . . . endmodule
8.5 Data decompression Data decompression is the inverse application of data compression. Compressed input data is expanded (decompressed) and restored to its original form. Data being expanded may be either a single word or a group of incoming words within a packet. Similarly, the expanded data may be sent out as a single word or as multiple words within a packet. In this section, we introduce a simple packet data decompressor design component to demonstrate our assertion-based IP creation process.
Chapter 8, “Datapath”
239
8.5.1 Block diagram interface description Figure 8-22 illustrates a block diagram for a simple packet data decompressor design. For our example, all signal transitions relate only to the rising edge of the clock (clk). Table 8-9 provides a summary of the interface signals for our simple packet data decompressor design example. Figure 8-22
A simple packet data decompressor block diagram
datain[7:0]
dataout[15:0]
start_packet
dataout_valid
accept_beat
Packet Data Decompressor
decomppkt_ready decomppkt_error
rst_n clk
Table 8-9 Interface description for packet data decompressor
Name start_packet datain[7:0] accept_beat decomppkt_ready decomppkt_error
dataout_valid dataout[15:0] clk rst_n
Description First packet beat of compressed data Compressed input data Input packet data beat has been accepted Decompressed packet is ready for input Error signal asserted when a packet is not fully received or is not fully read out before another packet is started Output data valid from decompressor Expanded data out Synchronous clock Asynchronous reset
8.5.2 Overview description Our simple data decompressor accepts a packet of four eight-bit compressed data beats and sends out the packet 240 Creating Assertion-Based IP
uncompressed into eight 16-bit data beats. The details of the decoding algorithm are not important for our discussion and are not presented in this section. There are flow control signals that indicate when data is ready to be sent and when it has been received. In addition, the comppkt_ready signal indicates the beginning transmission of an input packet, which is to be uncompressed.
Basic packet data decompression operation The waveforms in Figure 8-23 and Figure 8-24 illustrate the typical operation for our simple packet data decompressor. Figure 8-23
Packet data decompressor input operation 0
1
2
3
4
5
start_packet
accept_beat d0
datain[7:0]
Figure 8-24
d2
d1
d3
Packet data decompressor output operation 6
7
8
13
14
15
decomppkt_ready dataout_valid dataout[15:0]
D0
D1
D7
decomppkt_error
Once the four beats of compressed and data have been received, the packet is expanded and is then sent out as a packet of eight beats of uncompressed data. For our simple example, we assume a minimum and maximum latency of two clocks between the time the last beat of a compressed packet is received and the first beat of an expanded packet is transmitted.
Chapter 8, “Datapath”
241
Error transfer operation Our simple packet data decompressor supports fast input devices by allowing incoming compressed data packets to be dropped when the comppkt_error signal is asserted by the fast input device. Our simple packet data decompressor begins a new uncompressed operation on the incoming packet. When this error condition occurs, the signal comppkt_error is asserted for a single cycle, which indicates that the receiving device should flush all partially received packet beats.
8.5.3 Natural language properties Prior to creating a set of SystemVerilog assertions for our simple packet data decompressor design, we must identify a comprehensive list of natural language properties, as shown in Table 8-10. Although the set of properties found in this table is not necessarily comprehensive, it is representative of a real set of properties and sufficient to demonstrate our process. Table 8-10 Simple packet data decompressor design properties Assertion name
Summary
a_reset_state
Output controls after reset are inactive Eight and only eight uncompressed packets are transmitted only after a
a_8_output_pkts_after_start
start_packet
No new start_packet(s) are asserted until all input packets are received, or a decomppkt_error is asserted
242 Creating Assertion-Based IP
Assertion name
Summary
a_4_input_pkts_after_ready
Four and only four compressed packets are received only after a start_packet is asserted
a_datain_stable a_in_to_out_or_err
a_uncompressed_data_integrity
No new start_packet(s) are asserted until all input packets are received, or a decomppkt_error is asserted The datain bus must be stable until accept_beat is asserted After a start_packet, if there is no error while receiving four compressed packet beats, then decomppkt_ready must be asserted two cycles after last beat The data from the input packet, once uncompressed, should be the same value as the output packet
8.5.4 Assertions To create a set of SystemVerilog assertions for our simple packet data decompressor design, we begin defining the connection between our assertion-based monitor and other potential components within the testbench (such as a driver transactor and the DUV). We accomplish this goal by creating a SystemVerilog interface, as Figure 8-25 illustrates.
Chapter 8, “Datapath”
243
Figure 8-25
SystemVerilog interface
Driver
DUV
AssertionBased Monitor
Example 8-13 demonstrated an interface for our simple packet data decompressor example. For this case, our assertion-based monitor would reference the interface signals via the direction defined by the monitor_mp named modport. .
Example 8-13
Encapsulate signals inside an interface
interface tb_data_decompressor_if( input clk , input rst_n ); bit bit [7:0] bit
start_packet; datain; accept_beat;
bit decomppkt_error; bit decomppkt_ready; bit [15:0] dataout; bit dataout_valid; modport driver_mp ( input clk, rst, . . . . ); . . . modport duv_mp ( input clk, rst, . . . . ); // Continued on next page
244 Creating Assertion-Based IP
Example 8-13
Encapsulate signals inside an interface
modport monitor_mp ( input clk, rst, input datain, input start_packet, input accept_beat, input dataout_valid, input decomppkt_ready, input decomppkt_error, input dataout ); endinterface
In addition to pin-level interfaces, we need a means for our simple packet data decompressor assertion-based monitor to communicate with various analysis verification components within the testbench. Hence, we introduce an error status analysis port within our monitor, as Figure 8-26 illustrates. Figure 8-26
Error status and coverage analysis ports coverage AssertionBased Monitor
Coverage Collector
error status
Test Controller
In addition to assertion error status, coverage events are an important piece of analysis data that requires its own analysis port. We discuss coverage with respect to creating assertion-based IP separately in Section 5.4. Upon detecting a data decompression error, the analysis port broadcasts the error condition (using an error status transaction object) to other verification components.
Chapter 8, “Datapath”
245
The error status transaction class is defined in Example 814, which is an extension of avm_transaction base class. The tb_decmp_status class includes an enum that identifies the various types of data decompressor errors and a set of methods to uniquely identify the specific error it detected. Example 8-14
Error status class
class tb_decmp_status extends avm_transaction; typedef enum { ERR_RESET_STATE, ERR_8_OUTPUT_PKTS_AFTER_START, ERR_4_INPUT_PKTS_AFTER_READY, ERR_DATAIN_STABLE, ERR_IN_TO_OUT_OR_ERR, ERR_UNCOMPRESSED_DATA_INTEGRITY } decmp_status_t; decmp_status_t decmp_status; int err_client_num; function void set_err_reset_state; decmp_status = ERR_RESET_STATE ; endfunction function void set_err_8_output_pkts_after_start; decmp_status = ERR_8_OUTPUT_PKTS_AFTER_START ; endfunction function void set_err_4_input_pkts_after_ready; decmp_status = ERR_4_INPUT_PKTS_AFTER_READY ; endfunction function void set_err_datain_stable; decmp_status = ERR_DATAIN_STABLE ; endfunction function void set_err_in_to_out_or_err; decmp_status = ERR_IN_TO_OUT_OR_ERR ; endfunction function void set_err_uncompressed_data_integrity; decmp_status = ERR_UNCOMPRESSED_DATA_INTEGRITY ; endfunction . . . endclass
We are now ready to write assertions for our simple packet data decompressor design properties defined in Table 8-10 (see page 242). Assertion 8-28, “Packet data decompressor inactive after reset” demonstrates our first property.
246 Creating Assertion-Based IP
Assertion 8-28
Packet data decompressor inactive after reset
property p_reset_state; @(posedge monitor_mp.clk) $rose(monitor_mp.rst_n) |-> (monitor_mp.accept_beat==1’b0) & (monitor_mp.decomppkt_error==1’b0) & (monitor_mp.decomppkt_ready==1’b0) & (monitor_mp.dataout==16’b0) & (monitor_mp.dataout_valid==1’b0); endproperty a_reset_state : assert property (p_reset_state) else begin status = new(); status.set_err_reset_state(); status_ap.write(status); end
Assertion 8-29, “Valid compressed input packet sequence” demonstrates our next property. Assertion 8-29
Valid compressed input packet sequence
// Boundary case after reset property p_4_input_pkts_after_ready_init; @(posedge monitor_mp.clk) $rose(monitor_mp.rst_n) |-> !(monitor_mp.accept_decomppkt | monitor_mp.comppkt_error) throughout monitor_mp.start_packet[->1]; endproperty a_4_input_pkts_after_ready_init : assert property (p_4_input_pkts_after_ready_init) else begin status = new(); status.set_err_4_input_pkts_after_ready(); status_ap.write(status); end // Normal case property p_4_input_pkts_after_ready; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) $rose(start_packet) |-> ( (monitor_mp.start_packet throughout monitor_mp.accept_beat[->1]) ##1 (!monitor_mp.start_packet thoughtout monitor_mp.accept_beat[=3]) ##1 (!monitor_mp.accept_beat & monitor_mp.decomppkt_ready) ) or (monitor_mp.comppkt_error[->1] intersect monitor_mp.accept_beat[=0:4]); endproperty // Continued on next page
Chapter 8, “Datapath”
247
Assertion 8-29
Valid compressed input packet sequence
a_4_input_pkts_after_ready : assert property (p_4_input_pkts_after_ready) else begin status = new(); status.set_err_4_input_pkts_after_ready(); status_ap.write(status); end
Note in Assertion 8-29 that we wrote a separate assertion to handle the boundary case after reset. However, we chose to use the same error status method as the normal case to identify the violation. For the normal case, the assertion checks that once start_packet is asserted, it remains asserted until either the first occurrence of accept_beat or when comppkt_error is asserted. In addition, this assertion checks that four and only four input packets are transferred (indicated by an active accept_beat), unless an error occurs (indicated by an active comppkt_error). Assertion 8-30, “Valid uncompressed output packet sequence” demonstrates our next property. Note that we wrote a separate assertion to handle the boundary case after reset. However, we chose to use the same error status method as the normal case to identify the violation. Assertion 8-30
Valid uncompressed output packet sequence
// Boundary case after reset property p_8_output_pkts_after_start_init; @(posedge monitor_mp.clk) $rose(monitor_mp.rst_n) |-> (!monitor_mp.datain_valid throughout monitor_mp.decomppkt_ready[->1]); endproperty a_8_output_pkts_after_start_init : assert property (p_8_output_pkts_after_start_init) else begin status = new(); status.set_err_8_output_pkts_after_start(); status_ap.write(status); end // Continued on next page
248 Creating Assertion-Based IP
Assertion 8-30
Valid uncompressed output packet sequence
// Normal case property p_8_output_pkts_after_start; @(posedge monitor_mp.clk) monitor_mp.decomppkt_ready |-> ( (monitor_mp.dataout_valid) ##1 (~monitor_mp.decomppkt throughout monitor_mp.dataout_valid[=7]) ##1 (!monitor_mp.dataout_valid & monitor_mp.decomppkt_ready) ); endproperty a_8_output_pkts_after_start : assert property (p_8_output_pkts_after_start) else begin status = new(); status.set_err_8_output_pkts_after_start(); status_ap.write(status); end
Assertion 8-31, “Packet data decompressor stable input data” demonstrates our next property. Assertion 8-31
Packet data decompressor stable input data
property p_datain_stable; @(posedge monitor_mp.clk) !monitor_mp.accept_beat |-> $stable(monitor_mp.datain)) endproperty a_datain_stable : assert property (p_datain_stable) else begin status = new(); status.set_err_datain_stable(); status_ap.write(status); end
Assertion 8-32, “Valid input to output control behavior” demonstrates our next property. This assertion accounts for the minimum and maximum latency of two clocks after the four compressed input beats have been received and decompressed output beats are ready.
Chapter 8, “Datapath”
249
Assertion 8-32
Valid input to output control behavior
property p_in_to_out_or_err; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) (monitor_mp.start_packet) ##0 ( !monitor_mp.decomppkt_error throughout (monitor_mp.accept_beat[->4]) ) |-> ##2 (monitor_mp.decomppkt_ready); endproperty a_in_to_out_or_err : assert property (p_in_to_out_or_err) else begin status = new(); status.set_err_in_to_out_or_err(); status_ap.write(status); end
Data integrity property Our final property (a_expanded_data_integrity) involves data decompression integrity. This is an example of a property that is generally too difficult to express using SVA constructs alone. We could introduce additional modeling code that assembles the four beats of compressed data. Then, our modeling code would implement the decompression algorithm to create an uncompressed output packet for comparison. We would then write a set of assertions that compare the eight beats of uncompressed data read out of the decompressor to the values calculated by the modeling code. This illustrates the point that assertions are not always the most efficient means of specifying and verifying data integrity properties in simulation. Alternative solutions involving verification components, such as a scoreboard, often provide a more effective approach to verifying data integrity properties. For an excellent overview of scoreboard verification component use, see [Glasser et al., 2007].
250 Creating Assertion-Based IP
8.5.5 Encapsulate properties In this step, we turn our set of related simple packet data decompressor assertions (and any coverage properties) into a reusable assertion-based monitor verification component. We add analysis ports to our monitor for communication with other simulation verification components as Chapter 3 “The Process” demonstrates. Example 8-15
Encapsulate properties inside a module
import avm_pkg::*; import tb_tr_pkg::*; // tb_decmp_status class definition module tb_data_decompressor_mon ( interface.monitor_mp monitor_mp ); avm_analysis_port #(tb_decmp_status) status_ap = new(“status_ap”, null); avm_analysis_port #(tb_decmp_coverage) coverage_ap = new(“coverage_ap”, null); tb_decmp_status status; . . . // Any required modeling code here (to model environment) . . . // All assertions and coverage properties here . . . endmodule
8.6 Summary In this chapter, our focus was on demonstrating our assertion-based IP creation process on various datapath components. One message we hope you take away from this chapter is that not all behaviors are easily expressed using temporal constructs from an assertion language alone. In fact, in this chapter we demonstrated the increasing difficulty of writing data integrity properties as the sections progress. While in Section 8.1, “Multiport register file,” after a fair amount of work, we were able to create a set of data integrity assertions by taking advantage of SVA local variables, in Section 8.2, “Data queue” it became necessary Chapter 8, “Datapath”
251
to add modeling code to simplify coding our data integrity assertions. The design component examples in Section 8.4, “Data compression” and Section 8.5, “Data decompression” are actually easier to check in simulation using a scoreboard than trying to write a set of complex data integrity assertions. A key point in this section is that, when creating verification IP, choose the form that is easiest to express. Do not be a purist! You do not have to capture all behaviors of the design using SVA constructs alone. Indeed, doing so is often not possible.
252 Creating Assertion-Based IP
A
P P E N D I X
QUICK TUTORIAL FOR SVA
APPENDIX A
In this appendix, we provide a quick tutorial for SystemVerilog Assertions (SVA). It is not our objective to present a comprehensive overview of SVA. Our goal is to provide you with enough information so that you can understand the examples presented in this book. For a complete overview and reference of the SVA language, we recommend the following sources: [Cohen 2005], [Haque et al., 2006], and [Vijayaraghavan and Ramanathan 2005].
A.1 SVA fundamentals SVA, an assertion sublanguage of the IEEE Std 1800-2005 SystemVerilog standard [IEEE 1800-2005], is a linear-time temporal logic that is intended to be used to specify assertions and functional coverage properties for the validation and verification of concurrent systems. Using SVA, we are able to specify the design intent (that is, expected behavior) in terms of properties. Once defined, we can check our properties as assertions or use them as verification constraints (assumptions), or they can describe
Appendix A, “Quick Tutorial For SVA”
253
behavior that must be observed during the course of simulation (that is, coverage properties). SVA contains two types of assertions: immediate and concurrent. Immediate assertions follow simulation event semantics for their execution and are executed like a statement in a procedural block. Immediate assertions are primarily intended to be used with simulation. In contrast, concurrent assertions are based on clock semantics and use sampled values of variables (that is, they exist outside the context of procedural code).
A.1.1 Immediate assertions In many respects, an immediate assertion is similar to an if statement. They check to see if a conditional expression holds, and if not, then the simulator generates an error message. For instance, Example A-1 demonstrates a portion of some procedural code associated with an arbiter design where a procedural if statement checks that two of the arbiter’s grant signals are never active at the same time. Example A-1
Immediate check for mutually exclusive grants
module my_arb (. . . ) always @* begin // arbiter code . . . if (grant[0] & grant[1])) $display (“Mutex violation for grant[0] and grant[1]”); . . . end endmodule
A key observation is that the Boolean condition for the if statement is immediately checked within the procedural context of the code (that is, when the if statement is visited during the procedural code execution). 254 Creating Assertion-Based IP
Example A-2 demonstrates the same check, except in this case, a SystemVerilog immediate assertion is used. Example A-2
Immediate check for mutually exclusive grants
module my_arb (. . . ) always @* begin // arbiter code . . . assert (!(grant[0] & grant[1])); . . . end endmodule
For this example, the Boolean condition !(grant[0] & grant[1])
is immediately checked only when the assert statement is visited within the procedural context of the SystemVerilog always block. If the Boolean condition does not evaluate true during the procedural execution of the assertion, then an error message is automatically generated. Immediate assertions may be used within SystemVerilog and always blocks or as a task or function.
initial
A.1.2 Concurrent assertions There are properties of a design that must evaluate true globally, and not just during a particular instance in which a line of procedural code executes. Hence, SVA provides support for concurrent assertions, which may be checked outside the execution of a procedural block. That is, the checks are performed concurrently with all other procedural blocks in the verification environment. Concurrent assertions are easily identified by observing the presence of the SVA property keyword combined with the assert directive. Example A-3 situates a concurrent assertion for our arbiter example. In this case, the concurrent Appendix A, “Quick Tutorial For SVA”
255
assertion exists outside the context of a procedural always block. Example A-3
Immediate check for mutually exclusive grants
module my_arb (. . . ) always @* begin // arbiter code . . . end assert property (@(posedge clk) !(grant[0] & grant[1])); endmodule
Concurrent assertions are based on clock semantics, use sampled values of variables, and can be specified in always blocks, initial blocks, or stand alone outside a procedural block. All of the assertion-based IP examples we present in this book consist of a set of concurrent assertions with a specific sample clock associated with them to prevent false firings due to race conditions. One of the challenges associated with immediate (unclocked) assertions contained within procedural code is that they can suffer the same race condition problems as other design code, which can result in false firings during simulation. Hence, you must be careful when using them in this context.
A.1.3 Resets SystemVerilog provides a mechanism to asynchronously disable an assertion during a reset using the SVA disable iff clause. In Example A-4, we demonstrate the same assertion expressed in Example A-3, except we have added a reset signal. Even though our concurrent assertion is clocked (@(posedge clk)) the assertion is asynchronously disabled when the disable iff clause expression evaluates true.
256 Creating Assertion-Based IP
.
Example A-4
Asynchronously disabling an assertion with a reset
module my_arb (. . . ) always @* begin // arbiter code . . . end assert property (@(posedge clk) disable iff (reset) !(grant[0] & grant[1])); endmodule
A.1.4 Action blocks An SVA action block specifies the actions that are taken upon success or failure of the assertion (see the BNF fragment in Example A-5). The statement associated with the success of the assert statement is the first statement of an action block. It is called the pass statement and is executed if the expression evaluates to true. The pass statement can, for example, record the number of successes for a coverage log but can be omitted altogether. If the pass statement is omitted, then no user-specified action is taken when the assert expression holds. The statement associated with the assertion’s else clause is called a fail statement and is executed if the expression evaluates to false. The fail statement can also be omitted. The action block, if specified, is executed immediately after the evaluation of the assert expression.
Appendix A, “Quick Tutorial For SVA”
257
.
Example A-5
SystemVerilog concurrent assertion syntax
concurrent_assertion_statement ::= assert property ( property_spec ) action_block property_spec ::= [ clocking_event ] [ disable iff ( expression_or_dist ) ] property_expr action_block ::= pass_statement_or_null | [ pass_statement ] else fail_statement
Example A-6 demonstrates an assertion where the action block’s pass statement has been omitted, yet the fail statement exists and is used to pass failure information out of the assertion-based IP’s analysis port. Example A-6
SystemVerilog concurrent assertion syntax
assert property ( @(posedge clk) disable iff (reset) !(grant[0] & grant[1]) ) else begin // action block fail statement // See Appendix B, “Complete OVM/AVM Testbench Example” for OVM details status = new(); status.set_err_grant_mutex(); status_ap.write(status); end
A.2 SystemVerilog sequences In the previous section, we demonstrated simple assertions based on the evaluation of a single Boolean expression that must hold true at every sample point in time. In this section, we introduce SVA sequences, which are Boolean expressions that are evaluated in a linear order of increasing time. The temporal delay operator “##” constructs sequences by combining Boolean expressions (or smaller sequences). For
258 Creating Assertion-Based IP
instance, Example A-7 demonstrates a set of simple sequences using the temporal delay operator. Example A-7
Construction of sequences with temporal delay
start ##1 transfer
// a sequence in which the Boolean variable // transfer holds on the clock after start
start ##2 transfer
// a sequence in which the Boolean variable // transfer holds two clocks after start
start ##[0:2] transfer // a sequence in which the Boolean variable // transfer holds between zero to two clocks // after start
Figure A-1 illustrates a sequence in which the Boolean variable transfer holds exactly one clock after start holds. Figure A-1.
Trace on which the sequence (start ##1 transfer) holds
v
|
|
|
|
start transfer
Similarly, Figure A-2 illustrates a sequence in which the Boolean variable transfer holds exactly two clocks after start holds. Figure A-2.
Trace on which the sequence (start ##2 transfer) holds
v
|
|
|
|
start transfer
Finally, Figure A-3 illustrates a sequence in which the Boolean variable transfer holds between zero and two clocks after start holds. In fact, Figure A-1 and Figure A-2 also hold on the sequence defined by (start ##[0:2] transfer). Appendix A, “Quick Tutorial For SVA”
259
Figure A-3.
Trace on which sequence (start ##[0:2] transfer) holds
v
|
|
|
|
start transfer
A temporal delay may begin a sequence. The range may be a single constant amount or a range of time. All times may be used to match the following sequence. The range is interpreted as follows: Example A-8
Construction of sequences with temporal delay
s##0 a
- same as
(a)
##1 a
- same as
(1’b1 ##1 a)
##[0:1] a - same as
a
or
(1’b1 ##1 a)
When the symbol $ is used, the range is infinite. For example, req ##[1:$] gnt specifies a sequence in which a gnt signal occurs at some point after a req. Assertion 6-1, “A requesting client will eventually be served” on page 115 demonstrates the use of a delay operator.
A.2.1 Consecutive repetition SystemVerilog provides syntactic sugar to simplify expressing the repetition for Boolean expressions or a sequence expression. For example, the sequence (a ##1 a) can be expressed as sequence a[*2]. The [*n] construct is used to represent a repetition, where n is a constant. A repetition range can be expressed using [*m:n], where both m and n are constants.
260 Creating Assertion-Based IP
Sequence repetition examples include: .
Example A-9
Construction of sequences with temporal delay
a [*0] ##1 b
same as (b), a is not repeated
a [*2] ##1 b
same as (a ##1 a ##1 b)
a [*1:2] ##1 b
same as (a ##1 b) or (a ##1 a ##1 b)
(a ##1 b) [*2]
same as (a ##1 b ##1 a ##1 b)
The first example demonstrates that a repetition of zero is equivalent to the following: a[*0] ##n b
is equivalent to
##(n-1) b
where n is any constant greater than zero. Assertion 5-4, “Valid transfer size property” on page 74 demonstrates a consecutive repetition.
A.2.2 Goto repetition The goto repetition is syntactic sugar that allows for space (absence of the term) between the repetition of the terms. For example, a[->2] is a shortcut for the following sequence: ~a[*0:$] ##1 a ##1 ~a[*0:$] ##1 a
Assertion 6-2, “Two-client fair arbitration assertion for client 0” on page 116 demonstrate an SVA goto repetition.
A.2.3 Nonconsecutive repetition The nonconsectutive repetition operator is syntactic sugar that allows for space (absence of a term) between the repetition of the terms. For example, a[=2] is a shortcut for the following sequence: Appendix A, “Quick Tutorial For SVA”
261
~a[*0:$] ##1 a ##1 ~a[*0:$] ##1 a ##1 ~a[*0:$]
Note the difference in the goto repetition and nonconsecutive repetition operators. The nonconsecutive operators do not require the repeating term to be true at the end of the sequence. A nonconsecutive repetition is often followed by another element in the sequence. For example: a[=2] ##1 b
This expression describes a sequence of two nonconsecutive occurrences of a, followed eventually by an occurrence of b. Assertion 5-11, “Bus wdata properties” on page 104 demonstrates a nonconsecutive repetition.
A.2.4 Declared named sequences To facilitate reuse, sequences can be declared and then referenced by name. A sequence can be declared in the following: •
a module
•
an interface
•
a program
•
a clocking block
•
a package
•
a compilation-unit scope
Sequences can be declared with or without parameters, as demonstrated in Example A-10.
262 Creating Assertion-Based IP
.
Example A-10
Declared sequences
sequence op_retry; (req ##1 retry); endsequence sequence cache_fill(req, done, fill); (req ##1 done [=1] ##1 fill); endsequence
A.3 Property declarations SystemVerilog allows for declarations of properties to facilitate reuse. A property differs from a sequence in that it contains a clock specification for the entire property and an optional asynchronous term to disable the property evaluations. Named properties can be used to construct more complex properties, or they can be directly used during verification as an assertion, assumption, or coverage item. Properties can be declared with or without parameters, as demonstrated in Example A-11. .
Example A-11
Declared properties
property req_t1_start; @(posedge clk) req && req_tag == t1; endproperty property illegal_op; @(posedge clk) ~(req && cmd == 4); endproperty property en_mutex(en[0], en[1], reset_n); @(posedge clk) disable iff (~reset_n) ~(en[0] & en[1]); endproperty
// asynch reset
Properties may reference other properties in their definition. They may even reference themselves recursively. Properties Appendix A, “Quick Tutorial For SVA”
263
may also be written using multiple clocks to define a sequence that transitions from one clock to another as it matches the elements of the sequence.
A.4 Sequence and property operators The sequence operators defined for SystemVerilog allow us to compose expressions into temporal sequences. These sequences are the building blocks of properties and concurrent assertions. The first four allow us to construct sequences, while the remaining operators allow us to perform operations on a sequence as well as compose a complex sequence from simpler sequences.
A.4.1 AND Both SVA properties and sequences can be operands of the and operator. The operation on two sequences produces a match when both sequences produce a match (the end point may not match). A match occurs until the endpoint of the longer sequence (provided the shorter sequence produces one match). Examples of sequence and are: .
Example A-12
Sequence AND
(a ##1 b) and () same as (), which is a no match (a ##1 b) and (c ##1 d) same as (a & c ##1 b & d) (a ##[1:2] b) and (c ##3 d) same as (a & c ##1 b ##1 1 ##1 d) or (a & c ##1 1 ##1 b ##1 d)
264 Creating Assertion-Based IP
A.4.2 Sequence intersection An intersection of two sequences is like an and of two sequences (both sequences produce a match). This operator also requires the length of the sequences to match. That is, the match point of both sequences must be the same time. With multiple matches of each sequence, a match occurs each time both sequences produce a match. Example A-13 demonstrates the SVA sequence intersect operator. .
Example A-13
Sequence intersect
(1) intersect ()
same as (), which is a no match
##1 a intersect ##2 b
same as (), which is a no match
##2 a intersect ##2 b
match if ##2 (a & b))
##[1:2] a intersect ##[2:3] b
match if (1 ##1 a&b ) or (1 ##1 a&b ##1 b)
##[1:3] a intersect ## [1:3] b match if (##1 a&b) or (##2 a&b) or (##3 a&b)
A.4.3 OR SVA provides a means to or sequences. For or-ed sequences, a match on either sequence results in a match for the operation Example A-14 demonstrates the SVA sequence or operator. .
Example A-14
Sequence OR
() or ()
same as (), which is a no match
(## 2 a or ## [1:2] b)
match if (b) or (##1 b) or (## 2 a) or ( ##2 b)
Appendix A, “Quick Tutorial For SVA”
265
A.4.4 Boolean throughout This operator is used to specify a Boolean value throughout a sequence. The operator produces a match if the sequence matches and the Boolean expression holds until the end of the sequence. Example A-15 demonstrates the SVA sequence throughout operator. .
Example A-15
Sequence throughout
0 throughout
(1)
same as (), which is a no match
1 throughout
##1 a
same as ##1 a
a throughout
##2 b
same as (a ##1 a & b)
a throughout (b ##[1:3] c)
same as (a&b ##1 a ##1 a &c) or (a&b ##1 a ##1 a ##1 a&c) or (a&b ##1 a ##1 a ##1 a ##1 a&c)
A.4.5 Sequence within The within operator determines if one sequence matches within the length of another sequence.
Example A-16 demonstrates the SVA sequence within operator. .
Example A-16
Sequence within
() within (1)
same as (), which is a no match
(1) within ()
same as (), which is a no match
(a) within ## [1:2] b
same as (a&b) or (a ##1 b) or (##1 a&b)
266 Creating Assertion-Based IP
A.4.6 Sequence ended The method ended returns true at the end of the sequence. This is in contrast to matching a sequence from the beginning time point, which is obtained when we use only the sequence name. Example A-17 demonstrates the SVA sequence ended method. .
Example A-17
Sequence ended
sequence a1; @(posedge clk) (c ##1 b ##1 d); endsequence (a ##1
a1.ended)
same as (c ##1 b & a ##1 d)
(a ##2
a1.ended)
same as (c & a ## b ##1 d)
A.4.7 Sequence first_match The first_match operator returns true only for the first occurrence of a multiple occurrence match for a sequence. Example A-18 demonstrates the SVA sequence first_match method. .
Example A-18
Sequence ended
first_match (1 [*1:5])
same as (1)
first_match (##[0:4] 1)
same as (1)
first_match (##[0:1] a)
same as (a) or (!a ##1 a)
first_match (b throughout s1) same as (b throughout first_match(s1)) first_match(s1 within s2)
same as (s1 within first_match (s2))
Appendix A, “Quick Tutorial For SVA”
267
A.4.8 Implication SystemVerilog supports implication of properties and sequences. The left-hand operand of the implication is called the antecedent and the right-hand operand is called the consequent. Evaluation of an implication starts through repeated attempts to evaluate the antecedent. When the antecedent succeeds, the consequent is required to succeed for the property to hold. As a convenience, there are two forms of implication, overlapping (|->) and non-overlapping (|=>). The overlap occurs between the final cycle of the left-hand side (the antecedent) and the starting cycle of the right-hand side (the consequent) operands. For the overlapping form, the consequent starts on the current cycle (that the antecedent matched). The non-overlapping form has the consequent start the subsequent cycle. Implication is similar in concept to an if statement. Example A-19 demonstrates the SVA implication operators. .
Example A-19
Implication operators
a |-> b
same as
a ? b : 1’b1
(a ##1 b) |-> (c)
conceptually same as
(a ##1 b) |=> (c ##1 d)
same as
(a ##1 b) ? c : 1’b1
(a ##1 b) |-> ##1 c ##1 d)
A.5 SVA system functions and task SVA provides various system functions to facilitate specifying common functionality. This section describes a few of SVA’s more commonly used system functions.
268 Creating Assertion-Based IP
A.5.1 Sampled value functions Sampled value functions include the capability to access the current or past sampled value, or detect changes in the sampled value of an expression. The following functions are provided: $sampled(expression [, clocking_event]) $rose( expression [, clocking_event]) $fell( expression [, clocking_event]) $stable( expression [, clocking_event]) $past( expression1 [, number_of_ticks] [, expression2] [, clocking_event])
Using these functions is not limited to assertion features; they can be used as expressions in procedural code as well. The clocking event, although optional as an explicit argument to the functions, is required for semantics. The clocking event is used to sample the value of the argument expression. The clocking event must be explicitly specified as an argument or inferred from the code where it is used. The following rules are used to infer the clocking event: •
If used in an assertion, the appropriate clocking event from the assertion is used.
•
If used in an action block of a singly clocked assertion, the clock of the assertion is used.
•
If used in a procedural block, the inferred clock, if any, for the procedural code is used.
Otherwise, default clocking is used. In addition to accessing value changes, the past values can be accessed with the $past function. The following three optional arguments are provided: •
expression2 is used as a gating expression for the clocking event Appendix A, “Quick Tutorial For SVA”
269
•
number_of_ticks specifies the number of clock ticks in the past
•
clocking_event specifies the clocking event for sampling expression1
The expression1 and expression2 can be any expression allowed in assertions. Assertion 5-12, “Bus slave ready assertions” on page 105 demonstrates both a $rose and $fell. Assertion 5-5, “Bus must reset to INACTIVE state property” on page 86 demonstrates $past.
A.5.2 Useful functions Assertions are commonly used to evaluate certain specific characteristics of a design implementation, such as whether a particular signal is “one-hot.” The following system functions are included to facilitate such common assertion functionality: •
$onehot (<expression>) returns true if only one bit of the expression is high.
•
$onehot0 (<expression>) returns true if at most one bit of the expression is high.
•
$isunknown (<expression>) returns true if any bit of the expression is X or Z. This is equivalent to ^<expression> === ’bx.
All of the above system functions have a return type of bit. A return value of 1’b1 indicates true, and a return value of 1’b0 indicates false. Another useful function provided for the Boolean expression is $countones, to count the number of ones in a bit vector expression. •
$countones ( expression)
270 Creating Assertion-Based IP
An X and Z value of a bit is not counted towards the number of ones. Assertion 6-6, “Built-in function to check mutually exclusive grants” on page 119 demonstrates a $onehot system function.
A.5.3 System tasks SystemVerilog has defined several severity system tasks for use in reporting messages. These system tasks are defined as follows: $fatal(finish_num [, message {, message_argument } ] ); This system task reports the error message provided and terminates the simulation with the finish_num error code. This system task is best used to report fatal errors related to testbench/OS system failures (for example, can’t open, read, or write to files) The message and argument format are the same as the $display() system task. $error( message {, message_argument } ); This system task reports the error message as a run-time error in a tool-specific manner. However, it provides the following information: •
severity of the message
•
file and line number of the system task call
•
hierarchical name of the scope or assertion or property
•
simulation time of the failure
$warning(message {, message_argument } ); This system task reports the warning message as a run-time warning in a format similar to $error and with similar information.
Appendix A, “Quick Tutorial For SVA”
271
$info(message {, message_argument } ); This system task reports the informational message in a format similar to $error and with similar information. $asserton(levels, [ list_of_modules_or_assertions]) This system task will reenable assertion and coverage statements. This allows sequences and properties to match elements. If a level of 0 is given, all statements in the design are affected. If a list of modules is given, then that module and modules instantiated to a depth of the level parameter are affected. If specifically named assertion statements are listed, then they are affected. $assertkill(levels, [ list_of_modules_or_assertions]) This system task stops the execution of all assertion and cover statements. These statements will not begin matching until re-enabled with $asserton(). Use the arguments in the same way as $asserton uses them. $assertoff(levels, [ list_of_modules_or_assertions]) This system task prevents matching of assertion and cover statements. Sequences and properties in the process of matching sequences will continue. Assertion and cover statements will not begin matching again until re-enabled with $asserton(). Use the arguments in the same way as $asserton uses them.
A.6 Dynamic data within sequences SVA includes the ability to call a routine or sample data within the sequence for later analysis. The ability to call a routine (tasks, void functions, methods, and system tasks) allows you to record data for analysis or debugging. Example A-20 demonstrates the SVA sequences with system task calls. 272 Creating Assertion-Based IP
.
Example A-20
Calling system task within a sequence
sequence track_it; (req[->1],
$display(“Found the request at cycle %0d\n”, ‘cycle)) ##1 (sent[->1], $display(“Sent occurred at cycle %0d\n”, ‘cycle)) ##3 done; endsequence assert property (start |=> track_it);
SVA sequences allow you to declare local variables that can be used to sample data at a specific point within a sequence. Example 5-12, “Encapsulate coverage properties inside a module” on page 111 demonstrates an SVA sequence with a local variable. Long et. al [2007] provide an excellent overview and set of guidelines for coding local variables.
A.7 SVA directives SVA directives specify how properties are to be used. Example A-21 describes the syntax for SVA directives. .
Example A-21
Directives
immediate_assert_statement ::= assert ( expression ) action_block concurrent_assert_statement ::= assert property ‘(‘ property_spec ‘)’ action_block concurrent_assume_statement ::= assume property ‘(‘ property_spec ‘)’ ‘;’ concurrent_cover_statement ::= cover property ‘(‘ property_spec ‘)’ statement_or_null action_block ::= statement [ else statement_or_null ] | [statement_or_null] else statement_or_null statement_or_null ::= statement | ‘;’
Appendix A, “Quick Tutorial For SVA”
273
A.8 Useful named property examples This section defines a set of useful, named property declarations we have created with the intent to be shared by an assertion-based IP design team. Our goal is to create a set of named properties that is similar to a some of the expressive IEEE 1850 Std. Property Specification Language (PSL) [IEEE 1850-2005] linear-time temporal operators. This allows us to express complex properties with minimum code through reuse. .
Example A-22
Named property declarations
// file property_declarations.h ‘ifndef PROPERTY_DECLARATIONS ‘define PROPERTY_DECLARATIONS // P -> next (Q until R) property P_impl_Q_weak_until_R(ck,rst,P,Q,R); @(posedge ck) disable iff (rst) $rose(P) ##1 (~R)[*1:$] |-> Q; endproperty // P -> next (Q until! R) property P_impl_Q_strong_until_R(ck,rst,P,Q,R); @(posedge ck) disable iff (rst) $rose(P) |=> (~R throughout (Q[*0:$])) ##1 R; endproperty // P -> next (Q before R) property P_impl_Q_weak_before_R(ck,rst,P,Q,R); @(posedge ck) disable iff (rst) $rose(P) ##1 (~(Q & ~R))[*1:$] |-> ~R; endproperty // P -> next ( Q before! R) property P_impl_Q_strong_before_R(ck,rst,P,Q,R); @(posedge ck) disable iff (rst) $rose(P) |=> (~R throughout Q[=1:$]) ##1 (R & ~Q); endproperty ‘endif
274 Creating Assertion-Based IP
A
P P E N D I X
COMPLETE OVM/AVM TESTBENCH EXAMPLE APPENDIX B
In this appendix, we provide a complete OVM/AVM example to illustrate how to integrate assertion-based IP into a transaction-level testbench. We have divided this appendix into two parts. The first part provides the source code for our simple nonpipelined bus interface example (previously discussed in Section 5.2 on page 75). The second part provides a high-level reference for many of the potentially less obvious OVM/AVM classes used on our testbench example. The Open Verification Methodology (or OVM) is an opensource and class-based library that is freely available under an Apache License, Version 2.0 open-source license. OVM is a superset of the Cadence Design System’s Unified Reuse Methodology (URM) and Mentor Graphic's Advanced Verification Methodology (AVM) [Glasser et al., 2007], with some additional features. We chose the Open Verification Methodology as the basis for our testbench example because the source code for the OVM library is openly available and can be downloaded at http:// www.mentor.com/go/cookbook. Assuredly, there are other testbench base-class libraries available. The general ideas, processes, and techniques we present in this appendix to Appendix B, “Complete OVM/AVM Testbench Example”
275
demonstrate our assertion-based IP within a transactionlevel testbench can be extended to the specific implementation details of other verification environment methodologies, such as the VMM [Bergeron et al., 2006].
B.1 OVM/AVM Example Source Code Figure B-1 illustrates our testbench example for the simple nonpipelined bus interface example (previously discussed in Section 5.2 on page 75). Figure B-1
Simple nonpipelined bus testbench example
Driver
Responder
coverage AssertionBased Monitor
Coverage Collector
error status
Stimulus Generator
Test Controller
You might note that our example testbench is lacking a DUV. For our example, we are emulating the bus behavior using a responder verification transactor. For this case, the responder could be replaced with an actual DUV at a point in the design cycle when the RTL is available. However, the current testbench environment allows us to debug our 276 Creating Assertion-Based IP
assertion-based IP module prior to completion of the DUV. The source code for the various verification components is defined in the following sections. Example B-1
Testbench top module
interface clk_rst_if; bit clk , rst; endinterface module top; import avm_pkg::*; import tb_tr_pkg::*; import tb_env_pkg::*; clk_rst_if clk_rst_bus(); tb_clock_reset clock_reset_gen( clk_rst_bus ); pins_if #(.DATA_SIZE(8),.ADDR_SIZE(8)) nonpiped_bus ( .clk( clk_rst_bus.clk ), .rst( clk_rst_bus.rst) ); // assertion-based IP module tb_monitor_mod tb_monitor( .bus_if( nonpiped_bus ) ); // class-based components instantiated within the environment class tb_env env; initial begin env = new( nonpiped_bus, tb_monitor.cov_ap, tb_monitor.status_ap ); fork clock_reset_gen.run; join_none env.do_test; $finish; end endmodule
Appendix B, “Complete OVM/AVM Testbench Example”
277
B.1.1 top module The top module shown in Example B-1 (see page 277) instantiates all SystemVerilog interfaces, modules, and class-based components.
B.1.2 tb_clock_reset module The SystemVerilog tb_clock_reset module is responsible for generating all testbench clock and reset activity. . Module-based reference of an interface
Example B-2
Clock and reset generator
module tb_clock_reset( interface i ); parameter bit ACTIVE_RESET = 1; bit stop; initial begin stop = 0; end task run( int reset_hold = 2 , int half_period = 10 , int count = 0 ); i.clk = 0; i.rst = ACTIVE_RESET; for( int i = 0; i < reset_hold;i++ ) begin tick( half_period ); tick( half_period ); end i.rst = !i.rst; for( int i = 0; (i < count || count == 0) && !stop; i++ ) begin tick( half_period ); end endtask task tick( int half_period ); # half_period; i.clk = !i.clk; endtask endmodule
278 Creating Assertion-Based IP
B.1.3 pins_if interface The SystemVerilog pins_if interface defines the testbench interconnect for the various components that connect to our simple nonpipelined bus. . Module-based reference of an interface
Example B-3
SystemVerilog interface definition for pins_if
import avm_pkg::*; import tb_tr_pkg::*; interface pins_if( input clk , input rst ); parameter int DATA_SIZE = 8; parameter int ADDR_SIZE = 8; bit sel; bit en; bit write; bit [DATA_SIZE-1:0] wdata; bit [DATA_SIZE-1:0] rdata; bit [ADDR_SIZE-1:0] addr; modport driver_mp ( input clk , rst , output sel , en , write , addr , output wdata , input rdata ); modport responder_mp ( input clk , rst , input sel , en , write , addr , input wdata , output rdata ); modport monitor_mp ( input clk , rst , input sel , en , write , addr , input wdata , input rdata ); endinterface
Appendix B, “Complete OVM/AVM Testbench Example”
279
B.1.4 tb_monitor module The tb_monitor module, which is illustrated as the Assertion-Based Monitor in Figure B-1, is our assertionbased IP for the nonpipelined bus. Example B-4
Assertion-based IP tb_monitor module
import avm_pkg::*; import tb_tr_pkg::*; module tb_monitor_mod( interface bus_if ); avm_analysis_port #(tb_transaction) cov_ap = new(“cov_ap”, null); avm_analysis_port #(tb_status) status_ap = new(“status_ap”, null); parameter DATA_SIZE parameter ADDR_SIZE
= 8; = 8;
tb_status status; // See Section B.1.7 (see page 287) for details // Used to decode bus control signals bit bit bit bit bit bit
[ADDR_SIZE-1:0] bus_addr; [DATA_SIZE-1:0] bus_wdata; [DATA_SIZE-1:0] bus_rdata; bus_write; bus_sel; bus_en;
bit bit bit bit bit
bus_reset; bus_inactive; bus_start; bus_active; bus_error;
// Identify conceptual states from bus control signals always @(posedge bus_if.monitor_mp.clk) begin bus_addr bus_wdata bus_rdata bus_write
= = = =
bus_if.monitor_mp.addr; bus_if.monitor_mp.wdata; bus_if.monitor_mp.rdata; bus_if.monitor_mp.write;
bus_sel = bus_if.monitor_mp.sel; bus_en = bus_if.monitor_mp.en; // Continued on next page
280 Creating Assertion-Based IP
Example B-4
Assertion-based IP tb_monitor module
if (bus_if.monitor_mp.rst) begin bus_reset = 1; bus_inactive = 1; bus_start = 0; bus_active = 0; bus_error = 0; end else begin bus_reset = 0; bus_inactive = ~bus_if.monitor_mp.sel ~bus_if.monitor_mp.en; bus_start = bus_if.monitor_mp.sel ~bus_if.monitor_mp.en; bus_active = bus_if.monitor_mp.sel bus_if.monitor_mp.en; bus_error = ~bus_if.monitor_mp.sel bus_if.monitor_mp.en;
& & & &
end end // --------------------------------------------// REQUIREMENT: Bus legal states // --------------------------------------------property p_reset_inactive; @(posedge bus_if.monitor_mp.clk) disable iff (bus_reset) $past(bus_reset) |-> (bus_inactive); endproperty assert property (p_reset_inactive) else begin status = new(); status.set_err_trans_reset(); status_ap.write(status); end property p_valid_inact_trans; @(posedge bus_if.monitor_mp.clk) disable iff (bus_reset) ( bus_inactive) |=> (( bus_inactive) || (bus_start)); endproperty assert property (p_valid_inact_trans) else begin status = new(); status.set_err_trans_inactive(); status_ap.write(status); end // Continued on next page
Appendix B, “Complete OVM/AVM Testbench Example”
281
Example B-4
Assertion-based IP tb_monitor module
property p_valid_start_trans; @(posedge bus_if.monitor_mp.clk) disable iff (bus_reset) (bus_start) |=> (bus_active); endproperty assert property (p_valid_start_trans) else begin status = new(); status.set_err_trans_start(); status_ap.write(status); end property p_valid_active_trans; @(posedge bus_if.monitor_mp.clk) disable iff (bus_reset) (bus_active) |=> (( bus_inactive | bus_start)); endproperty assert property (p_valid_active_trans) else begin status = new(); status.set_err_trans_active(); status_ap.write(status); end property p_valid_error_trans; @(posedge bus_if.monitor_mp.clk) disable iff (bus_reset) (~bus_error); endproperty assert property (p_valid_error_trans) else begin status = new(); status.set_err_trans_error(); status_ap.write(status); end // --------------------------------------------// REQUIREMENT: Bus must remain stable // --------------------------------------------property p_bsel_stable; @(posedge bus_if.monitor_mp.clk) disable iff (bus_reset) (bus_start) |=> ($stable(bus_sel)); endproperty assert property (p_bsel_stable) else begin status = new(); status.set_err_stable_sel(); status_ap.write(status); end // Continued on next page
282 Creating Assertion-Based IP
Example B-4
Assertion-based IP tb_monitor module
property p_baddr_stable; @(posedge bus_if.monitor_mp.clk) disable iff (bus_reset) (bus_start) |=> $stable(bus_addr); endproperty assert property (p_baddr_stable) else begin status = new(); status.set_err_stable_addr(); status_ap.write(status); end property p_bwrite_stable; @(posedge bus_if.monitor_mp.clk) disable iff (bus_reset) (bus_start) |=> $stable(bus_write); endproperty assert property (p_bwrite_stable) else begin status = new(); status.set_err_stable_write(); status_ap.write(status); end property p_bwdata_stable; @(posedge bus_if.monitor_mp.clk) disable iff (bus_reset) (bus_start) && (bus_write) |=> $stable(bus_wdata); endproperty assert property (p_bwdata_stable) else begin status = new(); status.set_err_stable_wdata(); status_ap.write(status); end property p_burst_size; int psize; @(posedge bus_if.monitor_mp.clk) ((bus_inactive), psize=0) ##1 ((bus_start, psize++, build_tr(psize)) ##1 (bus_active))[*1:$] ##1 (bus_inactive); endproperty cover property (p_burst_size); // Continued on next page
Appendix B, “Complete OVM/AVM Testbench Example”
283
Example B-4
Assertion-based IP tb_monitor module
function void build_tr(int psize); tb_transaction tr; tr = new(); if (bus_write) begin tr.set_write(); tr.data = bus_wdata; end else begin tr.set_read(); tr.data = bus_rdata; end tr.burst_count = psize; tr.addr = bus_addr; cov_ap.write(tr); endfunction initial begin bus_reset = 0; bus_inactive = 0; bus_start = 0; bus_active = 0; end endmodule
B.1.5 tb_env class The environment class is the topmost container of an AVM testbench. It contains all the class-based components that comprise the testbench and orchestrate test execution of the testbench.
284 Creating Assertion-Based IP
Example B-5
Testbench environment class
package tb_env_pkg; import avm_pkg::*; import tb_tr_pkg::*; import tb_transactor_pkg::*; class tb_env extends avm_env; protected protected protected protected
tb_stimulus tb_driver tb_responder tb_coverage
p_stimulus; p_driver; p_responder; p_coverage;
analysis_fifo #( tb_status ) p_status_af; avm_analysis_port #( tb_status ) p_status_ap; avm_analysis_port #( tb_transaction ) p_cov_ap; local process p_stimulus_process; virtual pins_if #( .DATA_SIZE(8), .ADDR_SIZE(8) ) p_nonpiped_bus; // Environment class constructor function new( virtual pins_if #(.DATA_SIZE(8),.ADDR_SIZE(8)) nonpiped_bus, avm_analysis_port #( tb_transaction ) cov_ap, avm_analysis_port #( tb_status ) status_ap ); p_nonpiped_bus = nonpiped_bus; p_cov_ap = cov_ap; p_status_ap = status_ap; p_driver = new(“driver ”, this); p_responder = new(“responder”, this); p_coverage = new(“coverage “, this); p_stimulus = new(“stimulus “, this); p_status_af = new(“status_fifo”); endfunction // Continued on next page
Appendix B, “Complete OVM/AVM Testbench Example”
285
Example B-5
Testbench environment class
function void connect; p_stimulus.blocking_put_port.connect ( p_driver.blocking_put_export ); p_driver.m_bus_if = p_nonpiped_bus; p_responder.p_bus_if = p_nonpiped_bus; p_cov_ap.register( p_coverage.analysis_export ); p_status_ap.register( p_status_af.analysis_export ); endfunction // Test Controller task run; fork begin p_stimulus_process = process::self; p_stimulus.generate_stimulus; end terminate_when_timedout; terminate_on_error; terminate_when_covered; join endtask task terminate_when_timedout; #200000; p_stimulus_process.kill; avm_report_message(“terminate_when_timedout” , “” ); $finish; endtask task terminate_on_error; string s; tb_status status; p_status_af.get( status ); p_stimulus_process.kill; $sformat (s, “%s”, status.convert2string); avm_report_error(“terminate_on_error” , s ); $finish; endtask // Continued on next page
286 Creating Assertion-Based IP
Example B-5
Testbench environment class
task terminate_when_covered; wait( p_coverage.p_is_covered ); p_coverage.report; p_stimulus_process.kill; avm_report_warning(“terminate_when_covered” , “” ); $finish; endtask endclass endpackage
B.1.6 tb_tr_pkg package The tb_tr_pkg contains the class definitions for coverage transactions and error status, which is referenced in Example B-5. . Module-based reference of an interface
Example B-6
tb_tr_pkg package
package tb_tr_pkg; import avm_pkg::*; ‘include “tb_transaction.svh” ‘include “tb_status.svh” endpackage
B.1.7 tb_transaction class The tb_transaction class defines the nonpipelined bus address and data for both stimulus generation and coverage detection.
Appendix B, “Complete OVM/AVM Testbench Example”
287
Example B-7
tb_transaction class
class tb_transaction extends avm_transaction; typedef enum {IDLE, WRITE, READ} bus_trans_t; rand rand rand rand
bus_trans_t bit[4:0] bit[7:0] bit[7:0]
bus_trans_type; burst_count; data; addr;
function avm_transaction clone(); tb_transaction t = new; t.copy( this ); return t; endfunction function void copy( data = addr = burst_count = bus_trans_type = endfunction
input tb_transaction t ); t.data; t.addr; t.burst_count; t.bus_trans_type;
function bit comp( input tb_transaction t ); avm_report_message( t.convert2string , convert2string ); return ((t.data == data) & (t.addr == addr) & (t.burst_count == burst_count) & (t.bus_trans_type == bus_trans_type)); endfunction function string bus_transaction_type; if (bus_trans_type == IDLE) return (“IDLE “); else if (bus_trans_type == WRITE) return (“WRITE”); else return (“READ “); endfunction function string convert2string; string s; $sformat( s , “Bus type = %s , data = %d , addr = %d” , bus_transaction_type(), data , addr); return s; endfunction function bit is_idle; return (bus_trans_type == IDLE); endfunction // Continued on next page
288 Creating Assertion-Based IP
Example B-7
tb_transaction class
function bit is_write; return (bus_trans_type == WRITE); endfunction function bit is_read; return (bus_trans_type == READ); endfunction function bit is_done; return (burst_count == 1); endfunction function void set_idle(); bus_trans_type = IDLE; return ; endfunction function void set_write(); bus_trans_type = WRITE; return ; endfunction function void set_read(); bus_trans_type = READ; return ; endfunction function void set_data( bit [7:0] D ); data = D; return ; endfunction function void set_addr( bit [7:0] A ); addr = A; return ; endfunction function bit[7:0] get_data; return data ; endfunction function bit[7:0] get_addr; return addr ; endfunction function bit[4:0] get_burst_count; return burst_count ; endfunction // Continued on next page
Appendix B, “Complete OVM/AVM Testbench Example”
289
Example B-7
tb_transaction class
function void decrement_burst_count; burst_count = burst_count - 1 ; return; endfunction endclass
B.1.8 tb_status class The
tb_status avm_analysis_port
class is broadcast through an to identify a specific nonpipelined bus
error. Example B-8
tb_status class
class tb_status extends avm_transaction; typedef enum { ERR_TRANS_RESET , ERR_TRANS_INACTIVE , ERR_TRANS_START , ERR_TRANS_ACTIVE , ERR_TRANS_ERROR , ERR_STABLE_SEL , ERR_STABLE_ADDR , ERR_STABLE_WRITE , ERR_STABLE_WDATA } bus_status_t; bus_status_t bus_status; function void set_err_trans_reset; bus_status = ERR_TRANS_RESET; endfunction function void set_err_trans_inactive; bus_status = ERR_TRANS_INACTIVE; endfunction function void set_err_trans_start; bus_status = ERR_TRANS_START; endfunction // Continued on next page
290 Creating Assertion-Based IP
Example B-8
tb_status class
function void set_err_trans_active; bus_status = ERR_TRANS_ACTIVE; endfunction function void set_err_trans_error; bus_status = ERR_TRANS_ERROR; endfunction function void set_err_stable_sel; bus_status = ERR_STABLE_SEL; endfunction function void set_err_stable_addr; bus_status = ERR_STABLE_ADDR; endfunction function void set_err_stable_write; bus_status = ERR_STABLE_WRITE; endfunction function void set_err_stable_wdata; bus_status = ERR_STABLE_WDATA; endfunction function avm_transaction clone(); tb_status t = new; t.copy( this ); return t; endfunction function void copy( input tb_status t ); bus_status = t.bus_status; endfunction function bit comp( input tb_status t ); avm_report_message( t.convert2string , convert2string ); return (t.bus_status == bus_status); endfunction function string convert2string; string s; $sformat( s , “Assertion error %s” , bus_status_type()); return s; endfunction function string bus_status_type; return bus_status.name(); endfunction endclass
Appendix B, “Complete OVM/AVM Testbench Example”
291
B.1.9 tb_transactor package The tb_transactor package, which is referenced in Example B-5, includes all the SystemVerilog class-based components. Example B-9
tb_transactor package
package tb_transactor_pkg; import avm_pkg::*; import tb_tr_pkg::*; ‘include ‘include ‘include ‘include
“tb_stimulus.svh” “tb_driver.svh” “tb_responder.svh” “tb_coverage.svh”
endpackage
B.1.10 tb_stimulus class The tb_stimulus class is responsible for generating random stimulus in our example testbench. Example B-10
tb_stimulus class
class tb_stimulus extends avm_random_stimulus #( tb_transaction ); function new( string name = ““ , avm_named_component parent = null ); super.new( name , parent ); endfunction task generate_stimulus( tb_transaction t = null , input int max_count = 0 ); tb_transaction m_temp; int burst_count = 0; if (t == null) t = new; for( int i = 0; (max_count == 0 || i < max_count); i++ ) begin // Continued on next page
292 Creating Assertion-Based IP
Example B-10
tb_stimulus class
// Get new random transaction type and burst count assert( t.randomize() ); burst_count = t.get_burst_count(); for ( int j = 1; j