/// LSU EE 4755 -- Fall 2016 -- Digital Design / HDL /// /// Objects and Data Types // Time-stamp: <16 September 2016, 13:12:11 CDT, koppel@dmk-laptop> // Under construction. /// Contents // Identifiers // Objects - Nets and Variables // Integer Data Types // Some Operators // Floating Point Data Types // Net Object Types // Vectors and other Packed Arrays, Unpacked Arrays // Address Ranges in Declarations // Vectored and Scalared Declaration Modifiers // .. Other material under construction /// References // :SV12: IEEE 1800-2012 -- The SystemVerilog Standard // http://standards.ieee.org/getieee/1800/download/1800-2012.pdf // // :BV3: Brown & Vranesic, Fundamentals of Digital Logic with Verilog, 3rd Ed. // The text used in LSU EE 4720, 4730, and 4740. //////////////////////////////////////////////////////////////////////////////// /// Lexical Conventions // :SV12: Chapter 5 /// :Def: Lexical Conventions // Boring details about what characters you can use for // things such as identifiers and whitespace. /// Highlights of SystemVerilog lexical conventions: // // Identifiers (variable names, module names, etc.) are case sensitive. // For example, My_Module and my_module are different. // This is like C/C++, but unlike Fortran and VHDL. // // Whitespace is a non-zero string of spaces, tabs, newlines, formfeeds. // Whitespace rules are similar to C. // Identifiers are used for module names, module instance names, and // variable names. // Identifiers: // First character must be alphabetic or an underscore (_); // Following characters may be alphabetic, digit, underscore, or dollar sign. // Case is significant. (myvar and Myvar are different identifiers) // Maximum length 1024. That's a limit, not a goal. // :Example: // // Identifier Examples (in wire declarations). // module i_am_an_identifier(); uwire this_is_an_identifier; uwire a, A; // Two different variables. uwire a1; uwire icantreadthis; uwire his$is$hard$to$read$too; uwire but_this_I_can_read; uwire _this_works_too; uwire this_is_valid_identifier_but_unless_you_were_skilled_with_a_text_editor_it_would_be_really_difficult_to_use_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; uwire connection1, connection2, And, us, too; ModuleName ModuleInstanceName(connection1,connection2); I_m_also_an_idenfifier me_too(And,us,too); endmodule module ModuleName(output connection1,input connection2); endmodule module I_m_also_an_idenfifier(output a, input b, c); endmodule //////////////////////////////////////////////////////////////////////////////// /// Objects // :SV12: Section 6.5 -- Not for beginners. // :BV3: A.6.1 -- Good description, but pre-SystemVerilog /// Two Kinds of Objects : Nets and Variables /// Nets // // Are used to model connections between devices. // Usually get values by being connected to something ... // ... they are not normally assigned to. // The are the default kind for module ports. /// Variables // // These are like variables in most programming languages. // Usually get a value by being assigned in procedural code. /// It Is Important to Understand Net/Variable Differences // // In most cases an object needs to be either a net or a variable. // // To avoid frustration and wasted time ... // ... make sure that you understand the differences. /// Metaphorical Difference // // A net is like a hose: // It needs to be *connected to* something (a driver) .. // ... that's always feeding it. // // A var is like a cup: // It can be filled, and refilled any number of times. // Filling happens at particular points in time, not constantly. // :Example: // // Quick example of declaring and using nets and variables. module nt_examples(input [7:0] a); var int my_var; wire logic my_net, my_other_net; var logic my_other_var; logic my_other_var_2; // A var object. (Notice var omitted.) wire my_other_net_2; // A net of type logic. (Notice logic omitted.) assign my_net = a[0]; some_mod inst_name(my_other_net,a); initial begin my_var = 3; my_other_var = a[1]; #1; my_var = 4; end // Longer Declarations. // uwire logic [7:0] my_wire; // Declaration of a net of type logic. var logic [7:0] my_var; // Declaration of a variable of type logic. reg [7:0] my_var_v95; // Verilog 1995 equivalent of my_var; uwire integer my_w_int; // Declaration of a net of type integer; var integer my_v_int; // Declaration of a variable of type integer; // Taking advantage of defaults. // uwire [7:0] my_wire2; // Default data type is logic. logic [7:0] my_var2; // Default object is variable. uwire integer my_w_int2; integer my_v_int2; // Quick Examples of Use // always_comb begin my_var = a; // Okay, can assign to variables. // my_wire = 13; // Error because can't assign wires. end some_module sm( my_wire, my_var ); // Okay to use wire here. endmodule module some_module(output [7:0] a, input [7:0] b); endmodule /// Nets v. Variables // There are many rules about where each can be used. // // But these are easy to remember if you keep in mind that: // // Nets don't "remember" values, they must be connected to something. // // Variables must be assigned in procedural code, not by a module output. //////////////////////////////////////////////////////////////////////////////// /// Integer Data Types /// // :SV12: Section 6.11 // :SV12: Section 6.3.1 Logic Values /// Two-State Integer Types // // Like integers in ordinary programming languages. // Bit values: 0 or 1. // :Keyword: bit - 1 bit unsigned, 2-state. // :Keyword: byte - 8 bit signed, 2-state. // :Keyword: shortint - 16 bit signed, 2-state. // :Keyword: int - 32 bit signed, 2-state. // :Keyword: longint - 64 bit signed, 2-state. // Use these in testbenches and other places not corresponding // directly to hardware, and for intermediate values in arithmetic // expressions. // /// In most cases use int. // // Use bit, byte, and shortint in large arrays ... // ... to save simulator memory. /// Four-State Integer Types // // Used for simulation of hardware. // Bit values: 0, 1, x, z. // :Keyword: logic - user-defined size, unsigned, 4-state // :Keyword: integer - 32-bit, signed, 4-state // :Keyword: time - 64-bit, unsigned, 4-state // Use type logic to represent hardware, except for intermediate // values in arithmetic expressions. // // Type reg (not shown) is sort of a synonym for logic, and was used // in older versions of Verilog. Its name (reg) is misleading, which // is why logic is preferred. // // The integer and time types are also kept for compatibility with // old Verilog. /// Signed and Unsigned Qualifiers. // // Can be used to modify 2- and 4-state integer types. // :Keyword: signed // :Keyword: unsigned // Note: UNLIKE C, qualifier goes after type. (logic signed). // // Note: Whether a type is signed or unsigned ... // ... affects choice of operators ... // ... but does not effect how data is stored. // :Example: // // Use of types in a module that computes the maximum of two signed // numbers. module max_mod ( output logic signed [7:0] max, input logic signed [7:0] a, b ); // Type "logic" is used because we are modeling hardware. // // Qualifier "signed" is used because we want the simulator // and synthesis program to use the correct >= (greater than or // equal to) operator. // always_comb max = a >= b ? a : b; endmodule // Testbench for max module. // module max_demo_good(); /// Max Module Inputs // // Type "logic" is used for a and m because that is the type of // max module's ports. // // Variable kind (as opposed to net) because assigned in procedural code. // logic signed [7:0] a; byte signed b; // Type "byte" works here, but should be avoided. /// Max Module Output // // "Net" kind (uwire) because connected to a module output. // // It is important to use a 4-state type, such as logic, so // that "x" and "z" types are visible. // uwire logic signed [7:0] m; max my_max(m,a,b); // Type "int" is used because there will be no hardware corresponding // to this variable (num_tests). localparam int num_tests = 20; initial begin for ( int i=0; i<num_tests; i++ ) begin // Type "int" used because this does not correspond to hardware. // int shadow_max; a = $random; // Returns a 32-bit signed int type, cast to logic. b = $random; shadow_max = a >= b ? a : b; #10; if ( shadow_max !== m ) $write("Wrong result max(%d,%d) %d != %d (correct)\n", a, b, m, shadow_max); end $display("Done with %d tests.\n",num_tests); end endmodule // :Example: // // Another testbench for the max module, but this has errors due to // forgetting about the signed qualifier. module max_demo_bad(); // Connections to my_max module declared without the "signed" // qualifier. Values will be unsigned. // logic [7:0] a, b; uwire logic [7:0] m; max my_max(m,a,b); // Type "int" is used because there will be no hardware corresponding // to this variable (num_tests). localparam int num_tests = 20; initial begin for ( int i=0; i<num_tests; i++ ) begin // Type "int" used because this does not correspond to hardware. // int shadow_max; a = $random; b = $random; shadow_max = a >= b ? a : b; #10; if ( shadow_max !== m ) $display("Wrong result max(%d,%d) %d != %d (correct)\n", a, b, m, shadow_max); end $display("Done with %d tests.\n",num_tests); end endmodule //////////////////////////////////////////////////////////////////////////////// /// Some Operators // :SV12: Section 11 -- Operators and Expressions Chapter // :SV12: Table 11-1 -- Table showing many operators. /// System Verilog Operators // // Many operators match those in C. /// Cast Operator // // Used to convert one data type to another. // // :Syntax: TYPE'(EXPRESSION) // EXPRESSION is converted to TYPE // C Equivalent: ((TYPE)EXPRESSION) /// Slicing Operators // // :Syntax: ARRAY[ POS +: WIDTH ] // // POS: An integer expression. // WIDTH: A constant integer expression. // // Returns at array of elements: // { ARRAY[POS], ARRAY[POS+1], ..., ARRAY[POS+WIDTH-1] } // // :Syntax: ARRAY[ POS -: WIDTH ] // Similar. //////////////////////////////////////////////////////////////////////////////// /// Floating Point Data Types /// // :SV12: Section 6.12 /// Floating Point Types // // :Keyword: real - C double (usually IEEE 754 double, 64 bits). // :Keyword: shortreal - C float (usually IEEE 754 single, 32 bits). /// Uses // // Use them in testbench code, for example, to compute percentages. // // Cannot be used with net type objects. // // Do not use them when describing hardware ... // ... because synthesis programs (at least now AFAIK) won't accept them. /// Punning Functions // // :Keyword: $realtobits // :Keyword: $shortrealtobits // :Keyword: $bitstoreal // :Keyword: $bitstoshortreal // // The functions above *do not* convert or otherwise change the // underlying data, they simply return the same data as a new data // type. // // To understand the statement above remind yourself of the difference // between a number and the number's many possible representations. // :Example: // // Code showing use of FP types and conversion functions. module fp_examples; // The declaration below would be an error since it's using a net type, // and reals can only be declared as variables. // // uwire real rw; real d, from_dbits, from_dval; logic [63:0] dbits; logic [63:0] dval; initial begin // The 64 bits of d are set to the FP double-precision // representation of 3.4 based on the IEEE 754 standard. // d = 3.4; // Implicit cast of d's value (a real) to dval's type // (logic). The 64 bits of dval are set to 3 in an ordinary // binary representatin. // dval = d; // dval -> 3 // Underlying representation not changed. // dbits = $realtobits( d ); // // Now d and dbits are bit-for-bit identical, both are 3.4 // represented as an IEEE 754 double. $write("d %.20f dval 0x%4h, dbits 0x%h\n", d, dval, dbits); // Display Output: // // d 3.39999999999999991118 dval 0x0003, dbits 0x400b333333333333 dval = dval + 1; // This works because dval has an integer. // The addition below won't work as expected because Verilog // will use integer addition (since the type is logic) while the // underlying representation is floating-point. // dbits = dbits + 1; // Change type back to logic without changing underlying data. // from_dbits = $bitstoreal( dbits ) + 1; // Display Output: // // After +1: from_dbits 3.40000000000000035527 dval 0x0004, dbits 0x400b333333333334 $display("After +1: from_dbits %.20f dval 0x%4h, dbits 0x%h\n", from_dbits, dval, dbits); end endmodule // :Example: // // FP adder module and testbench. The testbench shows how to use FP // types correctly. The adder itself abuses them. (The next example // corrects the problem.) // Adder module, bad because it is probably not synthesizable due to // using floating-point type (shortreal here). // module my_adder_bad(output shortreal sum, input shortreal a, b); assign sum = a + b; endmodule // Testbench. The testbench code is good, it's the adder that's bad. // module real_demo_bad(); // Module inputs and outputs. // shortreal a, b, sum; // Adder is bad because it uses a FP type for arguments. // my_adder_bad a1(sum,a,b); localparam int num_tests = 10; initial begin // Compute 1.0 / 2^{32}, used to prepare a random number // in the range (-0.5,0.5). // real scale; scale = 1.0 / ( longint'(1) << 32 ); // // Note: Expression above uses cast operator: TYPE'(EXPR). // In this case 1 is converted to a long int. for ( int i=0; i<num_tests; i++ ) begin a = {$random} * scale; // Values should be in range [0,1] b = $random * scale; // Values should be in range [-0.5,0.5] #1; $display("Sum is %f + %f = %f\n",a,b,sum); end end endmodule // :Example: // // Example showing FP numbers in synthesizable hardware. // This example uses components from the Cadence ChipWare library. // Adder module using a Cadence-supplied FP adder module. // module my_adder_good(output logic [31:0] sum, input logic [31:0] a, b); // Adder module has a rounding mode input. Set it to 0. // localparam logic [2:0] rounding_mode = 0; // Adder module has a status output. Leave it unconnected. // uwire logic [7:0] status; // Unused. CW_fp_add chip_ware_adder (.a(a), .b (b), .rnd (rounding_mode), .z (sum), .status (status)); endmodule // Testbench for good adder. // module real_demo_good(); logic [31:0] ai, bi; uwire logic [31:0] sumi; my_adder_good a1(sumi,ai,bi); localparam int num_tests = 10; initial begin automatic real scale = 1.0 / ( longint'(1) << 32 ); for ( int i=0; i<num_tests; i++ ) begin shortreal a, b, sum; a = $random * scale; b = $random * scale; // Change type from shortreal to logic. // ai = $shortrealtobits(a); bi = $shortrealtobits(b); #1; // Change type from logic to shortreal. // sum = $bitstoshortreal(sumi); $display("Sum is %f + %f = %f\n",a,b,sum); end end endmodule //////////////////////////////////////////////////////////////////////////////// /// Net Object Types // :SV12: Section 6.6 // Used to interconnect devices. // Do not store values. // Can NOT ordinarily be assigned in procedural code. // Net types are assigned by: // Connecting them to the output of a module (in an instantiation). // Using an assignment statement (to be covered). // Assigning in the declaration. // Some net types: // // :Keyword: uwire: for ordinary connections, single driver. // :Keyword: wire: for ordinary connections, any number of drivers. // :Keyword: tri: for tri-state logic. (E.g., for busses.) // :Keyword: wand: for wired and connections // :Keyword: wor: for wired or connections. // // See :SV12: Table 6-1 for the full list. // // In this class we will just use uwire. module net_demo(output wire x, input wire a,b); wire wire_1; uwire wire_2; logic r1; // Example of a continuous assignment. assign wire_1 = a | b; and a1(wire_2,a,b); // This is a continuous assignment, NOT an initialization. wire wire_3 = wire_1 ^ r1; // Wired and example. wire_5 = (a | b ) & !wire_2 wand wire_5; assign wire_5 = a | b; assign wire_5 = !wire_2; wire wire_4; always @( a or b ) begin r1 = a; // wire_4 = b; // Not allowed. end endmodule //////////////////////////////////////////////////////////////////////////////// /// Vectors and other Packed Arrays, Unpacked Arrays /// // :SV12: 6.9 // :SV12: 7.4 Packed and unpacked arrays. // :Def: Unpacked Array // An ordinary array, can have any number of dimensions and // elements can be of any type. // // Uses: The usual (no different than the uses for arrays in // other languages). // :Def: Vector // A one-dimensional array which as a whole can be treated as an // integer, but which can also be treated as an array of // bits. // // Uses: Modeling a bunch of wires that collectively hold an // integer, or any other collection conveniently treated as a // unit. // :Def: Packed Array // A multi-dimensional array which as a whole can be treated as // an integer, but which can also be treated as a // multidimensional array of (usually) bits. // // Uses: The same as a vector, with more indexing flexibility, // for example, organizing a 32-bit object into 4 8-bit quantities. /// Vectors and Packed Arrays // // Element data type must be: bit, logic, or an enumerated type. // // Dimension sizes declared before the variable name. // // A one dimensional packed array is called a vector. // // :Example: logic [39:0] vector1; // 1D packed, aka, vector. // :Example: logic [9:0][3:0] vector2; // 2D packed. /// Unpacked Arrays // // Any element type, including a packed array. // // Address ranges (dimension sizes) declared after the variable name. // // :Example: real values[100]; // :Example: // // Three ways of declaring a 40-bit variable: as a vector, a 2-dimensional // packed array, and as a 1-dimensional unpacked array. module v_etc(); logic [39:0] vector1; // A 40-bit variable. logic [9:0][3:0] vector2; // A 40-bit variable organized as 10 groups of 4. logic unpacked3[39:0]; // 40 1-bit variables. initial begin // Conveniently assign vector types in one step. // vector1 = 40'h123456789a; vector2 = vector1; /// Can operate on packed array as though it were an integer. // // Operate on entire vector. // vector2 = vector2 + 1; // LSD changes from a to b. // Operate only on a 4-bit slice. // vector2[1] += 3; // Hex digit 1 changes from 9 to c. // Operate on a bit. // vector2[2][0] = 1; // Hex digit 2 changes from 8 to 9. // Not allowed. (Needs a streaming operator, >>) // // unpacked3 = vector1; // Initialize the unpacked array. // for ( int p = 0; p < $bits(vector1); p++ ) unpacked3[p] = vector1[p]; for ( int b = 0; b < 10; b++ ) begin logic [3:0] nib1, nib2, nib3, nib4; // nib is for nibble. nib1 = vector1[ b*4 +: 4 ]; // Bits b*4 to b*4+3. nib2 = vector2[ b ]; nib3 = { unpacked3[ b*4 + 3 ], unpacked3[ b*4 + 2 ], unpacked3[ b*4 + 1 ], unpacked3[ b*4 + 0 ] }; $display ("Nibble %d is %h or %h or %h or %h\n", b, nib1, nib2, nib3, nib4); end end endmodule ////////////////////////////////////////////////////////////////////////////// /// Address Ranges in Declarations /// // /// :Syntax: TYPE ARRAY_NAME[ADDRESS_RANGE]; // // Address range determines valid element numbers for an array. // // Address range also indicates type of array. // /// Possible address ranges: // /// Static Arrays, positive integer. // Synthesizable. // // :Example: int a[4]; // Valid indices: 0, 1, 2, 3 // // :Example: int a[S]; // Valid indices are: 0, 1, ..., S - 1 // // /// Static Arrays: Range. // Synthesizable. // // :Example: int a[10:8]; // Valid indices: 10, 9, 8. // In other words, a[9] is okay, but a[7] is out of range. // // :Syntax: LAST:FIRST // Element indices are FIRST, FIRST+1, ..., LAST // // /// Dynamic Arrays // Not (easily) synthesizable. // // Array size is determined at run time. // // :Syntax: ELEMENT_TYPE ARRAY_NAME[]; // // :Example: int a[]; a = new[size]; // Use new to allocate elts. // Variable size known only at run time. // // See :SV12: Section 7.5 // /// Associative Arrays // Not (easily) synthesizable. // // Index set determined by type. // // :Syntax: ELEMENT_TYPE ARRAY_NAME[INDEX_TYPE]; // // :Example: int a[ string ]; // Index can be any string. // // :Example: int a[ integer ]; // Index can be any integer. // // :Example: int a[ real ]; // Index can be any real number. (Really.) // Unlike other (non-associative) arrays, storage for elements // is allocated as new indices are encountered. // :Example: // // Example of use of different ranges. module range_examples(input int num_elts); logic [7:0] array1[]; // Dynamically sized. logic [7:0] array2[string]; // Associative. (string is a data type) int array3[10]; // Ten elements, indices 0..9 int array4[10:1]; // Ten elements, indices 1..10 // string step[real]; // ALAS, not yet implemented. initial begin array1 = new [ num_elts ]; // Hope that num_elts >= 6. // array1[ 5 ] = 123; array2[ "5" ] = 1; array2[ "five" ] = 2; for ( int i=0; i<10; i++ ) array3[i] = i; // Perfectly fine, same size and type. // // It doesn't matter that index numbering different. // array4 = array3; `ifdef REAL_INDICES step[1] = "Unlock door."; step[2] = "Enter room."; step[3] = "Sit down."; step[1.5] = "Turn off alarm."; `endif end endmodule /// Dynamic Arrays // // :SV12: 7.5 // // :Keyword: new[NUM_ELTS](init) -- // :Keyword: size() -- Member function, return number of elements. // :Keyword: delete() -- Member function, delete all element. /// Associative Arrays // // :SV12: 7.8 // // :Keyword: num() -- Member function, number of elements. // :Keyword: size() -- Member function, number of elements. // :Keyword: delete() -- Member function, delete all entries. // :Keyword: delete(INDEX) -- Member function, delete entry INDEX. // :Keyword: exists(INDEX) -- Member function, return 1 if INDEX in array. /// The foreach Looping Construct // // :Keyword: foreach -- Loop over elements of array or other container. // // :Syntax: foreach ( ARRAY[IVAR] ) STATEMENT // Execute statement for each element of ARRAY setting IVAR to // the element's index. IVAR is a new variable (it should not be declared). // // :Example: foreach ( a[i] ) b[i] = a[i]; // /// Other Topics // // Streaming operators. // { << 4 { packed_expr } } /// Vectored and Scalared Declaration Modifiers // LRM 3.3 (At end of section.) // The *vectored* keyword modifies a net declaration, informing // the compiler that one cannot look at individual bits of a // net. module vector_examples2(); // Range specification examples. wire [0:31] a; // Can examine bits. wire scalared [0:31] b; // Equivalent to above, can examine bits. wire vectored [0:31] c; // Shouldn't examine bits. wire a_is_odd = a[31]; // Works wire b_is_odd = b[31]; // Works wire c_is_odd = c[31]; // Error on some compilers, not Modelsim. endmodule // vector_examples `include "/apps/linux/cadence/RC142/share/synth/lib/chipware/sim/verilog/CW/CW_fp_add.v"