www.dbebooks.com - Free Books & magazines
Creating Assertion-Based IP
Series on Integrated Circuits and Systems Serie...
17 downloads
628 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<=7; i++) begin for (j = 0; j<=7; j++) begin assert property (p_no_req_i_two_gnt_j(i,j)); end end end endgenerate
6.1.2 Specific arbiter properties In addition to the general fair arbitration assertion we previously discussed, there are often additional specific arbiter assertion properties that you will want to consider when creating your set of arbiter assertions [Dasgupta 2006]. For example: •
Is there a minimum time interval after a request when a grant must not occur due to latency considerations within the arbiter design?
•
Does the interface support queuing multiple requests on the same port while waiting for a grant?
This section illustrates a few specific arbiter interface properties through examples. Many of the properties we discuss in this section apply to all arbiter designs and Chapter 6, “Arbiters”
117
arbitration schemes (for example, mutually exclusive grants), while other properties are unique and specific to a particular arbiter implementation (for example, interface handshake properties). Although your specific arbiter properties may differ from the examples in this section, our intent is to provide a starting point with multiple examples that you can modify to fit your specific needs.
Mutually exclusive grants For our simple two-client fair arbiter (illustrated in Figure 62), it is a requirement that the arbiter’s grants remain mutually exclusive to prevent resource conflicts when accessing a shared resource. In other words, the arbiter should never assert gnt[0] and gnt[1] at the same time. Assertion 6-5 shows a SystemVerilog assertion for this requirement. Assertion 6-5
Mutually exclusive grants
property p_mutex_gnt; @(posedge clk) disable iff (rst) !(gnt[0] & gnt[1]); endproperty a_mutex_gnt: assert property (p_mutex_gnt);
Assertion 6-6 shows an alternative way to express this assertion using the SystemVerilog system function $onehot0(). The $onehot0() built-in function checks that the bits of gnt (gnt[0] and gnt[1]) are mutually exclusive and returns true when there is at most one bit with the value one. Otherwise, it returns false. In fact, for an arbiter that supports a large number of clients, using the $onehot0() system function simplifies the process of writing a mutually exclusive assertion.
118 Creating Assertion-Based IP
.
Assertion 6-6
Built-in function to check mutually exclusive grants
property p_mutex_gnt; @(posedge clk) disable iff (rst) $onehot0(gnt); endproperty a_mutex_gnt: assert property (p_mutex_gnt);
.Trace on which Assertion 4-7 does not hold
Figure 6-5 illustrates behavior on which Assertion 6-6 does not hold. Figure 6-5
Trace on which Assertion 6-6 does not hold 0
1
2
3
4
5
6
7
8
9
req[0] req[1] gnt[0] gnt[1]
Minimum latency For many arbiter designs, there is a minimum time interval between the point that a request is asserted and a grant is issued. This interval occurs due to latency considerations within the arbiter design. Obviously, specifying the minimum time requirement between a request and grant in SystemVerilog is design specific. For instance, we will assume for our simple example that there is a minimum latency of three cycles between a request and a grant. Hence, a request is identified by a rising transition of the req[0] signal. Assertion 6-7 expresses an assertion for this requirement.
Chapter 6, “Arbiters”
119
.
Assertion 6-7
Forbidden occurrences of grants due to latency
property p_minimum_latency @(posedge clk) disable iff (rst) $rose(req[0]) ##[0:2] gnt[0] |-> 0; endproperty a_minimum_latency: assert property (p_minimum_latency);
Assertion 6-7 specifies three arbiter forbidden sequences in which the grant must not be issued sooner than the latency through the arbiter (for this example, less than three cycles). Figure 6-6 illustrates traces where Assertion 6-7 does not hold. Figure 6-6.
Traces on which Assertion 6-7 does not hold
0
1
2
3
4
5
6
7
8
9
5
6
7
8
9
6
7
8
9
req[0] gnt[0] (i) 0
1
2
3
4
req[0] gnt[0] (ii) 0
1
2
3
4
5
req[0] gnt[0] (iii)
120 Creating Assertion-Based IP
Grant without pending request One fundamental requirement of an arbiter is that it should never issue a grant to any client that has not asserted a request. For this requirement, we need to consider the following cases: 1 A client has never asserted a request in the past (that is,
an initial condition after reset). 2 An arbitration cycle associated with a client has
completed in the past. We can express a SystemVerilog assertion for the initial condition case as shown in Assertion 6-8, which specifies that a !gnt[0] holds up to and including the cycle in which the first req[0] holds—since we are assuming for our example that there is at minimum a one-cycle latency between the time in which a request is asserted and a grant is issued. Assertion 6-8
Initial condition case for no grant prior to request
property p_init_no_gnt_before_req; @(posedge clk) $fell(rst) |-> (~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<=7; i++) begin for (j = 0; j<=7; j++) begin assert property (p_fixed_priority_i_j(i,j)); end end end endgenerate
124 Creating Assertion-Based IP
6.1.4 Credit-based weighted priority In the previous sections, we introduced two common static arbitration schemes (fair arbitration and fixed-priority arbitration). A fixed priority scheme suffers from the possibility of creating a starvation condition for a particular client. In contrast, a fair arbitration scheme’s problem area is in failing to use a shared resource by a high bandwidth client in an optimal manner. In this section, we introduce our first variable arbitration scheme that is based on assigning a “weight” to each client, which indicates its expected bandwidth when accessing a shared resource. The weight value is used as a bias during the arbitration cycle (where higher-biased clients ultimately obtain greater access to a shared resource). There are a number of ways to implement a weightedpriority scheme within a flow-control design [Kariniemi and Nurmi 2005]. One popular weighted priority arbitration implementation in a contemporary flow-control SoC design is to allocate a set of credits to each client, and then process the pending arbiter client’s request in a classic round-robin fashion. Credit registers associated with each client are initialized with a value that represents the number of credits available to that particular client. Weighting is accomplished by allocating more credits to a higher-priority client and fewer credits to a lower-priority client. When a client asserts a request to the arbiter, it can only be issued a grant if it has available credit. After a grant is issued, the credit register is decremented. When no credits remain (all clients’ credit registers are equal to zero), the credit registers are re-initialized and the arbitration process repeats. Figure 6-11 illustrates a simple four-client, weightedpriority arbiter. Each arbiter request input port has a credit register associated with it (for example, cr[0] for client 0). The arbiter evaluates the combination of pending requests and credit registers in a round-robin fashion (as indicated by the arrow). For our discussion, we assume there is a minimum latency through the arbiter of one cycle.
Chapter 6, “Arbiters”
125
Figure 6-11.
Four-client variable weighted priority arbiter
v
req[0]
gnt[0]
cr[0]
req[1]
cr[1]
req[2]
cr[2]
req[3]
cr[3]
Variable Weighted Priority Arbiter
gnt[1] gnt[2] gnt[3]
In general, a weighted priority arbiter has a requirement that a grant should never be issued for a client lacking sufficient credits (that is, the client’s credit register is equal to zero). Assertion 6-12 expresses this requirement for our four-client weighted priority arbiter. Assertion 6-12
No grant without credit
property p_no_gnt_without_credit(i); @(posedge ck) disable iff (rst) gnt[i] & (cr[i]==0) |-> 0; endproperty generate begin genvar i; for (i = 0; i<=7; i++) begin assert property (p_no_gnt_without_credit(i)); end end endgenerate
Figure 6-12.
Trace on which Assertion 6-12 holds
v
0
1
2
3
4
5
6
7
8
9
gnt[0] cr[0]
3
2
Figure 6-12 illustrates behavior on which Assertion 6-12 holds for client 0. Notice that client 0 has three credits available when a grant is issued. 126 Creating Assertion-Based IP
In Figure 6-12, the credit register (cr[0]) decrements after a grant is issued. This is a requirement of our implementation. Hence, we could express an assertion for this requirement as shown in Assertion 6-13. .
Assertion 6-13
Credit register decrements after grant
property p_credit_reg_dec(i); @(posedge ck) disable iff (rst) gnt[i] |=> (cr[i] == ($past(cr[i])-1)); endproperty generate begin genvar i; for (i = 0; i<=7; i++) begin assert property (p_credit_reg_dec(i)); end end endgenerate
Let us assume we have an eight-client arbiter. We can use Assertion 6-14 to express that a client with sufficient credit is not starved. Assertion 6-14
Eight-client weighted priority starvation assertion
property p_no_starvation(i,j); @(posedge ck) disable iff (rst) req[i] & (cr[i]!=0) ##1 (~gnt[i] throughout (gnt[j])[->2]) |-> 0; endproperty generate begin genvar i, j; for (i = 0; i<=7; i++) begin for (j = 0; j<=7; j++) begin assert property (p_no_starvation(i,j)); end end end endgenerate
Since our variable weighted priority arbiter is implemented using a round-robin scheme, you might have noticed that
Chapter 6, “Arbiters”
127
Assertion 6-14 is similar to Assertion 6-4, which checks for fair arbitration.
6.1.5 Dynamic priority Conventional static arbitration schemes, such as fixed priority and round-robin arbitration, exhibit a number of undesirable behaviors, such as starvation and low client-toresource bandwidth due to the impartiality (or latency) of the static arbitration scheme. In Section 6.1.4, “Credit-based weighted priority” we examined a variable arbitration scheme that was designed to address some of the undesirable behaviors of a static arbitration scheme. While credit-based weighted priority addresses the starvation problem, poor system performance may still occur for situations where the bandwidth requirement for a particular client changes from a default credit-based configuration. To solve this problem, some designs require the clients to adjust their priority dynamically based on the urgency of servicing a critical event (for example, a full FIFO buffering input transaction). Consequently, the priority of a particular client is increased dynamically to prevent data loss or corruption. In this section, we examine another variable priority scheme known as dynamic priority. There are a number of ways to implement a dynamicpriority scheme. One technique is to associate a single-bit priority register with each client, as Figure 6-13 illustrates. Figure 6-13.
Four-client variable priority
v
req[0] req[1]
pr[0] pr[1]
req[2] req[3]
pr[2] pr[3]
Low Priority
128 Creating Assertion-Based IP
Variable Dynamic Priority Arbiter High Priority
gnt[0] gnt[1] gnt[2] gnt[3]
These registers are initialized to zero (low priority). When a critical event occurs within the design, the client can set the priority register to one (high priority), and then the arbiter processes the pending client’s request in a classic roundrobin fashion. For example, if client 0 and client 2 decide to increase their priority from low to high (which we have illustrated by shifting their priority registers to the right in Figure 6-13), then these higher-priority requests must be serviced prior to servicing any of the lower-priority requests. In general, a variable-priority arbiter requires that a lowerpriority grant should never be issued to a client when a highpriority request is pending. We can express this requirement for our four-client, weighted-priority arbiter with Assertion 6-15. Assertion 6-15
Four-client dynamic priority
property p_no_lower_priority(i,j); @(posedge ck) disable iff (rst) (req[i] & (pr[i]==1) & req[j] & (pr[j]==0)) ##1 ((~gnt[i] & (pr[i]==1) & (pr[j]=0)) throughout (gnt[j])[->1]) |-> 0; endproperty generate begin genvar i, j; for (i = 0; i<=7; i++) begin for (j = 0; j<=7; j++) begin assert property (p_no_lower_priority(i,j)); end end end endgenerate
Chapter 6, “Arbiters”
129
Figure 6-14.
Trace on which Assertion 6-15 does not holds
v
0
1
2
3
4
5
6
7
8
9
req[0] req[1] pr[0] pr[1] gnt[0] gnt[1]
Figure 6-14 illustrates behavior on which Assertion 6-15 does not hold for client 0. Notice that client 0 has a higher priority when a grant is issued to a lower-priority client. We can write an assertion (similar to Assertion 6-4) that specifies that the arbiter services the high-priority request in a fair fashion by using the SystemVerilog generate construct as shown in Assertion 6-16. Assertion 6-16
Eight-client dynamic high-priority assertion
property p_fair_high_priority(i,j); @(posedge ck) disable iff (rst) (req[i] & (pr[i]==1) & (pr[j]==1)) ##1 (~gnt[i] & (pr[i]==1) & (pr[j]==1)) throughout (gnt[j])[->2]) |-> 0; endproperty generate begin genvar i, j; for (i = 0; i<=7; i++) begin for (j = 0; j<=7; j++) begin assert property (p_fair_high_priority(i,j)); end end end endgenerate
130 Creating Assertion-Based IP
.
Assertion 6-17
Eight-client dynamic low-priority assertion
property p_fair_low_priority(i,j); @(posedge ck) disable iff (rst) (req[i] & pr==8’b0) ##1 ((~gnt[i] & pr==8’b0) throughout (gnt[j])[->2]) |-> 0; endproperty generate begin genvar i, j; for (i = 0; i<=7; i++) begin for (j = 0; j<=7; j++) begin assert property (p_fair_low_priority(i,j)); end end end endgenerate
Similarly, Assertion 6-17 specifies that the lower-priority requests must be treated fairly. complex combined priority schemes
In fact, you might have noticed that we have partitioned the behaviors we are interested in by checking the following: 1 Higher-priority requests are serviced before lower-
priority requests. 2 Requests within the same grouping of priority are
serviced in a fair fashion. Many of today’s complex arbiters actually combine a number of different arbitration schemes. To simplify the task of writing assertions for these complex arbiters, we recommend that you partition the behaviors in a fashion similar to what we did for our dynamic arbiter (for example, separate the priority properties from the fairness properties and do not attempt to write a single complex property to check both schemes).
Chapter 6, “Arbiters”
131
6.1.6 Interleaving request Thus far, we have only considered arbiters that support noninterleaving requests. That is, a requesting client must wait until it completes a pending arbitration cycle before it can queue a new request. However, a number of designs, such as pipelined protocols, are optimized for greater throughput by allowing a limited number of outstanding transactions to begin prior to the completion of a previous transaction. Hence, supporting interleaving requests is necessary. Let us re-consider our simple, two-client, fixed-priority arbiter and assume that our arbiter is able to buffer up to four pending requests per client. If we attempt to use our original Assertion 6-10 for an arbiter supporting interleaving requests, we run into a situation where multiple requests are satisfied by a single grant, as illustrated in Figure 6-15, which is not what we want. Figure 6-15.
A trace on which Assertion 6-10 holds erroneously
v
0
1
2
3
4
5
6
7
8
9
req[0]
gnt[0]
As you can see, if we use Assertion 6-10, then we run into a problematic situation where the grant issued at clock five matches both the clock two and clock three requests. To solve this problem, we introduce a variable in the modeling layer to uniquely tag the appropriate request-grant pairs, as Assertion 6-18 demonstrates.
132 Creating Assertion-Based IP
Assertion 6-18
fixed priority assertion for pipelined request
// Modeling code to keep track of req-gnt pairs // Set up variables to tag req's & gnt's. reg [0:1] req_0_cnt, gnt_0_cnt; initial {req_0_cnt, gnt_0_cnt} = 4'b0; always @(posedge clk) begin // Increment counter when event is seen. if (req[0]) req_0_cnt <= req_0_cnt + 1; if (gnt[0]) gnt_0_cnt <= gnt_0_cnt + 1; end // Fixed priority for 4 pending requests // For each uniquely tagged request // minimum arbiter request latency of 3 property p_pipelined_req_gnt; reg [0:1] v; @(posedge clk) disable iff (rst) (req[0], v=req_0_cnt) ##3 (~(gnt[0] & gnt_0_cnt == v) throughout (gnt[1])[->1]) |-> 0; endproperty a_pipelined_req_gnt: assert property (p_pipelined_req_gnt);
For pipelined request-grant pairs, it becomes necessary to consider the arbiter's latency when writing your assertions. For example, consider the trace for a level-sensitive design shown in Figure 6-16, where the lone req[0] is asserted at clock one, and the corresponding gnt[0] is issued at clock four due to a latency of three cycles through the arbiter. Without considering arbiter latency, it would appear that the fixed priority for the second req[0] asserted at clock two is violated at clock four. If we account for the arbiter's threecycle latency, then this is a valid trace. We have specified this behavior in the assertion of Figure 6-16 by adding a three-cycle delay between the occurrence of req[0] and the sequence in which gnt[0] occurs.
Chapter 6, “Arbiters”
133
Figure 6-16.
Minimum latency needs to be considered
v
0
1
2
3
4
5
6
7
8
9
req[0]
gnt[0]
6.2 Creating an arbiter assertion-based IP This section demonstrates how to create an arbiter assertionbased IP verification component for a non-interleaving, eight-client fair arbiter with a latency of three cycles by: •
Defining interfaces
•
Grouping all the required fair arbiter assertions into a module
•
Adding an error status analysis port
6.2.1 Block diagram interface description Figure 6-17 illustrates a block diagram for a simple eightclient fair arbiter design. For our example, all signal transitions relate only to the rising edge of the bus clock (clk). Table 6-1 provides a summary of the bus signals for our simple arbiter example.
134 Creating Assertion-Based IP
Figure 6-17.
Eight-client fair arbiter
req[0]
gnt[0]
req[1]
gnt[1]
req[2]
gnt[2]
req[3]
gnt[3]
req[4] req[5]
gnt[4]
Eight-Client Fair Arbiter
gnt[5]
req[6]
gnt[6]
req[7]
gnt[7]
clk rst
Table 6-1 Simple eight-client fair arbiter signal description Name
Description
clk
All bus transfers occur on the rising edge of clk An active high bus reset Client request signals Arbiter grant signals
rst req[0:7] gnt[0:7]
6.2.2 Overview description For our simple arbiter example, to be fair, a pending request for a particular client should never have to wait more than eight arbitration cycles before it is serviced. Otherwise, the alternate client must have been unfairly issued a grant multiple times. There is a minimum time latency through the arbiter of three clocks between the point that a request is asserted and a grant is issued.
Chapter 6, “Arbiters”
135
6.2.3 Natural language properties Prior to creating a set of SystemVerilog interface assertions for our simple eight-client fair arbiter, we must first identify a comprehensive list of natural language properties, as shown in Table 6-2: Table 6-2 Simple eight-client fair arbiter properties Property name
Summary
Bus legal state p_no_req_i_two_gnt_j
Arbiter is fair
p_mutex_gnt
No more than one grant asserted at a time
p_minimum_latency
No grant within three cycles of a request
p_no_gnt_before_req
No grant before a request
6.2.4 Assertions To create a set of SystemVerilog assertions for our simple eight-client fair arbiter, 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 6-18 illustrates. Example 6-1 on page 137 demonstrated an interface for our arbiter example. For this case, our assertion-based monitor references the interface signals via the direction defined by the monitor_mp named modport.1
1. For the example in Figure 6-18, the parent module would pass in the appropriate SystemVerilog modport (versus the SystemVerilog interface) to each specific CPU instance to create the unique connection. 136 Creating Assertion-Based IP
Figure 6-18
SystemVerilog interface req[0] gnt[0] ....
CPU 0
Arbiter
req[7] CPU 7
gnt[7] ...
Assertion-Based Monitor
.
Example 6-1
Encapsulate bus signals inside an interface
interface tb_8_client_fair_arbiter_if( input clk , input rst ); parameter int NUM_CLIENTS = 8; bit [0:NUM_CLIENTS-1] req; bit [0:NUM_CLIENTS-1] gnt; modport cpu_0_mp ( input clk , rst , input gnt[0] , output req[0] ); . . . modport cpu_7_mp ( input clk , rst , input gnt[7] , output req[7] ); modport monitor_mp ( input clk , rst , input gnt , input req ); endinterface
Chapter 6, “Arbiters”
137
In addition to pin-level interfaces (which monitor the bus) we need a means for our eight-client fair arbiter assertionbased monitor to communicate with various analysis verification components within the testbench. Hence, we introduce an error status analysis port within our monitor, as Figure 6-19 illustrates. Figure 6-19
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 bus error, the analysis port broadcasts the error condition (using an error status transaction object) to other verification components. The error status transaction class is defined in Example 6-2, which is an extension on an ovm_transaction base class. The tb_status_arb class includes an enum that identifies the various types of arbiter errors, and a set of methods to uniquely identify the specific error it detected.
138 Creating Assertion-Based IP
Example 6-2
Error status class
class tb_status_arb extends ovm_transaction; typedef enum { ERR_FAIR , ERR_MUTEX_GNT , ERR_MINIMUM_LATENCY , ERR_INIT_NO_GNT_REQ , ERR_NO_GNT_BEFORE_REQ } arb_status_t; arb_status_t arb_status; int err_client_num; function void set_err_fair (input int i); arb_status = ERR_FAIR ; err_client_num = i; endfunction function void set_err_mutex_gnt; arb_status = ERR_MUTEX_GNT ; endfunction function void set_err_minimum_latency(input int i); arb_status = ERR_MINIMUM_LATENCY; err_client_num = i; endfunction function void set_err_init_no_gnt_req(input int i); arb_status = ERR_INIT_NO_GNT_REQ; err_client_num = i; endfunction function void set_err_no_gnt_before_req(input int i); arb_status = ERR_NO_GNT_BEFORE_REQ; err_client_num = i; endfunction . . . endclass
We are now ready to write assertions for our bus interface properties defined in Table 6-2.
Chapter 6, “Arbiters”
139
Example 6-3
Modeling to map control signals to conceptual states
bit [0:7] gnt = monitor_mp.gnt; bit [0:7] req = monitor_mp.req; // Fair arbiter (minimum 3 cycle latency between req and gnt) property p_no_req_i_two_gnt_j (i,j); @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) req[i] ##3 (~gnt[i] throughout (gnt[j])[->2]) |-> 0; endproperty generate begin genvar i, j; for (i = 0; i<=7; i++) begin for (j = 0; j<=7; j++) begin assert property (p_no_req_i_two_gnt_j(i,j)) else begin status = new(); status.set_err_fair(i,j); status_ap.write(status); end end end end endgenerate // mutually exclusive grants property p_mutex_gnt; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) $onehot0(gnt); endproperty a_mutex_gnt: assert property (p_mutex_gnt) else begin status = new(); status.set_err_mutex_gnt(); status_ap.write(status); end // minimal latency property p_minimum_latency(i); @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) $rose(req[i]) ##[0:2] gnt[i] |-> 0; endproperty // Continued on next page
140 Creating Assertion-Based IP
Example 6-3
Modeling to map control signals to conceptual states
generate begin genvar i, j; for (i = 0; i<=7; i++) begin assert property (p_minimum_latency(i)) else begin status = new(); status.set_err_minimum_latency(i); status_ap.write(status); end end end endgenerate // grant without pending request property p_init_no_gnt_before_req(i); @(posedge monitor_mp.clk) $fell(monitor_mp.rst) |-> (~gnt[i] throughout (req[i])[->1]); endproperty property p_no_gnt_before_req(i); @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) gnt[0] |=> (~gnt[i] throughout (req[i])[->1]); endproperty generate begin genvar i, j; for (i = 0; i<=7; i++) begin assert property (p_init_no_gnt_before_req(i)) else begin status = new(); status.set_err_init_no_gnt_before_req(i); status_ap.write(status); end; assert property (p_no_gnt_before_req(i)) else begin status = new(); status.set_err_no_gnt_before_req(i); status_ap.write(status); end end end endgenerate
6.2.5 Encapsulate properties In this step, we turn our set of related arbiter assertions (and any coverage properties) into a reusable assertion-based Chapter 6, “Arbiters”
141
monitor verification component. We add analysis ports to our monitor for communication with other simulation verification components as Chapter 3, “The Process” demonstrates. Example 6-4
Encapsulate properties inside a module
import ovm_pkg::*; import tb_tr_pkg::*; // tb_status class definition module tb_8_client_fair_arbiter_mon ( interface.monitor_mp monitor_mp ); ovm_analysis_port #(tb_status_arb) status_ap = new(“status_ap”, null); ovm_analysis_port #(tb_coverage) coverage_ap = new(“coverage_ap”, null); tb_status_arb status; . . . // Any required modeling code here (to model environment) . . . // All assertions and coverage properties here . . . endmodule
6.3 Summary Understanding how to create assertion-based IP for an arbiter, which easily integrates with other testbench components, is an essential topic of discussion. In this chapter, we reviewed various common arbitration schemes and their associated properties. We then demonstrated the process of creating assertion-based IP for a simple eightclient fair arbiter. A common mistake assertion-based IP creators make when attempting to write properties for various arbitration schemes is that they try to specify the behavior of all inputs with respect to all outputs at the same time. This is obviously overwhelming and complex. In this chapter, we demonstrated that formal properties can be written for 142 Creating Assertion-Based IP
various arbitration schemes by only considering the behavior of pairs of clients. Another common mistake assertion-based IP creators make when attempting to write a formal property for complex nested arbitration schemes is that they try to write a single assertion that captures the behavior for the combined nested scheme. A better approach is to consider partitioning the nested arbitration schemes and writing a simpler set of assertions for each separate arbitration scheme. Finally, when creating parameterized assertion-based IP for arbiters, it is critical to accommodate many different arbiter interface, latency, and size requirements.
Chapter 6, “Arbiters”
143
C
H A P T E R
CONTROLLERS
CHAPTER 7
While busses serve as the framework for today’s platformbased 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) to very complex controllers (such as the memory controller illustrated in the bus-based design example in Figure 7-1). Figure 7-1.
Controller example within a bus-based design
CPU
UART
Pipelined Bus
Nonpipelined Bus Bridge
Arbiter
Memory Controller
Serial Bus Bridge
Timer
This chapter demonstrates our process of creating assertionbased IP for a simple memory controller. We ask you to focus on the techniques for creating the assertion-based IP. By using our generic, simple memory-controller design, we hope that you will be able to set aside the details of a specific type of memory controller design that could Chapter 7, “Controllers”
145
otherwise overwhelm you and distract you from the learning objectives. After learning the process, you should be able to extend these ideas to create assertion-based verification IP for your own specific memory controller. 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.
7.1 Simple generic memory controller Specifications for commercially available memories, such as an SDR or DDR SDRAM, generally describe the required behavior for the memory’s controller in terms of a conceptual state-machine that defines the legal sequences of memory commands. In Chapter 5, “Interfaces,” we demonstrated the process of creating a conceptual statemachine to describe legal bus operations. Developing a conceptual state-machine for a memory controller follows a similar process and is fairly straight forward. This process consists of the following steps: 1 Define the legal memory controller interface
commands 2 Map conceptual states of the memory controller to
specific output commands 3 Define the legal command sequences (that is,
transitions for the conceptual state-machine) This section introduces a simple memory controller to demonstrate the process of creating assertion-based IP.
146 Creating Assertion-Based IP
7.1.1 Block diagram interface description Figure 7-2 illustrates a block diagram for a system containing our simple memory controller. Our simple memory controller is designed to support all transactions for a generic SDRAM, with the goal of minimizing memory wait states. Figure 7-2 s
Block diagram for a memory controller system
C
RESET# CLKIN ADS# Processor
BLAST# Memory Controller
W_R# BE[3:0] READY# A[31:2] TIMER
MA[9:0]
MA[10]
MA[11]
Data Buffer
DQM[3:0]
WE#
CAS#
RAS#
CS#
D[31:0]
CLKIN D[31:0]
SDRAM
Table 7-1 provides a summary of the signals for the memory controller example. Table 7-1 Memory controller signal description Name
Connection
Description
CLKIN
Processor to Controller
External clock
A[31:2]
Processor to Controller
Address
W_R#
Processor to Controller
Write/read strobe Chapter 7, “Controllers”
147
Name
Connection
Description
BLAST#
Processor to Controller
Last transfer of a burst
ADS#
Processor to Controller
Address strobe
BE(3:0)
Processor to Controller
Byte enables decoded from A[2:0]
RESET#
Processor to Controller
System reset
TIMER#
Timer to Controller
Refresh timeout
READY#
Controller to Processor
Data valid
RAS#
Controller to SDRAM
Row address strobe
CAS#
Controller to SDRAM
Column address strobe
WE#
Controller to SDRAM
Write enable
CS#
Controller to SDRAM
Chip select
MA[11:0]
Controller to SDRAM
Multiplexed row/col address lines
DQM[3:0]#
Controller to SDRAM
Data output mask lines
D[31:0]
SDRAM to/from Processor
Data
7.1.2 Overview description We begin by introducing the basic operations supported by our simple memory controller, which consists of the following transactions: •
Single Read Cycle
•
Burst Read Cycle
•
Single Write Cycle
•
Burst Write Cycle
Most vendors provide an SDRAM specification that contains an interface command truth table and state transition table, which describes the normal operation of the SDRAM. Assume, for our example, that the SDRAM interface command truth table is defined as shown in 148 Creating Assertion-Based IP
Table 7-2, which is a mapping of output controls to command names.1 To simplify the example, and without loss of generality, we have abstracted the details for the DQM lines, the power on initialization sequence, the mode register set (MRS) sequence, and other potential states generally used to describe specific details related to memory access timing. After understanding this example, you should be able to extend the process ideas we present in this section to your design to include additional features. Table 7-2 Memory controller to SDRAM command table Name
CS#
RAS#
CAS#
WE#
Operation
O_DSEL
1
X
X
X
General deselect
O_IDLE
1
1
1
1
Idle
O_NOP
0
1
1
1
No operation
O_READ
0
1
0
1
Read operation
O_WRITE
0
1
0
0
Write operation
O_ACT
0
0
1
1
Row activating
O_PRE
0
0
1
0
Precharge
O_REF
0
0
0
1
Refresh
In Table 7-3 we list the processor to memory controller commands (that is, a mapping of input controls to command names). Table 7-3 Processor to memory controller command table Name
ADS#
W_R#
Operation
I_RD_INIT
0
0
Initiate read
I_WR_INIT
0
1
Initiate write
I_READ
X
0
Continue read
I_WRITE
X
1
Continue write
I_IDLE
1
X
Idle
1. Each vendor’s SDRAM specification is unique. However, you will find that each vendor provides a truth table describing the SDRAM interface commands, similar to our generic example. Chapter 7, “Controllers”
149
Contained within our simple memory controller is an eleven-bit page detection tag register, which is used to determine a memory address page hit for either a read or write transaction. For this example, upon detecting a page hit, we assume that the controller’s internal HIT# signal is asserted. In addition, we assume that our design has an internal signal DONE#, which indicates the completion of the memory transaction. DONE# is asserted low in the same cycle as BLAST#, and it remains asserted low until the cycle after READY# is asserted low. Finally, when the refresh timer times out (TIMER asserted for a single clock cycle), the controller’s internal TIM# is asserted low until the refresh is serviced (an O_REF command is issued to initiate a refresh). In general, you might be tempted to exactly replicate this logic in your verification IP (to eliminate the need to reference any internal state of the memory controller). However, this approach misses an important point of functional verification: the source of design intent must be independently referenced and implemented by the verification environment to serve as a golden reference. An alternative approach would be to interpret the spec and separately implement logic that identifies a hit, done, blast, and timeout condition. Due to space limitations, we have decided to reference these three internal signals to simplify our example. Table 7-4 defines our memory controller legal state transitions. Table 7-4 Memory controller conceptual state transitions R E A D Y #
Current State
Input Memory Controller Command (see Table 7-3)
D H O I N T E # #
IDLE
X
X X 0
REF
1 O_NOP
IDLE
I_IDLE
X X 1
IDLE
0 O_IDLE
IDLE
I_WR_INIT
0 1 X WRITE
0 O_WRITE
IDLE
I_WR_INIT
1 X X ACT
1 O_ACT
150 Creating Assertion-Based IP
T I M Next # State
Output SDRAM Command (see Table 7-2)
R E A D Y #
Current State
Input Memory Controller Command (see Table 7-3)
D H O I N T E # #
IDLE
I_RD_INIT
0 1 X READ
0 O_READ
IDLE
I_RD_INIT
1 X X ACT
1 O_ACT
WRITE
I_WRITE
0 1 X WRITE
0 O_WRITE
WRITE
I_WRITE
1 X X ACT
1 O_ACT
WRITE
I_WRITE
0 0 X PRE
1 O_PRE
READ
I_READ
0 1 X READ
0 O_READ
READ
I_READ
1 X X ACT
0 O_ACT
READ
I_READ
0 0 X PRE
1 O_PRE
REF
X
X X X IDLE
1 O_DSEL
PRE
I_IDLE
X 1 X IDLE
0 O_IDLE
ACT
I_WRITE
0 X X WRITE
0 O_WRITE
ACT
I_READ
0 X X READ
0 O_READ
T I M Next # State
Output SDRAM Command (see Table 7-2)
The state diagram for our SDRAM memory sequences is shown in Figure 7-3 on page 152. All memory sequences are initiated when ADS# is asserted low, which is sampled on the rising edge of CLKIN. When the RESET# signal is asserted low (during power-on), the processor is in its reset state. The SDRAM controller issues a precharge command (O_PRE) to all banks, eight auto-refresh commands (O_REF), and a mode register set command. When RESET# is released, the controller is in its IDLE state waiting for the processor to initiate a memory transaction.
Chapter 7, “Controllers”
151
Figure 7-3
Memory controller conceptual state-machine
IDLE MRS
INIT REFRESH
REF
INIT PRE
ACT
WRITE
READ
POWER ON PRE
To simplify the discussion, we have decided to ignore the details for the power-on sequence (which is illustrated by the grey circles in Figure 7-3), as well as the configuration details of the memory controller mode register. In addition, we ignore all timing and latency considerations and assume all transitions complete in a cycle. For a real memory controller, you will need to consider these additional details, which are unique to a particular vendor’s SDRAM, when creating your memory controller conceptual state-machine. Our example SDRAM control sequences are defined as follows: •
Refresh sequence. The refresh sequence is initiated when the external timer pulses high every 32 ms. Any memory cycles that are in progress will be completed prior to beginning the refresh sequence.
•
Idle state. When no read, write, or refresh transaction is in progress, the memory controller remains in an idle state.
152 Creating Assertion-Based IP
Figure 7-4
Single read hit sequence
s
0
1
2
3
BE[3:0]
BYTE
A[31:2]
ADDRESS
4
5
6
7
8
ENABLE
ADS# BLAST# W_R# CS# MA[11]
BS
MA[10] MA[9:0]
COL
WE# RAS# CAS# DATA[31;0]
DATA
READY# STATE
IDLE
IDLE
READ
PRE
IDLE
IDLE
IDLE
IDLE
IDLE
•
Single read hit sequence. When the processor issues an I_RD_INIT command (see Table 7-3), and HIT# is asserted low (the internal page tag register for the bank currently accessed matches the requested row address), a single read hit sequence begins as shown in Figure 74. First, an O_READ command (see Table 7-2) is issued from the memory controller to the SDRAM. On the following cycle, the SDRAM places the data on the bus, and the memory controller alerts the processor by asserting READY#. The memory controller terminates the transaction on the following cycle by issuing a O_PRE precharge command to the SDRAM.
•
Single read miss sequence. When the processor issues an I_RD_INIT command (see Table 7-3), and HIT# is deasserted (the internal page tag register for the bank currently accessed does not match the requested row address), a single read miss sequence begins as shown in Figure 7-5. The memory controller de-asserts the READY# signal to pause the processor until the data is valid. Then the memory controller issues an O_ACT Chapter 7, “Controllers”
153
command (see Table 7-2) to the SDRAM. Next, an O_READ command is issued from the memory controller to the SDRAM. On the following cycle, the SDRAM places the data on the bus, and the memory controller alerts the processor by asserting READY# low. The memory controller terminates the transaction on the following cycle by issuing an O_PRE precharge command to the SDRAM. Figure 7-5
Single read miss sequence
s
0
1
2
3
4
BE[3:0]
BYTE
A[31:2]
ADDRESS
5
6
7
8
ENABLE
ADS# BLAST# W_R# CS# MA[11]
BS
MA[10] MA[9:0]
ROW
COL
WE# RAS# CAS# DATA[31;0]
DATA
READY# STATE
IDLE
•
IDLE
ACT
READ
PRE
IDLE
IDLE
IDLE
IDL
Single write hit sequence. When the processor issues an I_WR_INIT command (see Table 7-3), and HIT# is asserted low (the internal page tag register for the bank currently accessed matches the requested row address), a single write hit sequence begins as shown in Figure 76. An O_WRITE command (see Table 7-2) is issued from the memory controller to the SDRAM, while the processor places the data on the bus. The memory controller asserts READY# low, indicating that the SDRAM is ready. Then, the memory controller terminates the transaction on the following cycle by issuing an O_PRE precharge command to the SDRAM.
154 Creating Assertion-Based IP
Figure 7-6
Single write hit sequence
s
0
1
2
3
4
BE[3:0]
BYTE
A[31:2]
ADDRESS
5
6
7
8
ENABLE
ADS# BLAST# W_R# CS# MA[11]
BS
MA[10] MA[9:0]
COL
WE# RAS# CAS# DATA
DATA[31:0] READY# STATE
IDLE
•
IDLE
ACT
WRITE
PRE
IDLE
IDLE
IDLE
Single write miss sequence. When the processor issues an I_WR_INIT command (see Table 7-3), and HIT# is deasserted (the internal page tag register for the bank being accessed does not match the requested row address), a single write miss sequence begins as shown in Figure 7-7. The memory controller de-asserts the READY# signal to pause the processor until the data is valid. Then the memory controller issues an O_ACT command (see Table 7-2) to the SDRAM. Next, an O_WRITE command is issued from the memory controller to the SDRAM, while the processor places the data on the bus. The memory controller asserts READY# low indicating that the SDRAM is ready. Then, the memory controller terminates the transaction on the following cycle by issuing a O_PRE precharge command to the SDRAM.
Chapter 7, “Controllers”
155
Figure 7-7
Single write miss sequence 0
1
2
3
4
BE[3:0]
BYTE
A[31:2]
ADDRESS
5
6
7
8
ENABLE
ADS# BLAST# W_R# CS# MA[11]
BS
MA[10] MA[9:0]
ROW
COL
WE# RAS# CAS# DATA
DATA[31:0] READY# STATE
IDLE
•
IDLE
ACT
READ
PRE
IDLE
IDLE
IDLE
Burst read sequence. When the processor asserts an I_RD_INIT command (see Table 7-3), a burst read sequence begins as shown in Figure 7-8.2 The memory controller de-asserts the READY# signal to pause the processor until the data is valid. Then, the memory controller issues an O_ACT command (see Table 7-2) to the SDRAM. Next, an O_READ command is issued from the memory controller to the SDRAM. On the following cycle, the SDRAM places the first beat of data on the bus, and the memory controller alerts the processor by asserting READY# low. The last beat of data is identified by the processor asserting BLAST# low with a requesting address to the memory controller. After the final beat of data is read, memory controller terminates the
2. We have simplified the read and write burst sequences for our memory controller example by always forcing row-activating command (O_ACT) prior to starting the memory burst. If required, it is straightforward to modify our example to handle a page hit or miss case, like we did for a single cycle read or write. 156 Creating Assertion-Based IP
transaction by issuing an O_PRE precharge command to the SDRAM. Figure 7-8
Burst read sequence
s
0
1
2
3
4
BE[3:0]
BYTE
A[31:2]
ADDRESS
5
6
7
8
ENABLE A1
A2
A3
DATA
D1
D2
READ
READ
ADS# BLAST# W_R# CS# MA[11]
BS
MA[10] MA[9:0]
ROW
COL
WE# RAS# CAS# DATA[31:0]
D3
READY# STATE
IDLE
•
IDLE
ACT
READ
READ
PRE
IDLE
Burst write sequence. When the processor asserts an I_WR_INIT command (see Table 7-3), a burst write sequence begins as shown in Figure 7-9. The memory controller de-asserts the READY# signal to pause the processor until the data is valid. Then, the memory controller issues an O_ACT command (see Table 7-2) to the SDRAM. Next, an O_READ command is issued from the memory controller to the SDRAM. The processor places the first beat of data on the bus, and the memory controller alerts the processor that the SDRAM is ready by asserting READY# low. The last beat of data is identified by the processor asserting BLAST# low with a requesting address to the memory controller. After the final beat of data is written, memory controller terminates the transaction by issuing an O_PRE precharge command to the SDRAM.
Chapter 7, “Controllers”
157
Figure 7-9
Burst write sequence 0
1
2
3
4
BE[3:0]
BYTE
A[31:2]
ADDRESS
5
6
7
8
ENABLE A1
A2
A3
D1
D2
D3
ADS# BLAST# W_R# CS# MA[11]
BS
MA[10] MA[9:0]
ROW
COL
WE# RAS# CAS# DATA
DATA[31:0] READY# STATE
IDLE
IDLE
ACT
WRITE WRITE WRITE WRITE
PRE
IDLE
7.1.3 Natural language properties Before we write our SystemVerilog assertion, our next step is to create a natural language list of properties for our simplified memory controller, as shown in Table 7-5. In terms of creating this organizing table, we recommend listing properties associated with conceptual state transitions followed by specific properties associated with individual output pins.
158 Creating Assertion-Based IP
Table 7-5 Memory controller properties Assertion Name
Summary
Valid reset transition a_reset_idle_trans The initial state after reset is IDLE Valid idle transitions a_idle_refresh_trans REFRESH state follows IDLE when TIM# is asserted low a_idle_idle_trans
IDLE
a_idle_write_trans
WRITE
a_idle_read_trans
READ state follows IDLE for an I_RD_INIT command, and a page hit occurs (HIT# asserted low)
a_idle_act_trans
ACT state follow IDLE for either an I_RD_INIT I_WR_INIT command and a page miss occurs (HIT# de-asserted high)
state must follow IDLE when TIM# is deasserted and no I_RD_INIT or I_WR_INIT command was received state follows IDLE for an I_WR_INIT command, and a page hit occurs (HIT# asserted)
or
Valid refresh transition a_ refresh_idle_trans IDLE state follows a REFRESH state Valid write transitions a_write_write_trans WRITE state follows WRITE state when DONE# is deasserted, and a page hit occurs (HIT# asserted low) a_write_act_trans
ACT
a_write_pre_trans
PRE
state follows a WRITE state when DONE# is deasserted, and a page miss occurs (HIT# asserted high) state follows a WRITE state when DONE# is asserted low
Valid read transitions a_read_read_trans READ state follows READ state when DONE# is deasserted, and a page hit occurs (HIT# asserted low)
Chapter 7, “Controllers”
159
Assertion Name
Summary
a_read_act_trans
ACT state
a_read_pre_trans
PRE state
follows a READ state when DONE# is deasserted, and a page miss occurs (HIT# asserted high) follows a READ state when DONE# is
asserted Valid row activating transitions a_act_write_trans WRITE state follows ACT when W_R# is de-asserted a_act_read_trans
READ
state follows ACT when W_R# is asserted
Valid precharge transition a_pre_idle_trans IDLE state follows PRE state Processor to memory controller address strobe a_ADS_valid After ADS# is asserted for one cycle, it must not be asserted again until after the completion of the current memory transaction (that is, state IDLE) Processor to memory controller last transfer strobe a_BLAST_valid Once BLAST# is asserted low for one cycle, it cannot be asserted low again until after an ADS# is asserted Processor to memory controller write / read signal a_W_R_stable W_R# must remain stable throughout a memory transaction Processor to memory controller address signals a_A_stable Address must not change values during the ACT state Processor to memory controller byte enable signals a_BE_stable Byte enable signals must not change values during the ACT state Memory controller to processor ready signal a_READY_low READY# must only be asserted low during a READ or WRITE state a_READY_high
READY# must be or WRITE state
160 Creating Assertion-Based IP
asserted high when not in a READ
Assertion Name
Summary
Memory controller to SDRAM valid commands a_valid_commands Only valid commands issued from controller
7.1.4 Assertions To create a set of SystemVerilog assertions for our simple memory controller, we begin by defining the connection between the assertion-based monitor and other potential components in the testbench (such as a driver transactor and the DUV). To accomplish this task, we create a SystemVerilog interface, as illustrated in Figure 7-10. Figure 7-10
SystemVerilog interface
Driver
DUV
AssertionBased Monitor
Example 7-1 demonstrates an interface for our simple memory controller example. For this case, the assertionbased monitor references the interface signals in the direction defined by the monitor_mp named modport.
Chapter 7, “Controllers”
161
.
Example 7-1
Encapsulate bus signals inside a SV interface
interface tb_mem_ctrl_if( input clk , input rst ); parameter parameter parameter parameter parameter bit bit bit bit bit bit
int int int int int
DATA_SIZE = 32; ADDR_SIZE = 32; BE_SIZE = 4; MA_SIZE = 12; DQM_SIZE = 4;
[ADDR_SIZE-1:2] a; w_r_n; blast_n; ads_n; [BE_SIZE-1:0] be; ready_n;
// // // // // //
A W_R# BLAST# ADS# BE READY#
bit tim_n; bit hit_n; bit done_n;
// TIM# // HIT# // DONE#
bit bit bit bit bit bit bit
// // // // // // //
ras_n; cas_n; we_n; cs_n; [MA_SIZE-1:0] ma; [DQM_SIZE-1:0] dqm_n; [DATA_SIZE-1:0] d;
RAS# CAS# WE# CS# MA DQM# D
modport driver_mp ( . . . . ); modport duv_mp ( . . . . ); modport monitor_mp ( input clk , rst , input a , w_r_n, blast_n , ads_n , be , ready_n , input tim_n , hit_n , done_n , input ras_n , cas_n , we_n , cs_n , ma , dqm_n , d ); endinterface
In addition to pin-level interfaces, we need a means for the memory controller assertion-based monitor to communicate with various analysis verification components within the testbench. Hence, we introduce error status and coverage analysis ports within the monitor, as Figure 7-11 illustrates.
162 Creating Assertion-Based IP
Figure 7-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 7-2 defines the error status transaction class, which is an extension on an ovm_transaction base class. The tb_status class includes an enum that identifies the various types of memory controller errors and a set of methods to uniquely identify the specific error it detected. Example 7-2
Error status class
class tb_status_mc extends ovm_transaction; typedef enum { ERR_RESET_IDLE_TRANS , ERR_IDLE_REFRESH_TRANS , ERR_IDLE_IDLE_TRANS , ERR_IDLE_WRITE_TRANS , ERR_IDLE_READ_TRANS , ERR_IDLE_ACT_TRANS , ERR_ REFRESH_IDLE_TRANS , ERR_WRITE_WRITE_TRANS , ERR_WRITE_ACT_TRANS , ERR_WRITE_PRE_TRANS , ERR_READ_READ_TRANS , ERR_READ_ACT_TRANS , ERR_READ_PRE_TRANS , ERR_ACT_WRITE_TRANS , ERR_ACT_READ_TRANS , ERR_PRE_IDLE_TRANS , // Continued on next page
Chapter 7, “Controllers”
163
Example 7-2
Error status class ERR_ADS_VALID , ERR_BLAST_VALID , ERR_W_R_STABLE , ERR_ERR_STABLE , ERR_BE_STABLE , ERR_READY_LOW , ERR_READY_HIGH , ERR_VALID_COMMANDS } mc_status_t;
mc_status_t
mc_status;
function void set_err_reset_idle_trans; mc_status = ERR_RESET_IDLE_TRANS; endfunction function void set_err_idle_refresh_trans; mc_status = ERR_IDLE_REFRESH_TRANS; endfunction function void set_err_idle_idle_trans; mc_status = ERR_IDLE_IDLE_TRANS; endfunction function void set_err_idle_write_trans; mc_status = ERR_IDLE_WRITE_TRANS; endfunction . . . . function void set_err_valid_commands; mc_status = ERR_VALID_COMMANDS; endfunction . . . endclass
To simplify writing the assertion (and to increase the clarity), we create some modeling code (see Example 7-3) to map the interface signals defined in Table 7-2 and Table 7-3 (see page 149) into SDRAM and memory controller commands. Example 7-3
Modeling to map control signals to conceptual states
module tb_mem_cntrl_mon( interface.monitor_mp monitor_mp ); ovm_analysis_port #(tb_status) status_ap = new(“status_ap”, null); // Continued on next page
164 Creating Assertion-Based IP
Example 7-3
Modeling to map control signals to conceptual states
. . . bit bit bit bit bit
i_wr_init i_rd_init i_read i_write i_idle
= ~monitor_mp.ads_n & monitor_mp.w_r_n; = ~monitor_mp.ads_n & ~monitor_mp.w_r_n; = ~monitor_mp.w_r_n; = monitor_mp.w_r_n = monitor_mp.ads_n;
bit o_dsel bit o_idle
= =
bit o_nop
=
bit o_read
=
bit o_write
=
bit o_act
=
bit o_pre
=
bit o_ref
=
bit o_error
monitor_mp.cs_n; monitor_mp.cs_n monitor_mp.cas_n ~monitor_mp.cs_n monitor_mp.cas_n ~monitor_mp.cs_n ~monitor_mp.cas_n ~monitor_mp.cs_n ~monitor_mp.cas_n ~monitor_mp.cs_n monitor_mp.cas_n ~monitor_mp.cs_n monitor_mp.cas_n ~monitor_mp.cs_n ~monitor_mp.cas_n
& & & & & & & & & & & & & &
monitor_mp.ras_n monitor_mp.we_n; monitor_mp.ras_n monitor_mp.we_n; monitor_mp.ras_n monitor_mp.we_n; monitor_mp.ras_n ~monitor_mp.we_n; ~monitor_mp.ras_n monitor_mp.we_n; ~monitor_mp.ras_n ~monitor_mp.we_n; ~monitor_mp.ras_n monitor_mp.we_n;
= ~monitor_mp.cs_n ~monitor_mp.cas_n || ~monitor_mp.cs_n monitor_mp.cas_n
& & & &
~monitor_mp.ras_n & ~monitor_mp.we_n monitor_mp.ras_n & ~monitor_mp.we_n;
& & & & & & &
. . . endmodule
In our simple memory controller example, we followed the signal naming convention you would encounter when reading a typical SDRAM specification. That is, active low signals in the specification are denoted with a “#” character at the end of the signal name. During the implementation process, engineers typically convert to an implementation naming convention when denoting an active low signal. In our SystemVerilog examples, we follow an implementation naming convention in which the “_n” characters are added to the end of all active low signal names. We are now ready to write assertions for the memory controller properties defined in Table 7-5 (see page 159). Our first property demonstrated in Assertion 7-1 specifies the valid reset transition.
Chapter 7, “Controllers”
165
Assertion 7-1
Memory controller reset transition
property p_reset_idle_trans; @(posedge monitor_mp.clk) $fell(monitor_mp.rst) |-> o_idle; endproperty a_reset_idle_trans: assert property (p_reset_idle_trans) else begin status = new(); status.set_err_reset_idle_trans(); status_ap.write(status); end
Assertion 7-2 specify the valid idle state transitions, as defined by our conceptual state-machine. Assertion 7-2
Memory controller idle transitions
property p_idle_refresh_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_idle & ~tim_n |=> o_refresh; endproperty a_reset_idle_trans: assert property (p_idle_refresh_trans) else begin status = new(); status.set_err_idle_refresh_trans(); status_ap.write(status); end property p_idle_idle_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_idle & tim_n & i_idle |=> o_idle; endproperty a_idle_idle_trans: assert property (p_idle_idle_trans) else begin status = new(); status.set_err_idle_idle_trans(); status_ap.write(status); end property p_idle_write_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_idle & tim_n & i_wr_init & ~hit_n |=> o_write; endproperty a_idle_write_trans: assert property (p_idle_write_trans) else begin status = new(); status.set_err_idle_write_trans(); status_ap.write(status); end // Continuned on next page
166 Creating Assertion-Based IP
Assertion 7-2
Memory controller idle transitions
property p_idle_read_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_idle & tim_n & i_rd_init & ~hit_n |=> o_read; endproperty a_idle_read_trans: assert property (p_idle_read_trans) else begin status = new(); status.set_err_idle_read_trans(); status_ap.write(status); end property p_idle_act_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_idle & tim_n & ~i_idle & hit_n |=> o_act; endproperty a_idle_act_trans: assert property (p_idle_act_trans) else begin status = new(); status.set_err_idle_act_trans(); status_ap.write(status); end
Assertion 7-3 specifies the valid refresh state transition, as defined by our conceptual state-machine. Assertion 7-3
Memory controller refresh transitions
property p_refresh_idle_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_refresh |=> o_idle; endproperty a_refresh_idle_trans: assert property (p_refresh_idle_trans) else begin status = new(); status.set_err_refresh_idle_trans(); status_ap.write(status); end
Assertion 7-4 specify the valid write state transitions, as defined by our conceptual state-machine.
Chapter 7, “Controllers”
167
Assertion 7-4
Memory controller write transitions
property p_write_write_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_write & ~hit_n & done_n |=> o_write; endproperty a_write_write_trans: assert property (p_write_write_trans) else begin status = new(); status.set_err_write_write_trans(); status_ap.write(status); end property p_write_act_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_write & hit_n & done_n |=> o_act; endproperty a_write_act_trans: assert property (p_write_act_trans) else begin status = new(); status.set_err_write_act_trans(); status_ap.write(status); end property p_write_pre_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_write & ~done_n |=> o_pre; endproperty a_write_pre_trans: assert property (p_write_pre_trans) else begin status = new(); status.set_err_write_pre_trans(); status_ap.write(status); end
Assertion 7-5 specify the valid read state transitions, as defined by our conceptual state-machine.
168 Creating Assertion-Based IP
.
Assertion 7-5
Memory controller read transitions
property p_read_read_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_read & ~hit_n & done_n |=> o_read; endproperty a_read_read_trans: assert property (p_read_read_trans) else begin status = new(); status.set_err_read_read_trans() status_ap.write(status); end property p_read_act_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_read & hit_n & done_n |=> o_act; endproperty a_read_act_trans: assert property (p_read_act_trans) else begin status = new(); status.set_err_read_act_trans(); status_ap.write(status); end property p_read_pre_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_read & ~done_n |=> o_pre; endproperty a_read_pre_trans: assert property (p_read_pre_trans) else begin status = new(); status.set_err_read_pre_trans(); status_ap.write(status); end
Assertion 7-6 specify the valid row-activating state transitions, as defined by our conceptual state-machine.
Chapter 7, “Controllers”
169
.
Assertion 7-6
Memory controller row-activating transitions
property p_act_write_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_act & i_write |=> o_write; endproperty a_act_write_trans: assert property (p_act_write_trans) else begin status = new(); status.set_err_act_write_trans(); status_ap.write(status); end property p_act_read_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_act & i_read |=> o_read; endproperty a_act_read_trans: assert property (p_act_read_trans) else begin status = new(); status.set_err_act_read_trans(); status_ap.write(status); end
Assertion 7-7 specifies the valid precharge state transition, as defined by our conceptual state-machine. .
Assertion 7-7
Memory controller precharge transitions
property p_pre_idle_trans; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) o_pre |=> o_idle; endproperty a_pre_idle_trans: assert property (p_pre_idle_trans) else begin status = new(); status.set_err_pre_idle_trans(); status_ap.write(status); end
Assertion 7-8 specifies the valid behavior for the processor to memory address strobe.
170 Creating Assertion-Based IP
.
Assertion 7-8
Processor to memory controller address strobe
property p_ADS_valid; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) ~ads_n |=> (ads_n throughout o_pre[->1]); endproperty a_ADS_valid: assert property (p_ADS_valid) else begin status = new(); status.set_err_ADS_valid(); status_ap.write(status); end
Assertion 7-9 specifies the valid behavior for the processor to memory last transfer strobe. Assertion 7-9
Processor to memory controller stable controls
property p_BLAST_initial_condition; @(posedge monitor_mp.clk) monitor_mp.rst |=> blast_n throughout ~ads_n[->1]; endproperty a_BLAST_initial_condition: assert property (p_BLAST_initial_condition) else begin status = new(); status.set_err_BLAST_initial_condition(); status_ap.write(status); end property p_BLAST_valid; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) ~blast_n |=> blast_n throughout ~ads_n[=1] ##1 ~blast_n; endproperty a_BLAST_valid: assert property (p_BLAST_valid) else begin status = new(); status.set_err_BLAST_valid(); status_ap.write(status); end
The set of properties shown in Assertion 7-10 specify stable behavior for the processor to memory controller W_R#
Chapter 7, “Controllers”
171
control (w_r_n), A address bits (a), and BE byte enable signals (be) during a memory transfer. Assertion 7-10
Processor to memory controller last transfer strobe
property p_W_R_stable; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) ~i_idle |=> $stable(w_r_n) throughout i_pre[->1]; endproperty a_W_R_stable: assert property (p_W_R_stable) else begin status = new(); status.set_err_W_R_stable(); status_ap.write(status); end property p_A_stable; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) ~i_idle |=> $stable(a) throughout i_pre[->1]; endproperty a_A_stable: assert property (p_A_stable) else begin status = new(); status.set_err_A_stable(); status_ap.write(status); end property p_BE_stable; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) ~i_idle |=> $stable(be) throughout i_pre[->1]; endproperty a_BE_stable: assert property (p_BE_stable) else begin status = new(); status.set_err_BE_stable(); status_ap.write(status); end
The set of properties shown in Assertion 7-11 specify legal behavior of the memory controller to process READY# signal.
172 Creating Assertion-Based IP
.
Assertion 7-11
Processor to memory controller ready signal
property p_READY_low; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) $past(~ready_n) -> (o_read || o_write); endproperty a_READY_low: assert property (p_READY_low) else begin status = new(); status.set_err_READY_low(); status_ap.write(status); end property p_READY_high; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) $past(ready_n) -> (~o_read && ~o_write); endproperty a_READY_high: assert property (p_READY_high) else begin status = new(); status.set_err_READY_high(); status_ap.write(status); end
The final property (shown in Assertion 7-12) specifies that only legal SDRAM commands are issued from the memory controller. .
Assertion 7-12
Memory controller to SDRAM valid commands
property p_valid_commands; @(posedge monitor_mp.clk) disable iff (monitor_mp.rst) ~o_error; endproperty a_valid_commands: assert property (p_valid_commands) else begin status = new(); status.set_err_valid_commands(); status_ap.write(status); end
7.1.5 Encapsulate properties In this step, we turn our set of related memory controller assertions (and any coverage properties) into a reusable assertion-based monitor verification component. We add Chapter 7, “Controllers”
173
analysis ports to our monitor for communication with other simulation verification components as Chapter 3, “The Process” demonstrates. Example 7-4
Encapsulate properties inside a module
import ovm_pkg::*; import tb_tr_pkg::*; // tb_status class definition module tb_mem_controller_mon ( interface.monitor_mp monitor_mp ); ovm_analysis_port #(tb_status_mc) status_ap = new(“status_ap”, null); ovm_analysis_port #(tb_coverage) coverage_ap = new(“coverage_ap”, null); tb_status_mc status; . . . // Any required modeling code here (to model environment) . . . // All assertions and coverage properties here . . . endmodule
7.2 Summary In this chapter, we demonstrated our process of creating assertion-based IP for a simple, yet non-trivial, memory controller. The general approach consists of the following steps: 1 Define the legal memory controller interface
commands 2 Map conceptual states of the memory controller to
specific output commands 3 Define the legal command sequences (that is,
transitions for the conceptual state-machine) Obviously, your particular conceptual state-machine is directly influenced by the memory vendor you have selected for your design. 174 Creating Assertion-Based IP
Once you define the memory controller conceptual statemachine, properties are then easily identified by specifying legal transitions. When creating assertion-based IP, some engineers prefer to explicitly model the conceptual state-machine as a monitor. An error state is introduced in this model, and a simple assertion is then written that states the conceptual statemachine must never enter the error state. This form of assertion-based IP does have a debugging advantage, since the implementor does not have to manage a large set of assertions normally required to implicitly describe legal conceptual state transitions. However, as the conceptual state-machine grows, or when it is necessary to describe concurrency or overlapping behaviors, a modeling approach to creating verification IP can quickly become overwhelming. In this chapter, we did not explicitly model the conceptual state-machine in the verification IP. We created properties that specified each valid transition of the conceptual statemachine. Although the focus of this book is on simulationbased use of assertion-based IP, it is worth mentioning that implicit specification of the conceptual states using assertions tends to perform better in formal property checking than introducing a large state-machine as part of the specification. The negative aspect of creating an implicit conceptual state-machine through a large set of assertions is a concern of completeness, as well as potentially introducing contradictions contained within a large set of assertions. In either case, careful review is critical.
Chapter 7, “Controllers”
175
C
H A P T E R
DATAPATH
CHAPTER 8
Design components are often classified as either datapathor control-oriented. In the previous chapters, we demonstrated our assertion-based IP creation process for control-oriented design components. In this chapter, we focus on various datapath components that are likely to be found in today’s platform-based SoC designs. Figure 8-1 illustrates our platform-based SoC example with various datapath components highlighted. Figure 8-1.
Platform-based SoC design with datapath components
CPU
UART
ALU
Pipelined Bus
Bridge
Nonpipelined Bus
FIFO
Arbiter
Graphics Controller
Timer
IFFT
Most people classify datapath as some form of design behavior that performs data processing. In Chattterjee [2006] and Foster et al. [2006], the authors propose refining the classification of datapath into either data transport or data transform, as illustrated in Figure 8-2.
Chapter 8, “Datapath”
177
Figure 8-2.
Design behavior classification Design Behavior
Control
Datapath
Data Transportation
Data Transformation
A data transport design component essentially transports unchanged data (for example, packets) from multiple input sources to multiple output sources (for example, a PCI Express Data Link Layer design component). On the other hand, a data transform design component performs a mathematical computation (an algorithm) over different data input values (for example, an IFFT convolution block). Foster et al. [2006] argue that data transport blocks are amenable to formal verification (that is, model checking) due to the independence of the bits in the datapath, which makes the formal verification independent of the width of the datapath. However, this symmetry reduction technique (a formal verification abstraction) cannot be applied to data transform design components. As we discussed in the introduction, our philosophy toward creating assertion-based IP in this book is to first focus on capturing the intent of the design in a set of assertions suitable for simulation, and then optimize any of the problematic assertions you encounter during formal verification only if you experience problems while attempting proof—and only if there is a clear return on effort to achieve a complete proof. Hence, in this chapter we have not limited our discussion to written assertions that are only amenable to formal verification, and we demonstrate our assertion-based IP creation process on both data transportation and data transform design components.
178 Creating Assertion-Based IP
You will note that the data integrity class of properties we demonstrate in this chapter become progressively more difficult to express from one section to the next, even with the added expressive power of SVA local variables. In fact, we argue in Section 8.4, “Data compression” and Section 8.5, “Data decompression” that using assertion-based IP to check data integrity in simulation for this particular class of design components is not necessarily the best technique. Alternative solutions, such as scoreboarding, might be a better solution to the problem. Finally, even though this chapter focuses on datapath components, you will note that there are still many control oriented properties associated with these components that need to be expressed.
8.1 Multiport register file A register file is a data transportation design component example that consist of an array of registers found in many of today’s devices, such as a central processing unit (CPU) or a network router. These modern devices will almost always define a set of registers that are used to stage data that is to be transported between memory and various other connected functional units. In this section, we introduce a simple multiport register file design component to demonstrate our assertion-based IP creation process. While reviewing this chapter, you might notice some similarities in our simple multiport register file to a simple network router. Hence, many of the concepts can be extended to more complicated switch type designs.
8.1.1 Block diagram interface description Figure 8-3 illustrates a block diagram for a simple multiport register file design component. For our example, all signal transitions relate only to the rising edge of the clock (clk). Chapter 8, “Datapath”
179
Table 8-1 provides a summary of the interface signals for our simple packet data compressor design example. Figure 8-3
A simple multiport register file block diagram
p0_wr_data[15:0]
p0_rd_addr[4:0]
p0_wr_en
p0_rd_sel
p0_wr_addr[4:0]
p0_rd_size
p1_wr_data[15:0]
p0_rd_data[31:0] Multiport Register File
p1_wr_en p1_wr_addr[4:0]
p1_rd_addr[4:0]
p1_wr_addr_conflict
p1_rd_sel
rst_n
p1_rd_size
clk
p1_rd_data[31:0]
Table 8-1 Interface description for the multiport register file
Name p0_wr_data[15:0] p0_wr_en p0_wr_addr[4:0] p1_wr_data[15:0] p1_wr_en p1_wr_addr[4:0] p1_wr_addr_conflict p0_rd_addr[4:0] p0_rd_sel p0_rd_size p0_rd_data[31:0] p1_rd_addr[4:0] p1_rd_sel p1_rd_size p1_rd_data[31:0] clk rst_n
Description Write port 0 write data Write port 0 write enable Write port 0 address Write port 1 write data Write port 1 write enable Write port 1 address Write address conflict (p0 takes priority over p1) Read port 0 address Read port 0 read select Read port 0 read size (16-bit=0, 32-bit=1) Read port 0 register data Read port 1 address Read port 1 read select Read port 1 read size (16-bit=0, 32-bit=1) Read port 1 register data Synchronous clock Asynchronous reset
180 Creating Assertion-Based IP
8.1.2 Overview description Our simple multiport register file consists of 32 16-bit registers. There are two 16-bit input write ports and two 32bit output read ports. Each write port can individually address any register within the register file. However, port p0 has higher write priority over p1 when both ports are attempting to write to the same register file address. A memory address write conflict results in p0 completing its write and the register file then asserts p1_wr_addr_conflict to indicate that the p1 write port attempt was unsuccessful. The read ports can access either a 16-bit or 32-bit value. That is, if p0_rd_size is set to zero, then a 16-bit read on port p0 occurs. However, if p0_rd_size is set to one, then a 32-bit read of port p0 occurs. Note that a 16-bit read access can address any register within the register file. However, for a 32-bit read, the lower returned bits will always contain the value of an even address register while the upper bits will contain the value of an odd register. This means that if the p0_rd_addr (or p1_rd_addr) is set to an odd value (for example, five) then the read value for the lower 16 bits will consist of the value obtained from register four, while the read value for the upper 16 bits will consist of the value obtained from register five.
Basic register operation The waveforms in Figure 8-4 illustrate a write operation for our simple multiport register file. The write operation begins with an address of 5’d0 and some arbitrary data value (represented by ‘DA in Figure 8-4) placed on port p0’s input controls. If p0_wr_en is asserted at clock one then the input data is written into register zero. At clock two, the data value ‘DB is written into register address 5’d1 from port p0, and simultaneously, the data value ‘DX is written into register address 5’d4 from port p1. At clock three, the data value ‘DY is written into register address 5’d3 from port p1. Finally, at clock five, both ports p0 and p1 are trying to write data into register address 5’d2. This creates a conflict that is resolved by writing the higher priority port p0’s input data value of ‘DC into register address 5’d2 and asserting Chapter 8, “Datapath”
181
p1_wr_addr_conflict. Hence, the input data value from port p1‘DZ is dropped at clock five.
Figure 8-4
Register file 16-bit write operation 0
p0_wr_data[15:0]
1
2
3
`DA
`DB
`DC
5'd0
5'd1
5'd2
4
5
p0_wr_en p0_wr_addr[4:0] p1_wr_data[15:0]
`DX
`DY
`DZ
5'd4
5'd3
5'd2
p1_wr_en p1_wr_addr[4:0]
p1_wr_addr_conflict
Figure 8-5
Register file 16-bit and 32-bit read operation 5
6
p0_rd_data[15:0]
`DA
p0_rd_data[31:16]
`DB
p0_rd_addr[4:0]
5'd0
7
8
9
10
`DC
5'd1
5'd2
p0_rd_sel p0_rd_size p1_rd_data[15:0]
`DB
`DY
p1_rd_data[31:16] p1_rd_addr[4:0]
`DC
5'd1
5'd3
5'd2
p1_rd_sel p1_rd_size
The waveforms in Figure 8-5 illustrate a write operation for our simple register file. 182 Creating Assertion-Based IP
This figure illustrates the case where a 32-bit read from either an odd or even address yields the same result. That is, the upper 16 bits of a 32-bit read will always align with an odd address, while the lower 16 bits align with an even address (that is, odd address minus one). Hence, a 32-bit read to either 5’d1 or 5’d0 yields the same result as illustrated prior to clocks six and seven in our example.
8.1.3 Natural language properties Prior to creating a set of SystemVerilog assertions for our simple multiport register file, we must identify a comprehensive list of natural language properties, as shown in Table 8-2. 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-2 Simple multiport register file properties Assertion name
Summary
a_reset_state_inactive a_wr_rd_data_integrity
Initial value of registers after reset is 0 A write followed by a read on any port to the same register will return the last highest priority written data Reads before any writes will read 0 A pair of reads from a register, without an intervening write on any port, will each return the same data. Output read data without a read select signal will not change from its prior value
a_reg_after_reset a_rd_rd_data_integrity
a_rd_stable
Chapter 8, “Datapath”
183
8.1.4 Assertions To create a set of SystemVerilog assertions for our simple multiport register file, 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-15 illustrates. Example 8-7 on page 219 demonstrated an interface for our simple multiport register file example. For this case, our assertion-based monitor references the interface signals via the direction defined by the monitor_mp named modport. .
Example 8-1
Encapsulate signals inside an interface
interface tb_register_file_if( input clk , input rst ); bit bit bit bit bit bit bit bit bit bit bit bit bik bit bit
[15:0] p0_wr_data; p0_wr_en; [4:0] p0_wr_addr; [15:0] p1_wr_data; p1_wr_en; [4:0] p1_wr_addr; p1_wr_addr_conflict; [4:0] p0_rd_addr; p0_rd_sel; p0_rd_size; [31:0] p0_rd_data; [4:0] p1_rd_addr; p1_rd_sel; p1_rd_size; [31:0] p1_rd_data;
modport dut ( input clk , rst , . . . ); . . . modport input input input input input
monitor_mp ( clk , rst , p0_wr_data, p0_wr_en, p0_wr_addr, p1_wr_data,
// Continued on next page
184 Creating Assertion-Based IP
Example 8-1 input input input input input input input input input input input
Encapsulate signals inside an interface
p1_wr_en, p1_wr_addr, p1_wr_addr_conflict, p0_rd_addr, p0_rd_sel, p0_rd_size, p0_rd_data, p1_rd_addr, p1_rd_sel, p1_rd_size, p1_rd_data );
endinterface
In addition to pin-level interfaces, we need a means for our simple multiport register file 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-6 illustrates. Figure 8-6
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 require their own analysis ports. We discuss coverage with respect to creating assertion-based IP separately in Section 5.4. Upon detecting a register file error, the analysis port broadcasts the error condition (using an error status transaction object) to other verification components. Chapter 8, “Datapath”
185
The error status transaction class is defined in Example 8-2, which is an extension of avm_transaction base class. The tb_reg_file_status class includes an enum that identifies the various types of register file errors and a set of methods to uniquely identify the specific error it detected. Example 8-2
Error status class
class tb_reg_file status extends avm_transaction; typedef enum { ERR_RESET_STATE_INACTIVE, ERR_WR_RD_DATA_INTEGRITY, ERR_REG_AFTER_RESET, ERR_RD_RD_DATA_INTEGRITY, ERR_RD_STABLE } reg_file_err_status_t; reg_file_err_status_t reg_file_err_status; bit [5:0] err_reg_addr; function void set_err_reset_state_inactive; reg_file_err_status = ERR_RESET_STATE_INACTIVE ; endfunction function void set_err_wr_rd_data_integrity (input bit [5:0] i); reg_file_err_status = ERR_WR_RD_DATA_INTEGRITY ; err_reg_addr = i; endfunction function void set_err_reg_after_reset (input bit [5:0] i); reg_file_err_status = ERR_REG_AFTER_RESET ; err_reg_addr = i; endfunction function void set_err_rd_rd_data_integrity (input bit [5:0] i); reg_file_err_status = ERR_RD_RD_DATA_INTEGRITY ; err_reg_addr = i; endfunction function void set_err_rd_stable(input bit [5:0] i); reg_file_err_status = ERR_RD_STABLE; err_port_num = i; endfunction . . . endclass
Assertion 8-1, “Register file output inactive after reset” demonstrates our first property.
186 Creating Assertion-Based IP
Assertion 8-1
Register file output inactive after reset
property p_reset_state_inactive; @(posedge monitor_mp.clk) $rose(monitor_mp.rst_n) |-> (monitor_mp.p0_rd_data==32’d0) & (monitor_mp.p1_rd_data==32’d0) & (monitor_mp.p1_wr_addr_conflict==1’b0) ; endproperty a_reset_state_inactive : assert property (p_reset_state_inactive) else begin status = new(); status.set_err_reset_state_inactive(); status_ap.write(status); end
Assertion 8-2, “Register file data integrity” demonstrates our next property. Assertion 8-2
Register file data integrity
// Parameterized property to check 16-bit data integrity // Address A is a constant from a generate statement! property p_wr_rd_data_integrity(A, we0, we1, re, logic [16:0] lv_data; @(posedge monitor_mp.clk) disable iff
wa0, wdata0, wa1, wdata1, ra, rdata, rdsize); (~monitor_mp.rst_n)
// (a) If either a 16-bit write to port 0 address A or to port 1 // (that is, the lower priority port) address A. . . (we0 & wa0==A, lv_data=wdata0) or (!(we0 & wa0==A) & (we1 & wa1==A), lv_data=wdata1) // (b) followed by a sequence in which no writes to address A from // either port, and eventually a read to address A. . . ##1 !(we0 & wa0==A | we1 & wa1==A) throughout (re & ra==A)[->1] // (c) new read data is same as original read data, selecting // appropriate upper or lower 16-bit slice from 32-bit read value |-> (lv_data==(rdsize & (A & 5b1)) ? rdata[31:16] : rdata[15:0]); endproperty generate begin genvar i; for (i = 0; i<=31; i++) begin // Continued on next page
Chapter 8, “Datapath”
187
Assertion 8-2
Register file data integrity
a_wr_rd_p0: assert property ( p_wr_rd_data_integrity( i, monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p0_rd_sel, monitor_mp.p0_rd_addr, monitor_mp.p0_rd_data, monitor_mp.p0_rd_size) ) else begin status = new(); status.set_err_wr_rd_data_integrity(i); status_ap.write(status); end a_wr_rd_p1: assert property (p_wr_rd_data_integrity( i, monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p1_rd_sel, monitor_mp.p1_rd_addr, monitor_mp.p1_rd_data, monitor_mp.p1_rd_size) ) else begin status = new(); status.set_err_wr_rd_data_integrity(i); status_ap.write(status); end end // for end // generate endgenerate Steps for creating data integrity assertions
When attempting to create a data integrity assertion for a register file, you might be overwhelmed initially by the complexity of the problem. However, we recommend that you approach this class of problems in the following manner: •
First, only consider a single output port when creating your assertion—we will consider the other output ports separately
•
Next, only consider a single register address value to check for data integrity as part of the assertion—we will consider the other register addresses separately
•
Finally, for simulation assertion-based IP, you must consider all possible valid input behaviors
•
Then, either implicitly or explicitly create a separate assertion for each register address related to a specific
188 Creating Assertion-Based IP
port. For example, an assertion could be written using a local variable that holds the last written address, which implicitly can handle all possible addresses. Implicit versus explicit addresses
This implicit addressing approach has the advantage of handling large memories, yet the assertion can be more complicated to code or understand. Alternatively, a set of explicit address assertion could be generated by creating a SystemVerilog generate statement that explicitly specifies all possible address values as a constant. This approach often has the advantage of being simpler to code, which we demonstrate in our next assertion. However, for large memories, the explicit addressing technique can grow the number of generated assertions rapidly and potentially create a performance problem. In this section, we demonstrate both explicit and implicit addressing approaches. You might note that both approaches (for our example) result in about the same amount of code. Our Assertion 8-2 begins by looking for a write on the higher priority port 0, or a write on the lower priority port 1 with no write on port 0 (see label (a) in our example), to a specific reference address of interest. Note that our reference address A in this example is actually a constant created by a generate statement, so it is unnecessary to store the address into a local variable. Next, in the step labeled (b), our property looks for a sequence of no writes from either of the input ports to the specific address of interest (in this case, A) followed by a read to address A. Then, the property checks in the step labeled (c) that the read data is the same as the last write data. Notice that if a new write occurs prior to the read occurring, the antecedent on the currently evaluating property does not hold. This effectively ends the previous check and restarts the evaluation of the property for the new write to address A. Keep in mind that we are only interested in the last write to address A.
Chapter 8, “Datapath”
189
For Assertion 8-2, we are monitoring a 16-bit write to a specific memory address (A). Hence, for the 16-bit read, we can simply compare the previously written value (held in the lv_data local variable) with the lower 16 bits of the read data. However, for a 32-bit read, we need to account for a 16-bit write to an odd address, which we detect by the equation (A & 5’b1), and then compare the upper 16 bits of the 32-bit read value to the previously written value for the case when the address is odd. For the case when the address is even, we simply check the lower 16 read bits to the previously written value. Assertion 8-3, “Register file data cleared after reset” demonstrates our next property. The technique we use for the coding problem demonstrated in Assertion 8-2 is also applied here. Assertion 8-3
Register file data cleared after reset
// Parameterized property to check a single reg (16-bit data) // Address A is a constant from a generate statement! property p_reg_after_reset(A, we0, wa0, wdata0, we1, wa1, wdata1, re, ra, rdata, rdsize); @(posedge monitor_mp.clk) // (a) After a reset $rose(monitor_mp.rst_n) // (b) and a sequence in which no writes to reg address A from // either port, and eventually a read to reg address A ##0 !(we0 & wa0==A | we1 & wa1==A) throughout (re & ra==A)[->1] // (c) then the read data must be 0 (accounting for either a 16- or // 32-bit read and that we are only monitoring a single reg addr A) |-> (rdsize & (A & 5’b1)) ? rdata[31:16]=16’b0 : rdata[15:0]==16’b0; endproperty generate begin genvar i; for (i = 0; i<=31; i++) begin // Continued on next page
190 Creating Assertion-Based IP
Assertion 8-3
Register file data cleared after reset
a_rd_p0: // reads from port 0 assert property ( p_reg_after_reset(i, monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p0_rd_sel, monitor_mp.p0_rd_addr, monitor_mp.p0_rd_data, monitor_mp.p0_rd_size)) else begin status = new(); status.set_err_init_rd_is_0(i); status_ap.write(status); end a_rd_p1: // reads from port 1 assert property (p_reg_after_reset( i, monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p1_rd_sel, monitor_mp.p1_rd_addr, monitor_mp.p1_rd_data, monitor_mp.p1_rd_size)) else begin status = new(); status.set_err_data_integrity(i); status_ap.write(status); end end // for end endgenerate
The property begins whenever reset completes (see step (a) in example), which is detected by looking for a rising edge of the active low reset. Once this occurs, in step (b) we look for a sequence in which no writes occur on either input port, followed by a read to a specific memory address of interest (in our case, that is A). Once this occurs, in step (c) we check that the read value is zero. Note that we are monitoring 16bit writes to a specific address. Hence, a 32-bit read must account for either an odd address (and check the upper 16 bits of the read data value) or it must account for an even address (and check the lower 16 bits of the read data value). Read followed by another read to same address
Concerning data integrity, it is insufficient to simply check that the data is valid for the case of a read following a write to the same address. We also need to ensure that a read followed by another read to the same address (with no Chapter 8, “Datapath”
191
writes to the address we are monitoring) results in the same value. Hence, property a_rd_rd_data_integrity defined in Table 8-2, “Simple multiport register file properties” checks this situation. To simplify understanding this property, we have broken the problem down into four cases that you must consider: 1 A 16-bit read to a specific address, followed by another
16-bit read to the same address, and no writes have occurred to the address we are monitoring 2 A 16-bit read to a specific address, followed by another
32-bit read to the same address, and no writes have occurred to the address we are monitoring 3 A 32-bit read to a specific address, followed by another
16-bit read to the same address, and no writes have occurred to the address we are monitoring 4 A 32-bit read to a specific address, followed by another
32-bit read to the same address, and no writes have occurred to the address we are monitoring Although there are similarities between each case—there are sufficient differences that warrant partitioning these behaviors into separate properties (at least initially, during the development phase, to simplify the complexity that can occur when the properties are combined into a single property). Assertion 8-4, “16-bit read followed by 16-bit read data integrity” is our first case. Our property begins by looking for the occurrence of a 16-bit read, which is labeled as (a) in our example. Once a 16-bit read occurs, the data and address is stored in a local variable for future reference. The next step, which is labeled as (b) in our example, is to look for a sequence of an eventual read to the same address (stored in the local variable lv_A), and throughout this sequence there are no new writes to the address that we are monitoring. If this sequence is detected, then the new 16-bit read data, which is the lower 16-bits of the 32-bit read port, must match the original 16-bit read data, which were stored 192 Creating Assertion-Based IP
in local variable lv_data. This step is labeled as (c) in our example. Assertion 8-4
16-bit read followed by 16-bit read data integrity
property p_16_rd_16_rd_integrity(we0, wa0, wdata0, we1, wa1, wdata1, re, ra, rdata, rdsize); logic [15:0] lv_data; logic [5:0] lv_A; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) // (a) 16-bit read occurs, grab data and address (re & !rdsize, lv_data = rdata[15:0], lv_A = ra) // (b) another 16-bit read to same address and no writes ##1 !((we0 & wa0==lv_A) | (we1 & wa1==lv_A)) throughout (re & ra==lv_A & !rdsize)[->1] |-> // (c) new 16-bit read data same as original 16-bit read data ( lv_data == rdata[15:0] ); endproperty a_16_rd_16_rd_integrity_p0: // port 0 read assert property ( p_16_rd_16_rd_integrity( monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p0_rd_sel, monitor_mp.p0_rd_addr, monitor_mp.p0_rd_data, monitor_mp.p0_rd_size)) else begin status = new(); status.set_err_rd_rd_integrity(monitor_mp.p0_rd_addr); status_ap.write(status); end a_16_rd_16_rd_integrity_p1: // port 1 read assert property ( p_16_rd_16_rd_integrity( monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p1_rd_sel, monitor_mp.p1_rd_addr, monitor_mp.p1_rd_data, monitor_mp.p1_rd_size)) else begin status = new(); status.set_err_rd_rd_integrity(monitor_mp.p1_rd_addr); status_ap.write(status); end
Assertion 8-5, “16-bit read followed by 32-bit read data integrity” demonstrates our second case. Our property begins exactly like our previous example. However, in step (b) we are now looking for a 32-bit read. Once this sequence is detected, the appropriate 16-bit slice of the new 32-bit Chapter 8, “Datapath”
193
read data (based on an odd or even read address) is compared to the original 16-bit read data, as demonstrated in step (c) in our example. Assertion 8-5
16-bit read followed by 32-bit read data integrity
property p_16_rd_32_rd_integrity(we0, wa0, wdata0, we1, wa1, wdata1, re, ra, rdata, rdsize); logic [15:0] lv_data; logic [5:0] lv_A; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) // (a) 16-bit read occurs, grab data and address (re & !rdsize, lv_data = rdata[15:0], lv_A = ra) // (b) another 32-bit read to same address and no writes ##1 !((we0 & wa0==lv_A) | (we1 & wa1==lv_A)) throughout (re & ra==lv_A & rdsize)[->1] |-> // (c) new read data is same as original read data, selecting // appropriate upper or lower 16-bit slice from 32-bit read value ( lv_data == (lv_A & 5’b1) ? rdata[31:16] : rdata[15:0] ); endproperty a_16_rd_32_rd_integrity_p0: // port 0 read assert property ( p_16_rd_32_rd_integrity( monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p0_rd_sel, monitor_mp.p0_rd_addr, monitor_mp.p0_rd_data, monitor_mp.p0_rd_size)) else begin status = new(); status.set_err_rd_rd_integrity(monitor_mp.p0_rd_addr); status_ap.write(status); end a_16_rd_32_rd_integrity_p1: // port 1 read assert property ( p_16_rd_32_rd_integrity( monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p1_rd_sel, monitor_mp.p1_rd_addr, monitor_mp.p1_rd_data, monitor_mp.p1_rd_size)) else begin status = new(); status.set_err_rd_rd_integrity(monitor_mp.p1_rd_addr); status_ap.write(status); end
Assertion 8-6, “32-bit read followed by 16-bit read data integrity” demonstrates our third case. Our property begins
194 Creating Assertion-Based IP
similar to our first example, except the appropriate upper or lower 16-bit read data is stored for future comparison based on the reference to either an odd or even address (see step (a) in our example). In step (c) from our example, the new 16-bit read data, which comprises the lower bits of the read port, is compared to the original 16-bit read data, which is stored in local variable lv_data. Assertion 8-6
32-bit read followed by 16-bit read data integrity
property p_32_rd_16_rd_integrity(we0, wa0, wdata0, we1, wa1, wdata1, re, ra, rdata, rdsize); logic [15:0] lv_data; logic [5:0] lv_A; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) // (a) 32-bit read occurs, grab appropriate 16-bit data and address (re & rdsize, lv_data = (ra&5’b1) ? rdata[31:16] : rdata[15:0], lv_A = ra) // (b) another 16-bit read to same address and no writes ##1 !((we0 & wa0==lv_A) | (we1 & wa1==lv_A)) throughout (re & ra==lv_A & !rdsize)[->1] |-> // (c) new 16-bit read data same as original read data ( rdata[15:0] == lv_data ); endproperty a_32_rd_16_rd_integrity_p0: // port 0 read assert property ( p_32_rd_16_rd_integrity( monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p0_rd_sel, monitor_mp.p0_rd_addr, monitor_mp.p0_rd_data, monitor_mp.p0_rd_size)) else begin status = new(); status.set_err_rd_rd_integrity(monitor_mp.p0_rd_addr); status_ap.write(status); end a_32_rd_16_rd_integrity_p1: // port 1 read assert property ( p_32_rd_16_rd_integrity( monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p1_rd_sel, monitor_mp.p1_rd_addr, monitor_mp.p1_rd_data, monitor_mp.p1_rd_size)) else begin status = new(); status.set_err_rd_rd_integrity(monitor_mp.p1_rd_addr); status_ap.write(status); end
Chapter 8, “Datapath”
195
Assertion 8-7, “32-bit read followed by 32-bit read data integrity” demonstrates our final case. Our property begins by looking for the occurrence of a 32-bit read, which is labeled as (a) in our example. When this event is detected, the data and address are stored in a local variable for future reference. The next step, which is labeled (b) in our example, is to look for a sequence of an eventual read to the same address (stored in the local variable lv_A), and throughout this read sequence there are no new 16-bit writes to either the even (lower) or odd (upper) address that we are monitoring. To accomplish this, in step (b) we force the write address to an odd value with the equation: ((wa0|5’b1)==(A|5’b1))
By doing this, we are able to detect the occurrence of a 16bit write to either the even or odd address for the read address we are monitoring. Note that it is not necessary to know specifically which even or odd address was written to abort our check. Finally, in step (c), we compare the new 32-bit read value to the previously stored 32-bit read value. Assertion 8-7
32-bit read followed by 32-bit read data integrity
property p_32_rd_32_rd_integrity(we0, wa0, wdata0, we1, wa1, wdata1, re, ra, rdata, rdsize); logic [31:0] lv_data; logic [5:0] lv_A; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) // (a) 32-bit read occurs, grab appropriate 16-bit data and address (re & rdsize, lv_data = rdata, lv_A = ra) // (b) another 32-bit read to same address and no 16-bit writes to // either the upper or lower word the original read address ##1 !(we0 & ((wa0|5’b1)==(A|5’b1)) | we1 & ((wa1|5’b1)==(A|5’b1))) throughout (re & ra==lv_A & rdsize)[->1] |-> // (c) new 32-bit read data same as original read data ( rdata == lv_data ); endproperty // Continued on next page
196 Creating Assertion-Based IP
Assertion 8-7
32-bit read followed by 32-bit read data integrity
a_32_rd_32_rd_integrity_p0: // port 0 read assert property ( p_32_rd_32_rd_integrity( monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p0_rd_sel, monitor_mp.p0_rd_addr, monitor_mp.p0_rd_data, monitor_mp.p0_rd_size)) else begin status = new(); status.set_err_rd_rd_integrity(monitor_mp.p0_rd_addr); status_ap.write(status); end a_32_rd_32_rd_integrity_p1: // port 1 read assert property ( p_32_rd_32_rd_integrity( monitor_mp.p0_wr_en, monitor_mp.p0_wr_addr, monitor_mp.p0_wr_data, monitor_mp.p1_wr_en , monitor_mp.p1_wr_addr, monitor_mp.p1_wr_data, monitor_mp.p1_rd_sel, monitor_mp.p1_rd_addr, monitor_mp.p1_rd_data, monitor_mp.p1_rd_size)) else begin status = new(); status.set_err_rd_rd_integrity(monitor_mp.p1_rd_addr); status_ap.write(status); end
Assertion 8-8, “Register file output stable when not selected” demonstrates our next property. Assertion 8-8
Register file output stable when not selected
property p_rd_stable (rd_sel, rd_data); @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) (!rd_sel) |-> (rd_data == $past(rd_data)); endproperty a_rd_stable_p0 : assert property (p_rd_stable(monitor_mp.p0_rd_sel, monitor_mp.p0_rd_data)) else begin status = new(); status.set_err_rd_stable(0); status_ap.write(status); end a_rd_stable_p1 : assert property (p_rd_stable(monitor_mp.p1_rd_sel, monitor_mp.p1_rd_data)) else begin status = new(); status.set_err_rd_stable(1); status_ap.write(status); end
Chapter 8, “Datapath”
197
8.1.5 Encapsulate properties In this step, we turn our set of related multiport register file 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-3
Encapsulate properties inside a module
import avm_pkg::*; import tb_tr_pkg::*; // tb_reg_file_status class definition module tb_register_file_mon ( interface.monitor_mp monitor_mp ); avm_analysis_port #(tb_reg_file_status) status_ap = new(“status_ap”, null); avm_analysis_port #(tb_reg_file_coverage) coverage_ap = new(“coverage_ap”, null); tb_reg_file_status status; . . . // Any required modeling code here (to model environment) . . . // All assertions and coverage properties here . . . endmodule
8.2 Data queue A queue is a buffer data structure type of design component that provides data transportation services to its various entities, such as data or control commands that are stored unmodified for later processing. Probably the most wellknown form of a queue is the first-in-first-out (FIFO) queue. In a FIFO queue, the first element placed in the queue will be the first one out. This is equivalent to the requirement that whenever an element is added, all elements that were added before have to be removed before the new element
198 Creating Assertion-Based IP
can be accessed. There are also non-FIFO queue data structures, such as a priority queue. Queues are typically verified separately from other design components to ensure their proper operation. Their interfaces are generally simple and can be easily isolated from their driving logic. Queues generally are fixed in depth, though they can be parameterized to support various depths. Dynamically resized queues, such as an elasticity buffer, have recently become popular—providing a means to smooth out or balance the flow from the burst of data within a system. 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 various complex queues. We ask you to focus on the techniques for creating the assertions and verification IP. By using our generic, simple, eight-entry queue, we hope that you will be able to set aside the details of a particular larger complex queue 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 more complex queues.
8.2.1 Block diagram interface description Figure 8-7 illustrates a block diagram for a simple eightentry queue design. For our example, all signal transitions relate only to the rising edge of the clock (clk). Table 8-3 provides a summary of the interface signals for our simple eight-entry queue design example.
Chapter 8, “Datapath”
199
Figure 8-7
A simple eight-entry queue block diagram
datain[15:0]
dataout[15:0]
push
queue_full
pop
queue_empty
clear[7:0]
Queue
entry_valid[7:0]
rst_n clk
Table 8-3 Interface signals for our simple eight-entry queue
Name datain[15:0] push pop clear[7:0] dataout[15:0] queue_full queue_empty entry_valid[7:0] rst_n clk
Description Input data to the queue Command to enqueue an entry into the queue Command to dequeue entry from queue Setting bit active high clears a queue entry (younger entries shift to occupy cleared entries) Data value for dequeued entry All entries in the queue are full All entries in the queue are empty Indicates valid entries (rhs bit represents oldest entry while lhs bits represent youngest entries) Asynchronous active low reset Synchronous clock
8.2.2 Overview description Our simple eight-entry queue consists of a 16-bit input data port with an enqueue (push) signal, an indicator for when the queue is full, a 16-bit output port with a dequeue (pop) signal, and an indicator for when the queue is empty. One unique aspect of our queue is the ability to remove an entry from the middle of the queue using the clear entry command.
200 Creating Assertion-Based IP
Basic enqueue and dequeue operation The waveforms in Figure 8-8 illustrate a typical enqueue (push) and dequeue (pop) operation for our simple queue. Our simple eight-entry queue is initially empty, which is indicated by both the active queue_empty signal and the entry_valid signals set to zero and the dataout value is set to zero. Prior to clock two, a D0 value is placed on the datain bus, and the push signal is asserted. This illustrates an enqueue operation. The first (right-hand side) bit of entry_valid is set (after clock two) to indicate that there is a single item in the queue, and the dataout value now represents the first item placed in the queue. Figure 8-8
Enqueue and dequeue operation 0
1
datain[15:0]
2 D0
3 D1
4 D2
5 D3
push pop dataout[15:0] clear[7:0] entry_valid[7:0]
16'b0
D0
D1
8'b00000001
8'b00000001
8'b0 8'b00000000
8'b00000011
queue_empty queue_full
Prior to clock three, a D1 value is placed on the datain bus, and the push signal is asserted, which again illustrates an enqueue operation. At the same time, a pop signal is asserted, which illustrates a simultaneous dequeue operation. After clock three, the end item in the queue (D1) appears on the dataout bus and remains stable until some time in the future when either it is cleared or another queue entry is dequeued. Notice after clock three, since we just performed a simultaneous enqueue and dequeue operation, the entry_valid signals indicate only one item is in the Chapter 8, “Datapath”
201
queue (which represents the D1 value that was just enqueued) Prior to clock four, a D2 value is placed on the datain bus, and the push signal is asserted to perform an enqueue operation. The entry_valid signals after clock four now indicate that there are two valid items in the queue (since each bit represents a valid entry item for our simple queue). As new enqueue operations occur, additional entry_valid bits will be set in a contiguous fashion from right to left. That is, the oldest enqueued entry will be represented by the right-hand-most set bit while the youngest entry is represented by the left-hand-most set bit.
Basic clear operation The waveform in Figure 8-9 illustrates the removal of a previous enqueued entry from the middle of the queue. Figure 8-9
Clear operation t+1
t
t+2
t+3
t+4
t+5
datain[15:0]
push pop dataout[15:0] clear[7:0]
8'b00000000
entry_valid[7:0]
8'b11111111
8'b00000010
D0
D2
8'b00111111
8'b00011111
8'b00000000 8'b01111111
queue_empty queue_full
For our example, we are assuming that the queue has been filled with a sequence of values (D0, D1, D2,...,D7), which is indicated by the active queue_full signal and all bits of
202 Creating Assertion-Based IP
entry_valid being set prior to clock t. queue (D0) appears on the dataout port.
The first item in the
Prior to clock t+2, the clear command is set to 8’b00000010, indicating that the second oldest item in the queue is to be cleared (that is, the previous enqueued value of D1 has been cleared). Notice after clock t+2 that the queue_full signal is no longer active, and the entry_valid indicator signals have shifted right to occupy the previously cleared entry. You can see that there is now one unoccupied entry in the queue. Prior to clock t+3, a dequeue command occurs, and the oldest item (represented as D0) in the queue appears on dataout after clock t+3. The entry_valid value now shifts to the right to indicate that there are two available queue entries. Prior to clock t+4, another dequeue command occurs. This time, and the oldest item in the queue appears on dataout. For our example, we illustrate this item as D2, since D1 was previously cleared on clock t+2. Notice that entry_valid now indicates that there are three available queue entries after the previous dequeue command on clock t+4.
8.2.3 Natural language properties Prior to creating a set of SystemVerilog assertions for our simple queue design, we must identify a comprehensive list of natural language properties, as shown in Table 8-4. 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-4 Simple queue design properties Assertion name
Summary
a_reset_state
Initial state after reset is empty and no entries are valid
Chapter 8, “Datapath”
203
Assertion name
Summary
a_no_overflow
A push cannot occur when the queue is full When all bits of entry_queue are set, then queue_full must be asserted A pop cannot occur for an empty queue Either entry_queue has at least one bit set, or queue_empty is asserted
a_queue_full a_no_underflow a_queue_empty a_push_pop_clear_entry_valid
a_contiguous_entry_valid a_no_invalid_entry_clear a_stable_dataout a_data_integrity
A push, pop, and clear operation should set entry_valid to the correct number of entries in the queue The entry_valid bits are always contiguous and shifted to the right A clear bit should not be asserted for an invalid entry The output data shall not change in the next cycle when pop is not asserted A datum pushed into the queue and not cleared should exit the queue after the prior entries are popped or cleared
8.2.4 Assertions To create a set of SystemVerilog assertions for our simple queue 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-10 illustrates.
204 Creating Assertion-Based IP
Figure 8-10
SystemVerilog interface
Driver
DUV
AssertionBased Monitor
Example 8-4 on page 205 demonstrated an interface for our queue example. For this case, our assertion-based monitor references the interface signals with the direction defined by the monitor_mp named modport. .
Example 8-4
Encapsulate signals inside an interface
interface tb_queue_if( input clk , input rst ); parameter int DATA_SIZE = 16; parameter int ENTRY_SIZE = 8; bit bit bit bit bit bit bit bit
[DATA_SIZE-1:0]
[ENTRY_SIZE-1:0] [DATA_SIZE-1:0] [ENTRY_SIZE-1:0]
datain; push; queue_full; queue_empty; entry_valid; dataout; pop; clear;
modport queue_mp ( . . . ); . . . modport monitor_mp ( input clk , rst_n , input datain , input push , input queue_full , input queue_empty , input entry_valid , // Continued on next page
Chapter 8, “Datapath”
205
Example 8-4 input input input );
Encapsulate signals inside an interface dataout , pop , clear
endinterface
In addition to pin-level interfaces, we need a means for our simple queue design 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-11 illustrates. In addition to assertion error status, coverage events are often an important piece of analysis data that requires its own analysis port, as illustrated in Figure 8-11. Figure 8-11
Error status and coverage analysis ports coverage AssertionBased Monitor
Coverage Collector
error status
Test Controller
Upon detecting an error, the analysis port broadcasts the error condition (using an error status transaction object) to other verification components. The error status transaction class is defined in Example 8-5, which is an extension of avm_transaction base class. The tb_queue_status class includes an enum that identifies the various types of queue errors and a set of methods to uniquely identify the specific error it detected.
206 Creating Assertion-Based IP
Example 8-5
Error status class
class tb_queue_status extends avm_transaction; typedef enum { ERR_RESET_STATE, ERR_NO_OVERFLOW, ERR_QUEUE_FULL, ERR_NO_UNDERFLOW, ERR_QUEUE_EMPTY, ERR_PUSH_POP_CLEAR_ENTRY_VALID, ERR_CONTIGUOUS_ENTRY_VALID, ERR_NO_INVALID_ENTRY_CLEAR, ERR_STABLE_DATAOUT, ERR_DATA_INTEGRITY } queue_status_t; queue_status_t
queue_status;
function void set_err_reset_state; queue_status = ERR_RESET_STATE ; endfunction function void set_err_no_overflow; queue_status = ERR_NO_OVERFLOW ; endfunction function void set_err_queue_full; queue_status = ERR_QUEUE_FULL; endfunction function void set_err_no_underflow; queue_status = ERR_NO_UNDERFLOW ; endfunction function void set_err_queue_empty; queue_status = ERR_QUEUE_EMPTY; endfunction function void set_err_push_pop_clear_entry_valid; queue_status = ERR_PUSH_POP_CLEAR_ENTRY_VALID ; endfunction function void set_err_contiguous_entry_valid; queue_status = ERR_CONTIGUOUS_ENTRY_VALID ; endfunction function void set_err_no_invalid_entry_clear; queue_status = ERR_NO_INVALID_ENTRY_CLEAR ; endfunction function void set_err_stable_dataout; queue_status = ERR_STABLE_DATAOUT ; endfunction function void set_err_data_integrity; queue_status = ERR_DATA_INTEGRITY ; endfunction . . . endclass
Chapter 8, “Datapath”
207
We are now ready to write assertions for our simple queue design properties defined in Table 8-4 on page 203. Assertion 8-9, “Queue inactive after reset” demonstrates our first property. Assertion 8-9
Queue inactive after reset
property p_reset_state; @(posedge monitor_mp.clk) $rose(monitor_mp.rst_n) |-> (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
Assertion 8-13, “No cleared invalid entry” demonstrates our next property. Assertion 8-13
No cleared invalid entry
property p_no_invalid_entry_clear; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) |(monitor_mp.clear) |-> |(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 <= 0; r_push_tag <= 0; end else begin if (pop) r_pop_tag <= r_pop_tag+1; if (push) r_push_tag <= r_push_tag+1; end end // Example property for simple queue that doesn’t support clear! property p_data_integrity; bit [15:0] lv_datain; bit [2:0] lv_tag; @(posedge monitor_mp.clk) disable iff (~monitor_mp.rst_n) (monitor_mp.push, lv_datain=datain, lv_tag=r_push_tag) |-> (monitor_mp.pop && r_pop_tag == lv_tag)[->1]) ##1 (monitor_mp.dataout == lv_datain); endproperty
Assertion 8-15, data queue integrity, is an example of a property that is difficult to express with SVA constructs alone. For example, to express this property, we need a means to uniquely (and globally) identify entries as they are being enqueued (push) so we can identify these entries when they are dequeued (pop). Hence, we must introduce additional modeling code that assigns a tag to each enqueued entry. We accomplish this unique identification by globally keeping track of push and pop occurrences. However, as demonstrated in Assertion 8-15, this requires modeling outside our property definition. For our example, when an entry is enqueued, the current value of the modeling code push tag (r_push_tag) is assigned to the local variable (lv_tag) within a specific property thread of evaluation. In addition, the queue data entry value (datain) is sampled into a local variable (lv_datain), which is used for a future dequeue comparison. Once an entry is dequeued, and the value of the current modeling code pop tag (r_pop_tag) for the dequeue 212 Creating Assertion-Based IP
operation matches the thread’s local variable (lv_tag), then the queue output data (dataout) is checked against the expected value previously captured in the lv_datain local variable. This simple example illustrates how additional modeling code is required in some circumstances to express a complex data integrity class of properties. Additional, and more complex modeling code would be required to express the same property if we expanded our example to include support for the queue clearing capabilities (that is, clear[7:0]), like our original queue example. 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]. Explicit versus implicit queue
In this section, we demonstrated the process of creating assertion-based IP for an explicit queue. However, when verifying end-to-end behavior of a block with our property set, we must often describe an implicit queue behavior of the block—without knowing any specific details about the queue implementation. That is, the block might perform a specific function (not necessarily an explicit queue block), and from an end-to-end perspective the behavior possesses certain queuing properties (that is, an implicit queue). Properties describing a block’s implicit queue behavior are generally intended to verify data integrity. Hence, assertions are not always the most efficient means of specifying and verifying these implicit queue behaviors for simulation.
8.2.5 Encapsulate properties In this step, we turn our set of related queue assertions (and any coverage properties) into a reusable assertion-based monitor verification component, as demonstrated in Chapter 8, “Datapath”
213
Example 8-6 (see page 214). We add analysis ports to our monitor for communication with other simulation verification components, as Chapter 3 “The Process” demonstrates. Example 8-6
Encapsulate properties inside a module
import avm_pkg::*; import tb_tr_pkg::*; // tb_queue_status class definition module tb_queue_mon ( interface.monitor_mp monitor_mp ); avm_analysis_port #(tb_queue_status) status_ap = new(“status_ap”, null); avm_analysis_port #(tb_coverage) coverage_ap = new(“coverage_ap”, null); tb_queue_status status; . . . // Any required modeling code here (to model environment) . . . // All assertions and coverage properties here . . . endmodule
8.3 Data error correction Error correction is the process of detecting bit errors during data transfer—and we can correct these bits in either software or hardware. Figure 8-12 illustrates a high-level error correcting datapath flow that includes an error correcting code (ECC) generator and checker. Figure 8-12
High-level error correcting datapath flow data_out
data_in
ECC Generator
data_in + ECC
214 Creating Assertion-Based IP
ECC Checker
error
For high data rates, error correction must be done in specialpurpose hardware because software is too slow. The ECC bits are generally computed by an algorithm that is implemented as a set of exclusive OR trees in hardware. For our discussion, an ECC generator block computes the ECC. Each data bit contributes to more than one ECC bit. Hence, by a careful selection of data bits in the algorithm that directly contribute to the calculation for a specific ECC bit, it is not only possible to detect a single-bit error, but actually identify which bit is in error (including the ECC bits). In fact, the computed ECC is usually designed so that all single-bit errors are corrected, while all double-bit errors are detected (but not corrected). For our discussion, an ECC checker recomputes the ECC from the transmitted data bits. The output of the recomputed ECC network is called a syndrome. If the syndrome is zero, no error occurred. If the syndrome is non-zero, it can be used to index into a look-up table1 to determine exactly which bit is in error and then correct it. For a multi-bit error, no match occurs in the lookup table, and the error output bit is set in our high-level datapath flow illustrated in Figure 812. ECC falls under the classification of data transformation, and in general, they pose difficulties for formal verification. However, Richards [2003] demonstrates a clever way of using assumptions and restrictions to obtain a proof across an ECC generator and checker block. Since our focus in creating assertion-based IP is to capture the intent of the design in a set of assertions suitable for simulation, and then only optimize the assertions for formal verification later if necessary, we have not limited our discussion to written assertions that are only amenable to formal verification. Therefore, in this section we demonstrate our assertionbased IP creation process on a single ECC checker block. You will note in this section that we treat the ECC checker as a black-box (that is, no detailed knowledge of the ECC checker implementation is required).
1. The lookup table could be implemented in hardware, firmware, or software. Chapter 8, “Datapath”
215
8.3.1 Block diagram interface description Figure 8-13 illustrates a block diagram for a simple ECC checker design component. For our example, all signal transitions relate only to the rising edge of the clock (clk). Table 8-5 provides a summary of the interface signals for our simple ECC checker design example. Figure 8-13. ECC checker block mem_data[35:0]
corr_data[127:0]
mem_valid
corr_valid
mem_last rst_n
ECC Checker
clk
errdetect[6:0] sb_err db_err
Table 8-5 Interface signals of the ECC checker block.
Name mem_data[35:0] mem_valid mem_last corr_data[127:0] corr_valid errdetect[6:0] sb_err db_err rst_n clk
Description Memory data input beat Active high memory data is valid Active high last (of 4) memory data beats Corrected (possible) data from memory Active high valid correct data Bit error location identifier Active high single bit err Active high double bit err Active low asynchronous reset Rising edge synchronous clock
8.3.2 Overview description Our simple ECC checker is designed to accept a 128-bit input (that is, 128-bit data and 16-bit ECC), which is time multiplexed transferred over four beats of 36-bit slices. Figure 8-14 illustrates a four-beat time multiplexed transfer waveform for our simple ECC checker. 216 Creating Assertion-Based IP
Figure 8-14
Four-beat multiplexed transfer to ECC checker 0
1
2
3
4
5
mem_valid mem_data[35:0]
D0
D1
D2
D3
mem_last corr_valid corr_data[127:0]
Data
errdetect[6:0]
Error Bit
sb_err db_err
Our ECC checker accepts a 36-bit slice of input data from mem_data when the mem_valid signal is asserted. The last beat of the four-beat transfer occurs when mem_last input signal
is asserted. The ECC checker will then reconstruct the 128bit data word and 16-bit ECC on the last transfer from the four-beat, 36-bit slices. Then, the ECC checker will detect and correct any single-bit errors, or detect (and not correct) any double-bit errors. When the data is ready for output, the ECC checker asserts the corr_valid output signal. If a single-bit error was detected, then the ECC checker asserts the sb_err output signal and identifies the appropriate failing bit on the errdetect bus. However, if a double-bit error occurred during the data transfer, the db_err output signal is asserted. Our simple ECC checker has a requirement that the output and errdetect buses must retain their previous values (that is, remain stable) when corr_valid is inactive. corr_data
Chapter 8, “Datapath”
217
8.3.3 Natural language properties Prior to creating a set of SystemVerilog assertions for our simple ECC checker, we must identify a comprehensive list of natural language properties, as shown in Table 8-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. Table 8-6 Simple ECC checker properties Assertion name
Summary
a_state_reset_inactive
The corr_valid, sb_err, and db_err are inactive after reset The last indicator is only asserted every 4 valid input transfers Corrected data does not change values after corr_valid is deasserted until next
a_legal_last_valid a_corr_data_stable
corr_valid
a_errdetect_stable
errdetect
does not change after corr_valid is deasserted until next corr_valid.
a_data_correct
If not double-bit error, then the output data must not be corrupted
8.3.4 Assertions To create a set of SystemVerilog assertions for our simple ECC checker, 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-15 illustrates.
218 Creating Assertion-Based IP
Figure 8-15
SystemVerilog interface
Driver
DUV
AssertionBased Monitor
Example 8-7 demonstrates an interface for our example. For this case, our assertion-based monitor references the interface signals via the direction defined by the monitor_mp named modport. .
Example 8-7
Encapsulate signals inside an interface
interface tb_ecc_if( input clk , input rst_n ); parameter int BEAT_SIZE = 36; parameter int DATA_SIZE = 128; parameter int ERR_SIZE = 7; bit [BEAT_SIZE-1:0] mem_data; bit mem_valid; bit mem_last; bit [DATA_SIZE-1:0] corr_data; bit corr_valid; bit [ERR_SIZE-1:0] errdetect; bit sb_err; bit db_err; modport ecc_gen_mp ( . . . . ); modport ecc_ck_mp ( . . . . ); // Coninuted on next page
Chapter 8, “Datapath”
219
Example 8-7
Encapsulate signals inside an interface
modport monitor_mp ( input clk , rst_n , input mem_data , input mem_valid input mem_last , input corr_data , input corr_valid , input errdetect , input sb_err , input db_err ); endinterface
In addition to pin-level interfaces, we need a means for our ECC checker 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-16 illustrates. Figure 8-16
Error status and coverage analysis ports coverage AssertionBased Monitor
Coverage Collector
error status
Test Controller
In addition to assertion error status, coverage events are generally an important piece of analysis data that requires its own analysis port if your assertion monitor contains coverage detection. Upon detecting an ECC checker error, the analysis port broadcasts the error condition (using an error status transaction object) to other verification components. 220 Creating Assertion-Based IP
The error status transaction class is defined in Example 8-8, which is an extension of an avm_transaction base class. The tb_ecc_status class includes an enum that identifies the various types of ECC checker errors, and a set of methods to uniquely identify the specific error it detected. Example 8-8
Error status class
class tb_ecc_status extends avm_transaction; typedef enum { ERR_STATE_RESET_INACTIVE, ERR_LEGAL_LAST_VALID, ERR_CORR_DATA_STABLE, ERR_ERRDETECT_STABLE, ERR_DATA_CORRECT } ecc_status_t; ecc_status_t
ecc_status;
function void set_err_state_reset_inactive; ecc_status = ERR_STATE_RESET_INACTIVE ; endfunction function void set_err_legal_last_valid; ecc_status = ERR_LEGAL_LAST_VALID ; endfunction function void set_err_corr_data_stable; ecc_status = ERR_CORR_DATA_STABLE; endfunction function void set_err_errdetect_stable; ecc_status = ERR_ERRDETECT_STABLE; endfunction function void set_err_data_correct; ecc_status = ERR_DATA_CORRECT; endfunction . . . endclass
We are now ready to write assertions for our simple ECC checker properties defined in Table 8-6. Assertion 8-16, “ECC checker inactive after reset” demonstrates our first property.
Chapter 8, “Datapath”
221
Assertion 8-16
ECC checker inactive after reset
property p_state_reset_inactive; @(posedge monitor_mp.clk) $rose(monitor_mp.rst_n) |-> (monitor_mp.db_err==1’b0) & (monitor_mp.sb_err==1’b0) & (monitor_mp.corr_valid==1’b0) & (monitor_mp.errdetect==7'b0) & (monitor_mp.corr_data[127:0]==128'b0); endproperty a_state_reset_inactive : assert property (p_state_reset_inactive) else begin status = new(); status.set_err_state_reset_inactive(); status_ap.write(status); end
Assertion 8-17, “ECC checker legal mem_last behavior” demonstrates our second property. We have partitioned this property into two assertions: •
Check the boundary condition after reset to ensure no spurious occurrence of mem_last
•
Check normal operation to ensure proper sequencing of mem_last
Assertion 8-17
ECC checker legal mem_last behavior
// Forbid a mem_last after reset and before a four-beat transfer property p_legal_last_valid_init; @(posedge monitor_mp.clk) $rose(monitor_mp.rst_n) |=> (~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<=burst_count; j++) begin // Keep same transaction type and burst count // but randomize data and address within burst count loop t.data = $random % 256 ; t.addr = $random % 256 ; $cast( m_temp , t.clone() ); blocking_put_port.put( m_temp ); t.decrement_burst_count(); end end endtask endclass
B.1.11 tb_driver class The tb_driver class is responsible for converting an untimed transaction into the appropriate timed pin activity defined by the nonpipelined bus protocol. Example B-11
tb_driver class
class tb_driver extends avm_threaded_component; typedef enum { INACTIVE, START, ACTIVE, ERROR } tb_driver_state_e; virtual pins_if #( .DATA_SIZE( 8 ), .ADDR_SIZE ( 8 ) ) m_bus_if; // Continued on next page
Appendix B, “Complete OVM/AVM Testbench Example”
293
Example B-11
tb_driver class
avm_blocking_put_export #( tb_transaction ) blocking_put_export; local tlm_fifo #( tb_transaction ) p_fifo; local tb_driver_state_e m_state; local tb_transaction m_transaction; // Generate cycle accurate bus controls for protocol function new( string name , avm_named_component parent ); super.new( name , parent ); p_fifo = new(“fifo” , this ); m_state = INACTIVE; blocking_put_export = new(“blocking_put_export”, this); set_report_verbosity_level( 400 ); endfunction function void export_connections; blocking_put_export.connect ( p_fifo.blocking_put_export ); endfunction task run; string m_trans_str; m_bus_if.driver_mp.en = 0; m_bus_if.driver_mp.sel = 0; forever begin @( posedge m_bus_if.driver_mp.clk ); if( m_bus_if.driver_mp.rst == 1 ) begin m_bus_if.driver_mp.en <= 0; m_bus_if.driver_mp.sel <= 0; m_state = INACTIVE; end else begin // Continued on next page
294 Creating Assertion-Based IP
Example B-11
tb_driver class
// Conceptual state-machine to // emulate bus protocol activity case( m_state ) INACTIVE : begin // Get stimulus generator output if( p_fifo.try_get ( m_transaction ) ) begin // If not idle, set bus controls to transition to a START state m_bus_if.driver_mp.write <= 0; m_bus_if.driver_mp.en <= 0; if ( ~m_transaction.is_idle() ) begin m_bus_if.driver_mp.sel <= 1; m_state = START; end else begin m_bus_if.driver_mp.sel <= 0; m_state = INACTIVE; end m_bus_if.driver_mp.addr
<= m_transaction.get_addr() ;
// Set bus controls for write if ( m_transaction.is_write() ) begin m_bus_if.driver_mp.write <= 1; m_bus_if.driver_mp.wdata <= m_transaction.get_data() ; end end else begin avm_report_error (“DRIVER reqest_fifo.try_get failed” , “”); end end // INACTIVE START : begin // Set bus controls to transition to an ACTIVE state m_bus_if.driver_mp.sel <= 1; m_bus_if.driver_mp.en <= 1; m_state = ACTIVE; // Continued on next page
Appendix B, “Complete OVM/AVM Testbench Example”
295
Example B-11
tb_driver class
if (m_bus_if.driver_mp.write) begin avm_report_message(“DRIVER sending “ , m_transaction.convert2string ); end end // START ACTIVE : begin if (~m_bus_if.driver_mp.write) begin $sformat( m_trans_str , “Bus type = READ , data = %d , addr = %d” , m_bus_if.driver_mp.rdata , m_bus_if.driver_mp.addr ); avm_report_message(“DRIVER receiving ” , m_trans_str ); end // Determine if burst is done m_bus_if.driver_mp.en <= 0; if( m_transaction.is_done() ) begin // Set bus controls to transition to an INACTIVE state m_bus_if.driver_mp.sel <= 0; m_state = INACTIVE; end else begin // Set bus controls to transition to a START state for burst m_bus_if.driver_mp.sel <= 1; m_state = START; // Get next transaction if( p_fifo.try_get( m_transaction ) ) begin m_bus_if.driver_mp.addr <= m_transaction.get_addr() ; // Set bus controls for write // Continued on next page
296 Creating Assertion-Based IP
Example B-11
tb_driver class if ( m_transaction.is_write() ) begin m_bus_if.driver_mp.write <= 1; m_bus_if.driver_mp.wdata <= m_transaction.get_data()
; end else begin m_bus_if.driver_mp.write <= 0; end end else begin avm_report_error(“DRIVER reqest_fifo.try_get failed”, “”); end end end // ACTIVE endcase end end endtask endclass
B.1.12 tb_responder class The tb_responder class emulates the DUV and responds to bus activity initiated from the tb_driver, as illustrated in Figure B-1. .
Example B-12
tb_responder class
class tb_responder extends avm_threaded_component; virtual pins_if #( .DATA_SIZE( 8 ), .ADDR_SIZE( 8 ) ) p_bus_if; function new( string name , avm_named_component parent = null ); super.new( name , parent ); set_report_verbosity_level( 400 ); endfunction // Continued on next page
Appendix B, “Complete OVM/AVM Testbench Example”
297
Example B-12
tb_responder class
task run; // Used to decode bus control signals localparam localparam localparam localparam
INACTIVE START ACTIVE ERROR
= = = =
2’b00; 2’b10; 2’b11; 2’b01;
string s_trans_str; // Evaluate cycle accurate bus controls for protocol forever begin @( posedge p_bus_if.responder_mp.clk ); if (p_bus_if.responder_mp.rst) continue; // Conceptual state-machine to decode bus protocol transitions case( {p_bus_if.responder_mp.sel,p_bus_if.responder_mp.en} ) INACTIVE : begin end // INACTIVE START : begin // If read transaction, get generator output for transaction if (~p_bus_if.responder_mp.write) begin // Get stimulus generator output for read transaction p_bus_if.responder_mp.rdata = $random % 256; $sformat( s_trans_str , “Bus type = READ , data = %d , addr = %d” , p_bus_if.responder_mp.rdata, p_bus_if.responder_mp.addr); avm_report_message(“RESPONDER sending s_trans_str ); end end // START
“ ,
ACTIVE : begin // If write transaction, print data and addr sent from driver // Continued on next page
298 Creating Assertion-Based IP
Example B-12
tb_responder class
if (p_bus_if.responder_mp.write) begin $sformat( s_trans_str , “Bus type = WRITE , data = %d , addr = %d” , p_bus_if.responder_mp.wdata, p_bus_if.responder_mp.addr); avm_report_message(“RESPONDER receiving “ , s_trans_str ); end end // ACTIVE endcase end endtask endclass
B.1.13 tb_coverage class The tb_coverage class is the coverage collector, which receives transactions detected by our assertion-based monitor as illustrated in Figure B-1, and uses a SystemVerilog covergroup construct to measure various bus burst lengths. Example B-13
tb_coverage class
class tb_coverage extends avm_threaded_component; bit
p_is_covered;
local bit [4:0] tsize; local bit ttype; local tb_transaction t; covergroup p_size_cov; csize : coverpoint tsize; ctype : coverpoint ttype; endgroup analysis_fifo #(tb_transaction) af; analysis_if #(tb_transaction) analysis_export; // Continued on next page
Appendix B, “Complete OVM/AVM Testbench Example”
299
Example B-13
tb_coverage class
function new( string name , avm_named_component parent = null ); super.new( name , parent ); p_size_cov = new; tsize = 0; p_is_covered = 0; set_report_verbosity_level( 4 ); af = new(“trans_fifo”,this); endfunction function void export_connections; analysis_export = af.analysis_export; endfunction task run; forever begin af.get(t); this.avm_report_message (“COLLECTOR receiving “, t.convert2string); ttype = t.is_write(); tsize++; // If done bursting (burst_count == 1), // sample final burst size and type if (t.get_burst_count() == 1) begin p_size_cov.sample; if( p_size_cov.get_inst_coverage > 75 ) begin p_is_covered = 1; end tsize = 0; end end endtask function void report; string report_str; $sformat( report_str , “%d percent covered” , p_size_cov.get_inst_coverage ); this.avm_report_message( “coverage report” , report_str ); endfunction endclass
300 Creating Assertion-Based IP
B.2 OVM/AVM high-level reference guide This section contains a high-level reference guide, which provides an overview description for many of the potentially less obvious OVM/AVM classes used in our complete testbench example. For a comprehensive list of all OVM/ AVM classes, we suggest you reference the OVM/AVM Encyclopedia appendix in [Glasser et al., 2007], which documents all of the classes in the OVM/AVM library. For each class, the OVM/AVM Encyclopedia provides a description of the class and what it is used for along with a listing of all the members and methods.1 avm_env extends avm_named_component A subclass of avm_env is the top-level class in any classbased AVM verification environment. All the components of the testbench are children of this toplevel class. avm_named_component extends avm_report_client This is the fundamental building block of the AVM. All avm_env, structural classes (for example, avm_threaded_component, avm_random_stimulus, and so forth) inherit from avm_named_component. avm_random_stimulus #(type trans_type=avm_transaction) extends avm_named_component
This is a general purpose unidirectional random stimulus generator. It is a useful component in its own right, but can also be used as a template to define other stimulus generators or extended to add stimulus generation methods to simplify test writing. The avm_random_stimulus class generates streams of trans_type transactions. These streams may be generated by the randomize() method of trans_type, 1. The OVM is a superset of the 3.0 version of the AVM. The names in this appendix are discussed in terms of avm_* prefix, which are backwards compatible, versus the ovm_* names that are being proposed at the time of this writing.
Appendix B, “Complete OVM/AVM Testbench Example”
301
or the randomize() method of one of its subclasses, depending on the type of the argument passed into the generate_stimulus() method. The stream may go indefinitely until terminated by a call to stop_stimulus_generation(), or you may specify the maximum number of transactions to be generated. avm_threaded_component extends avm_named_component
A
threaded
component
inherits
from
avm_named_component and adds the ability to spawn a run() task at the beginning of the simulation.
avm_*_export #(type T=int) extends avm_port_base #(tlm_*_if #(T))
An avm_*_export is a connector that provides interfaces to other components. It gets these interfaces by connecting to an avm_*_export or avm_*_imp in a child component. avm_*_port #(type T=int) extends avm_port_base #(tlm_*_if #(T))
An avm_*_port is a connector that requires interfaces be supplied to it. It may get these interfaces by connecting to a parent’s avm_*_port or an avm_*_export or avm_*_imp in a sibling. avm_analysis_port #(type T=int) extends avm_port_base #(analysis_if #(T)) avm_analysis_port is used by a component such as a monitor to publish a transaction to zero, one, or more subscribers. Typically, it will be used inside a monitor to publish a transaction observed on a bus to scoreboards and coverage objects.
avm_port_base#(type IF=avm_virtual_class) extends IF avm_port_base is the base class for all ports, exports, and implementations (avm_*_port, avm_*_export, and avm_*_imp). avm_port_base extends IF, which is the
type of the interface required or provided by the port, export, or implementation.
302 Creating Assertion-Based IP
analysis_fifo #(type T=int) extends tlm_fifo #(T) An analysis_fifo is a tlm_fifo with an unbounded size and a write() interface. It can be used any place an avm_subscriber is used. Typical usage is as a buffer between an analysis_port in a monitor and an analysis component (that is, a component derived from avm_subscriber). tlm_fifo #(type T=int) extends avm_named_component is a FIFO that unidirectional TLM interfaces.
tlm_fifo
implements
all
the
analysis_if #(type T=int) The analysis interface is a nonblocking, non-negotiable, unidirectional interface. It is typically used to transfer a transaction from a monitor, which cannot block, to a scoreboard or coverage object. avm_transaction This is the base class for all AVM transactions.
Appendix B, “Complete OVM/AVM Testbench Example”
303
BIBLIOGRAPHY [Accellera OVL 2007] Accellera Standard Open Verification Library Reference Manual, 2007. [Alur and Henzinger 1998] R. Alur, T. Henzinger, “Finitary fairness,” ACM Trans. Program. Lang. Syst., 20, 6 Nov. 1998. [AMBA 1999]
ARM, AMBA specification version 2.0, May 1999.
[Bailey et al., 2005] B. Bailey, G. Martin, T. Anderson, Taxonomies for the Development and Verification of Digital Systems, Springer, 2005. [Bailey et al., 2007] B. Bailey, G. Martin, A. Piziali, ESL Design and Verification: A Prescription for Electronic System Level Methodology, Morgan Kaufmann, 2007. [Bergeron et al., 2006] J. Bergeron, E. Cerny, A. Hunter, A. Nightingale, Verification Methodology Manual for SystemVerilog, Springer, 2006. [Chattterjee 2005] P. Chattterjee, “Streamline Verification Process with Formal Property Verification to Meet Highly Compressed Design Cycle,” Proc. Design Automation Conference, 2005. [Cohen 2005] B. Cohen, S. Venkataramanan, A. Kumar, SystemVerilog Assertions Handbook...for Formal and Dynamic Verification, VhdlCohen Publishing, 2005. [Dasgupta 2006] P. Dasgupta, A Roadmap for Formal Property Verification, Springer 2006. [Ecker et al., 2006] W., Ecker, V. Esen, M. Hull, “Execution semantics and formalisms for multi-abstraction TLM assertions,” Proc. MEMOCODE, 2006. [Eisner and Fisman 2006] Springer, 2006. [eRM 2005]
C. Eisner, D. Fisman, A Practical Introduction to PSL,
Scalable Testbench White Paper, Cadence Design Systems, 2005.
[Foster and Coelho 2001] H. Foster, C. Coelho, “Assertions Targeting A Diverse Set of Verification Tools,” Proc. Intn’l HDL Conference, March, 2001. [Foster et al., 2004] H. Foster, A. Krolnik, D. Lacey, Assertion-Based Design, 2nd Edition, Kluwer Academic Publishers, 2004. [Foster et al., 2006a] H. Foster, K. Larsen, M. Turpin, “Introducing The New Accellera Open Verification Library Standard,” Proc. DVCon, 2006.
Bibliography
305
[Foster et al., 2006b] H. Foster, L. Loh, B. Rabii, V. Singhal, “Guidelines for creating a formal verification testplan,” Proc. DVCON, 2006. [Glasser et al., 2007] M. Glasser, A. Rose, T. Fitzpatrick, D. Rich, H. Foster, The Verification Cookbook: Advanced Verification Methodology (AVM), Mentor Graphics Corp, 2007. http://www.mentor.com/go/cookbook. [Haque et al., 2006] F. Haque, J. Michelson, K. Khan, The Art of Verification with SystemVerilog Assertions, Verification Central, 2006. [I2C 2000] I2C Bus Specification, version 2.1, January 2000, http:// www.semiconductors.philips.com/buses/i2c. [IEEE 1800-2005] IEEE Standard 1800-2005 SystemVerilog: Unified Hardware Design, Specification and Verification Language, IEEE, Inc., New York, NY, USA, 2005 [IEEE 1850-2005] IEEE Standard 1850-2005 Property Specification Language (PSL), IEEE, Inc., New York, NY, USA, 2005. [ITRS 2003] The International Technology Roadmap for Semiconductors, 2003 Edition, Retrieved July 5, 2007 from the World Wide Web: http:// www.itrs.net/Links/2003ITRS/Home2003.htm. [Kariniemi and Nurmi 2005] H. Kariniemi and J. Nurmi, Arbitration and Routing Schemes for on-Chip Packet Networks, Springer, 2005, http:// www.tkt.cs.tut.fi/kurssit/9636/K05/Chapter18.pdf. [Keating and Bricaud 2002] M. Keating and P. Bricaud, Reuse Methodology Manual, Kluwer Academic Publishers, 2002. [Long et al., 2007] J. Long, A, Seawright, H. Foster, “SVA Local Variable Coding Guidelines for Effective Use,” Proc. DVCon, 2007. [Marschner 2002] E. Marschner, B. Deadman, G. Martin, “IP Reuse Hardening via Embedded Sugar Assertions,” Proc. International Workshop on IP-Based SoC Design, 2002. [Martin 2002] G. Martin, “UML for embedded systems specification and design: motivation and overview,” Proc. Design, Automation and Test in Europe, 2002. [OCP 2003] Open Core Protocol Specification 2.0, Document Revision 1.1, Part number: 161-000125-0002, 2003, www.ocpip.org. [Piziali 2004] A. Piziali, Functional Verification Coverage Measurement and Analysis, Kluwer Academic Publishers, 2004. [Richards 2003] J. Richards, “Creative assertion and constraint methods for formal design verification,” Proceedings of DVCon, 2003. [Ruah et al., 2005] S. Ruah, A. Fedeli, C. Eisner, “Property-Driven Specification of VLSI design,” Property Based System Design (PROSYD) methodology document FP6-IST-507219, 2005.
306 Creating Assertion-Based IP
[Susantol and Melham 2003] K. W. Susanto1, T. Melham, “An AMBA-ARM7 Formal Verification Platform,” Lecture Notes in Computer Science, Springer, 2003. [Vijayaraghavan and Ramanathan 2005] S. Vijayaraghavan, M. Ramanathan, A Practical Guide for SystemVerilog Assertion, Springer, 2005. [Wilcox 2004] P. Wilcox, Professional Verification: A Guide to Advanced Functional Verification, Kluwer Academic Publishers, 2004.
Bibliography
307
Index Symbols $ 260 $assertion 272 $assertkill 272 $assertoff 272 $asserton 272 $countones 270 $display 271 $error 271 $fatal 271 $fell 269 $info 272 $isunknown 270 $onehot 270 $onehot0 270 $past 269 $rose 269 $sampled 269 $warning 271 [*m:n] 260 [*n] 260 [=n] 262 [->n] 261 |=> 268 |-> 268
A abstraction 31 Accellera Open Verification Library (OVL) 11, 13 PSL 11 SVA 11 Verilog 11 Advanced High-performance Bus (AHB) 35, 63, 90 Advanced Peripheral Bus (APB) 35, 75
Advanced Verification Methodology (AVM) 20, 35, 41, 47, 51, 57 assertion-based IP 20 AMBA 35, 63, 75, 76, 90 analysis port 30, 50, 51, 52, 54, 69, 70, 71, 72, 83, 86, 99, 109, 138, 142, 162, 258 broadcasting data 30 publisher 30 subscriber 30, 51 analysis_fifo class 303 analysis_if class 303 arbiter 31, 90, 113 credit registers 125 credits 125 grant without pending request 121 interleaving request 132 latency 125 minimum latency 119 mutually exclusive grants 118 natural language properties 136 round-robin 117, 125 specific properties 117 arbitration schemes 114 fair 115, 128 fixed priority 122 variable dynamic priority 128 variable weighted priority 125, 127 weighted-priority 125 assertion coverage 31 assertion-based IP 1, 4, 7, 19, 31, 40 academic instructor 14 action 38, 57, 112 ad hoc process 7 analysis interface 24 analysis port 24, 30, 50, 51, 52, 69, 70, 71, 72, 83, 86, 99, 109, 138, 142, 162, 258
Index
309
architect 14 architecture 41 clarity xvi, 2, 38 coverage 108, 112 designer 14 detection 38, 57, 112 development philosophy 14 engineering manager 14 formal verification 14 guidelines and conventions 57 interface-based 53 libraries 11 modularity xvi, 2, 38 module versus interface assertions 41 module-based 44 motivation 3, 17 novice 14 optimization 14 process 13, 37, 39 provider 15 reuse 1, 11, 17 separating detection from action xvi, 2 simulation 14 systematic process 7, 9 systematic set of steps 37 systematic steps 39 terminology 31 verification engineer 14 assertion-based monitor 38 assertion-based verification (ABV) 1 libraries 11 methodology 11 ad hoc 11 reuse 38 assertions 31 black-box 6, 14 definition 9 design engineer xv, 5 end-to-end 6 implementation-level xv, 4 naming convention 57 verification engineer xv, 5 white-box 6, 14 assumption 31 avm_*_export class definition of 302 avm_*_port class definition of 302 avm_analysis_port class 52, 302 310
Creating Assertion-Based IP
avm_env definition of 301 avm_named_component class definition of 301 avm_port_base class 302 avm_random_stimulus class 301 avm_threaded_component class definition of 302 avm_transaction class 69, 83, 100, 138, 163, 303
B behavior 31 black box 32 bus functional model (BFM) 32, 35 bus-based design 59, 60, 61
C C or C++ model 7 channel class 303 tlm_fifo 303 class-based component 49 communication channel 23 component classes avm_env 301 avm_named_component 301 avm_random_stimulus 301 avm_threaded_component 302 conceptual state-machine 77, 152, 168, 169 implicit vs. explicit modeling 175 connector classes avm_*_export 302 avm_*_port 302 avm_analysis_port 52, 302 avm_port_base 302 consequent 268 constraint 32 controllers 32 memory 145 corner case 32 coverage 32, 108 coverage model 32 coverage property 55
D datapath 32 design component 32
design IP 59 design refinement process 7 blocks 8 bus cycle-accurate (BCA) 7 modules 8 RTL model 8 system function model 7 transaction-level model (TLM) 7 units 8 DUV 35 dynamic verification 33
E EDA xv, 4, 13, 35 elasticity buffer 199 environment class 49, 56 constructor 48 eRM 20, 35
F FIFO 35 fixed priority 122 formal verification 3, 14 FSM 35
H HDL 35 HVL 36
I IEEE Std 1800-2005 SystemVerilog SystemVerilog Assertions (SVA) 10, 36, 253 action block 257 action blocks 13 concurrent assertions 254, 255, 256 consecutive repetition 260 goto repetition 261 immediate assertions 254 implication 268 infinite range 260 keyword ## 258 and 264 assert 273
assume 273 cover 273 disable iff 256 ended 267 first match 267 intersect 265 or 265 property 255, 263 sequence 263 throughout 266 within 266 local variable 110 local variables 273 repetition nonconsecutive 261 sequence intersection 265 sequences 258 task and functions $assertkill 272 $assertoff 272 $asserton 272 $countones 270 $error 271 $fatal 271 $fell 269 $info 272 $isunknown 270 $onehot 270 $onehot0 270 $past 269 $rose 269 $sampled 269 $warning 271 temporal delay 258 IEEE Std 1850 Property Specification Language (PSL) xv, 4, 10, 36, 274 implication 268 Intellectual Property (IP) 8, 33, 36 industry guidelines 9 provider 15 quality 9 reuse 8 interfaces 63 nonpipelined bus 63, 75 basic read operation 80 basic write operation 78 natural language properties 81, 203, 218, 228, 242
Index
311
pipelined bus 63, 90 serial bus 63, 64 natural language properties. 67 Inter-Integrated Circuit (I2C) Bus 36, 64 International Technology Roadmap for Semiconductors (ITRS) 8
R requirement 34 reuse 70, 72 RTL 36
S
memory controller 146 burst read cycle 148 natural language properties 158 single read cycle 148 single write cycle 148 model 33 modularity 27 module-based transactor 44
SDRAM 36, 147, 148, 151, 152, 154, 165, 173 DDR 146 SDR 146 SoC 36, 59, 60, 61, 63 split transaction 90 static verification 34 SystemVerilog Assertions (SVA) xv, 4 SystemVerilog interface 37, 45, 48, 54, 55, 56, 68, 82 class-based reference 47 instantiation 46 modport 45, 46, 47, 68, 83, 99, 136, 161 virtual 49 SystemVerilog module-based assertion monitor 37
O
T
L libraries encapsulate property set 11 linear-time temporal logic 253 linear-time temporal operators 274
M
Open Core Protocol (OCP) 63 Open Verification Library (OVL) 36 Open Verification Methodology (OVM) 30, 36
P parameterized class 52 platform-based design 33, 63 properties 6 architecture 6 definition 9 micro-architectural 6 natural language 7, 8 property 34 encapsulation 89 property encapsulation 75, 108, 141, 173 protocol 34 protocol checker 34
Q queue explicit 213 implicit 213 312
Creating Assertion-Based IP
testbench architecture analysis layer 28 control layer 28 environment layer 29 transactors layer 29 TLM 36 TLM interface classes analysis_if 303 tlm_fifo class 303 transaction 34 transaction-level model (TLM) 7 untimed 8 transactions policy classes AVM transactions, base class for 69, 83, 100, 138, 163, 303
V verification stakeholders architect 6 design engineer 6 verification engineer 6
verification components xvi, 2, 10, 19, 20, 34, 38 analysis interface 24 analysis port 24, 50 channel 23 class-based 37 class-based versus module-based 42 communication channel 19 coverage collector 25, 51, 56 driver 26 export 22 interconnect 22 master 25 modularity 27 module-based 37 monitor 26 organization 26 analysis 29 concentric testbench 26 hierarchical testbench 27 operational 29
port 22 responder 26 reuse 51 scenario generator 25 scoreboard 25, 51 simulation controller 25 slave 26 stimulus generator 25 transaction interface 23 transaction port 24 Verification Intellectual Property (VIP) 1, 10, 35, 36, 40 declarative forms 4 imperative forms 3 synthesizable 3 VMM 20, 36
W white box 35
Index
313
Continued from page ii Leakage in Nanometer CMOS Technologies Siva G. Narendra and Anantha Chandrakasan ISBN 978-0-387-25737-2, 2005 Statistical Analysis and Optimization for VLSI: Timing and Power Ashish Srivastava, Dennis Sylvester, and David Blaauw ISBN 978-0-387-26049-9, 2005