know_your_objects - oop for hardware designers
DESCRIPTION
Object oriented Programming for HARDWARETRANSCRIPT
Know Your Objects
OOP for Hardware Designers
Rev. 1.0
ALDEC Webinar
Created and Presented by
Jerry Kaczynski,
ALDEC Research Engineer
www.aldec.com
Agenda • Introduction: Raising Abstraction Level
• Benefits of Encapsulation
• Why We Need Inheritance
• That Mysterious Polymorphism
• Decoding Dynamic Dispatch
• Summary/Q&A
2
www.aldec.com
RAISING ABSTRACTION LEVEL
Introduction:
3
www.aldec.com
Abstraction In Design Domain • When first digital Integrated Circuits were designed,
there was no need to raise abstraction level: small number of transistors needed to implement 4 NAND gates could be drawn on a piece of paper.
• Programming PLDs at the transistor level was not feasible: schematics and other gate level design tools were created.
• Schematics for CPLDs/FPGAs were getting too complicated, so RTL and behavioral descriptions were needed.
• Complete systems that can be squeezed into modern circuits require even higher levels of abstraction: transaction or even pure algorithmic.
4
www.aldec.com
Abstraction In Program Domain • First computers were programmed by flipping switches representing
bits of registers – no abstraction whatsoever…
• Assembly languages replaced bit combinations with easier to comprehend mnemonics. More abstract, but still tied to the particular machine.
• High-level languages abstracted both control (procedures, functions, etc.) and data (vectors, arrays, records, etc.)
• Object Oriented Programming (OOP) tied abstracted control and data together, providing powerful tool for designing large programs/systems.
5
www.aldec.com
Abstraction in HDLs • Classical Hardware Description Languages (HDLs) – VHDL and Verilog –
are operating at the procedural level to let you design at RTL or behavioral level.
• New, large digital systems require at least Transaction Level Modeling, which is easiest to implement in OOP.
• The penalty for using high abstraction level is inability to control minute details of implementation.
• Abstract models are gradually refined down to RTL/gate level, where they can be fine-tuned and implemented.
6
ALGORITHM
TLM
RTL
GATE
www.aldec.com
BENEFITS OF ENCAPSULATION
Bundling Things In Your Design:
7
www.aldec.com
Old Style • Classical languages (BASIC, C, Verilog, VHDL) abstract control and data
separately:
Subroutines (procedures/tasks and functions) describe what program can do.
Data types (arrays, records/structures) define data that can be manipulated by subroutines.
• In large designs that extensively use language resources this separation often leads to confusion: Can I use to_integer function with STD_LOGIC_VECTOR argument?
Can I read only low byte of memory word?
• Wouldn’t it be nice if data and operations you can perform on it were tied together?
8
www.aldec.com
VHDL Protected Types • VHDL has a mechanism for encapsulating data (in bundles resembling
records) and procedures/functions operating on them in protected types.
• Protected types have structure similar to packages: Public subprograms and data fields are listed in protected type declaration.
Private data fields/subprograms and implementations of public subprograms are listed in protected type body.
• To use protected type, you have to declare shared variable of that type.
• To do anything with shared variable, you use notation similar to accessing record fields: shared_variable_name.member_name.
• Note: This feature is available in the language since 2002 version.
9
www.aldec.com
VHDL ‘OOP’ Counter
type base_counter is protected procedure clear; procedure count; impure function show return integer; end protected base_counter; type base_counter is protected body variable contents : integer := -7; procedure clear is begin contents := 0; end procedure clear; procedure count is begin contents := contents + 1; end procedure count; impure function show return integer is begin return contents; end function show; end protected body base_counter;
shared variable my_counter : base_counter; . . . -- 'Untimed' use my_counter.clear; OUTPUT <= my_counter.show; . . . my_counter.count; OUTPUT <= my_counter.show; . . . -- 'Refined' use process (CLK, RESET) begin if RESET then my_counter.clear; elsif CLK'event and CLK='1' then my_counter.count; end if; end process;
10
www.aldec.com
Is it enough? • Protected type approach to bundling data and functions works pretty
well, but there is some room for improvements…
To enhance existing protected type, you have to modify its source code: reuse is not flexible enough.
Shared variables are static, so all you can do with them is decided at the compilation time.
• Some general-purpose languages (C++, Java) and SystemVerilog support classes and objects – the staple of OOP:
You can create child class that inherits everything from the original class.
Objects are created in dynamically allocated memory at runtime.
11
www.aldec.com
Classes and Objects • Class is an abstract idea of grouping together: Some pieces of data called attributes (or member variables or
properties),
Subroutines that operate on those pieces of data: methods (a.k.a. member functions).
• Based on the idea of class, we can build actual objects (can you see the analogy with types and variables?)
• When we declare object of a certain class in our program, a handle (or pointer) is created – not actual object.
• In the program code we have to call constructor method of the class to allocate memory for the object and assign initial values to its properties.
12
Class
Object
www.aldec.com
SystemVerilog ‘OOP’ Counter
package oop; class Base_Counter; int contents; function new(); contents = -7; endfunction task clear(); contents = 0; endtask task count(); contents += 1; endtask function int show(); return contents; endfunction endclass endpackage
module top; timeprecision 100ps; timeunit 1ns; import oop::*; Base_Counter my_counter = new; initial begin $display($stime, my_counter.show); #10 my_counter.clear; $display($stime, my_counter.show); repeat(10) begin #10 my_counter.count; $display($stime, my_counter.show); end end endmodule
13
www.aldec.com
Encapsulation? • One of the main reasons to use classes and objects is
to achieve encapsulation:
Bundling of data (attributes) and procedures operating on it (methods).
Shielding attributes from direct access by end users.
• Some scholars prefer only first or second feature of encapsulation mentioned above; we should be aware of both.
• Different languages assume different default level of access to class members:
If, like in C++, members are private by default (accessible only within its class), you can make them public by adding public qualifier in the declaration.
If members are public by default (like in SystemVerilog) you can change
it by using local qualifier (in SV only; C++ uses private qualifier).
14
www.aldec.com
Introducing UML • Universal Modeling Language (UML) is a popular graphical
representation of classes and their relationships.
• In UML class diagram a box represents the class:
Top section names the class.
Middle section lists class attributes.
Bottom section describes class methods.
• Level of member description detail varies: in general diagrams only member names are listed (no data types, argument names, etc.)
• Non-default access to the member may be marked by:
+ in front of the name of public member.
– in front of the name of private/local member.
15
www.aldec.com
WHY WE NEED INHERITANCE
Improving Reuse:
16
www.aldec.com
Smart Reuse • Let’s say that we want to add “load” functionality to our Base_Counter
class.
• In procedural languages we would have to:
Copy description of the original counter.
Add “load” functionality.
Save modified description as a new counter.
• This approach works as long as Base_Counter never changes:
If somebody changes Base_Counter description but forgets to modify new counter accordingly – we have a problem…
• Inheritance – very important feature of OOP – can solve this problem…
17
www.aldec.com
Inheritance • We can use inheritance to create new class (called derived class or child
class) based on original class (called base class or parent class).
• Inheritance creates “is-a” relationship: new class objects are also old class objects and can replace them in the program code.
• UML class diagram represents inheritance by ‘empty triangle arrow’ pointing from the new to the old class.
• Only changes to the old class contents are listed in the new class box: everything else is … inherited.
• Arrow points in the direction of generalization and against the direction of specialization.
• In our diagram, new Load_Counter class specializes old Base_Counter class (which is more general of the two).
18
www.aldec.com
SystemVerilog Child Counter Class
package oop; class Base_Counter; int contents; function new(); contents = -7; endfunction task clear(); contents = 0; endtask task count(); contents += 1; endtask function int show(); return contents; endfunction endclass
class Load_Counter extends Base_Counter; task load(input int val); contents = val; endtask endclass
endpackage
module top; timeprecision 100ps; timeunit 1ns; import oop::*; Load_Counter my_counter = new; initial begin $display($stime, my_counter.show); #10 my_counter.clear; $display($stime, my_counter.show); repeat(5) begin #10 my_counter.count; $display($stime, my_counter.show); end #10 my_counter.load(777); $display($stime, my_counter.show); end endmodule
19
We have extended base class functionality by adding method.
www.aldec.com
Another Flavor of Inheritance • Our Load_Counter demonstrated how derived class can add members,
but it is not the only way…
• Child class can also override methods of the parent class by providing their new definitions.
• Let’s say that we want to crate newer class that still has ‘load’ functionality, but is also parameterized with maximal count value.
• This time we will have to write new count() method that rolls back to zero after reaching maximal count.
• Updated UML class diagram of our class hierarchy is presented to the right.
20
www.aldec.com
Another Child Counter Class
package oop; class Base_Counter; int contents; function new(); contents = -7; endfunction task clear(); contents = 0; endtask task count(); contents += 1; endtask function int show(); return contents; endfunction endclass
class Load_Counter extends Base_Counter; task load(input int val); contents = val; endtask endclass >>>>>>>>
class Super_Counter #(parameter max = 31) extends Load_Counter; task count(); if (contents == max) contents = 0; else contents += 1; endtask endclass
endpackage
21
We have modified base class functionality by overriding method.
www.aldec.com
Side Comment: Aggregation • Inheritance is the preferred method of building new classes if further
extensions are planned.
• If we perform final assembly of our design (testbench or top-level model), we can use aggregation.
• Aggregation instantiates lower level class objects inside the container class.
• UML class diagrams represent aggregation using lines with empty diamonds at the container class end:
22
www.aldec.com
THAT MYSTERIOUS POLYMORPHISM
Making Smart Decisions:
23
www.aldec.com
Manipulating Handles • All examples of code we have seen so far were calling class constructor
to build object. Address of the object returned by the constructor was stored in the handle, which was used only to access object attributes and methods.
• You can pass objects around by making assignments of one handle to another. It makes sense since you can create multiple objects dynamically…
• OOP always lets you assign object of child class to the handle of parent class type. Such assignment triggers automatic upcasting of the object to the parent class type.
• So assigning Super_Counter object to Base_Counter handle is absolutely legal in our project…
24
www.aldec.com
Overridden Method Problem • Let’s do some handle passing:
Super_Counter #(15) better_counter = new; Base_Counter count_handle; count_handle = better_counter;
• Now we have Super_Counter object accessible via
Base_Counter variable. We have two implementations of count() method in class hierarchy. What will happen if we call this method like this: count_handle.count;
• The answer is: implementation of overridden method is selected based on the handle type, not object type!
• It means we have a problem: Base_Counter::count()
that is called does not know how to roll back...
25
www.aldec.com
Virtual Methods • You can declare methods as virtual.
• When you override virtual method in a child class, implementation created for that class will stick with the object no matter what is the type of the handle!
• That type of behavior is called polymorphism and is one of the key features of OOP.
• Let’s try it in our class hierarchy:
We have to make count() virtual.
We also have to display some control messages from virtual and non-virtual methods so we can see who is calling whom…
26
www.aldec.com
Counter Class with Virtual Method
package oop; class Base_Counter; int contents; function new(); contents = -7; endfunction task clear(); contents = 0; $display( "Base_Counter::clear was executed! \n"); endtask virtual task count(); contents += 1; $display( "Base_Counter::count was executed! \n"); endtask function int show(); return contents; endfunction endclass
class Load_Counter extends Base_Counter; task load(input int val); contents = val; endtask endclass >>>>>>>>
class Super_Counter #(parameter max = 31) extends Load_Counter; task clear(); contents = 0; $display( "Super_Counter::clear was executed! \n"); endtask
virtual task count(); if (contents == max) contents = 0; else contents += 1; $display( "Super_Counter::count was executed! \n"); endtask endclass
endpackage
27
www.aldec.com
Using Virtual Methods – TB // Here’s the snippet of testbench using our class with virtual method: Super_Counter #(15) better_counter = new; Base_Counter simple_counter = new; Base_Counter count_handle; initial begin count_handle = better_counter; $display("Calling non-virtual 'clear' from 'Base_Counter' object with 'Base_Counter' handle..."); simple_counter.clear; $display("Calling non-virtual 'clear' from 'Super_Counter' object with 'Base_Counter' handle..."); count_handle.clear; #10; $display("Calling virtual 'count' from 'Base_Counter' object with 'Base_Counter' handle..."); simple_counter.count; $display("Calling virtual 'count' from 'Super_Counter' object with 'Base_Counter' handle..."); count_handle.count; end
28
www.aldec.com
Using Virtual Methods – Results // Here’s the console dump from simulation of our class with virtual method: # KERNEL: Calling non-virtual 'clear' from 'Base_Counter' object with 'Base_Counter' handle... # KERNEL: Base_Counter::clear was executed! # KERNEL: # KERNEL: Calling non-virtual 'clear' from 'Super_Counter' object with 'Base_Counter' handle... # KERNEL: Base_Counter::clear was executed! # KERNEL: # KERNEL: Calling virtual 'count' from 'Base_Counter' object with 'Base_Counter' handle... # KERNEL: Base_Counter::count was executed! # KERNEL: # KERNEL: Calling virtual 'count' from 'Super_Counter' object with 'Base_Counter' handle... # KERNEL: Super_Counter::count was executed! # KERNEL:
29
www.aldec.com
DECODING DYNAMIC DISPATCH
Making Miracles Happen:
30
www.aldec.com
Things Can Get Complicated • If this presentation is your first encounter with encapsulation,
polymorphism and similar terms, you are justified to think that they are not exactly trivial…
• But wait! Developers of simulators have quite difficult tasks to deal with, too. Especially if they were handling only Verilog or VHDL before.
• If you call a function in classical HDL, compiler knows its location and can insert proper jump in the executable code.
• When compiler encounters virtual method call, compiler usually doesn’t know which implementation to call…
31
www.aldec.com
Dynamic Dispatch • In addition to dynamic memory allocation for objects, simulators
have to deal with runtime binding of method calls to proper implementations.
• This process is called dynamic dispatch or dynamic binding and requires some novel approach to organizing simulation.
• Usually compiler stores virtual method calls in special virtual table. Simulator fills that table with proper implementation addresses as objects are created and updates it as objects are passed around.
• Of course this handling is slower than regular method calls – that’s why methods are not virtual by default.
32
www.aldec.com
Why All The Trouble? • Objects, encapsulation, polymorphism mixed with Transaction
Level Modeling (TLM) let you create very powerful, yet extremely flexible verification environments.
• You probably heard about OVM or UVM – they are such environments! Now you should have a little easier task of understanding them….
33
www.aldec.com
SUMMING UP
34
www.aldec.com
Tools Used • All pieces of code presented in this webinar (VHDL and SystemVerilog)
were tested on Riviera-PRO™ simulator.
• While you can draw UML diagrams by hand, there are free tools that can make this task easier and faster:
Java-based Violet UML Editor runs on any platform and is ideal solution for beginners not familiar with UML. All UML diagrams in this presentation were created in Violet. To download it, google “VioletUML”.
UMLet is a slightly more advanced UML editor. Google its name to download it.
Popular multi-platform graphing tool Dia (http://dia-installer.de/) can create UML diagrams, too.
• Although there are tools that can generate C++ or Java code from the UML diagrams, none of them can generate SystemVerilog code…
35
www.aldec.com
Conclusion • Feel free to contact ALDEC if you need more info about EDA-related topics
and our design creation and verification tools.
36
Download: • Recorded webinars • White papers • Trial versions of our tools
ALDEC Website: http://www.aldec.com Telephone E-mail USA +1-702-990-4400 [email protected] Canada: +1-613-867-8600 [email protected] Europe: +33-6-80-32-60-56 [email protected] Japan: +81-3-5312-1791 [email protected] India: +91-80-32551030 [email protected] China: +86-21-6875-20-30 [email protected] Taiwan: +886-2-26599119 ext. 950 [email protected] Israel: +972-52-2573422 [email protected]