/// LSU EE 4755 -- Fall 2024 -- Digital Design / HDL /// /// Objects and Data Types /// Contents // Identifiers and Whitespace // Objects - Nets and Variables // Integer Data Types // Some Operators // Floating Point Data Types // Net Object Kinds // Vectors and other Packed Arrays, Unpacked Arrays // Address Ranges in Declarations // .. Other material under construction /// References // :SV: IEEE 1800-2023 -- The SystemVerilog Standard // https://ieeexplore.ieee.org/document/10458102 // This is for those already familiar with Verilog. // // :FP: IEEE 754-2013 -- IEEE Floating Point Standard // https://ieeexplore.ieee.org/document/8766229 // // :BV3: Brown & Vranesic, Fundamentals of Digital Logic with Verilog, 3rd Ed. // The text used in LSU EE 2740. // Use this text for review. //////////////////////////////////////////////////////////////////////////////// /// Identifiers and Whitespace // :SV: Chapter 5 /// 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++ and Python, 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 objects. uwire a1; uwire icantreadthis; uwire this$is$hard$to$read$too; uwire but_I_can_read_this; 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; // See: https://xkcd.com/1963/ 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 // :SV23: Section 6.5 -- Not for beginners. // :BV3: A.6.1 -- Good description, but pre-SystemVerilog /// Two Kinds of Objects : Nets and Variables // :Def: Kind // A classification of objects. The two kinds are: nets and variables. /// Nets // // :Sample: uwire [7:0] i_am_a_net; // :Sample: uwire logic [7:0] i_am_a_net; // // Are used to model connections between devices. // // Usually get values by being /driven/, meaning connected to something ... // ... they are not normally assigned to. // // The are the default kind for module ports. // /// Net Kind Types // // :Keywords: uwire,wire,tri,wand,wor,triand,trior,trireg, .. // Declaration of a net kind usually starts with one of these types. // // Some of these are described in detail in the Net Object Kinds section. // // In class the only net kind type we will willingly use is uwire. // Older Verilog and current tools may use wire. // /// Variables // // :Sample: var logic [7:0] i_am_a_variable; // :Sample: logic [7:0] i_am_a_variable; // 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 .. // .. if the wrong kind is used code won't compile. // // 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); // The declarations below include both a kind (var,wire) .. // .. and a type (int,logic). // var int my_var; wire logic my_net, my_other_net; wire logic [7:0] my_8b_net; uwire logic my_safe_net, my_other_safe_net; var logic my_other_var; logic my_other_var_2; // A var object. (Notice var omitted.) uwire my_other_net_2; // A net of type logic. (Notice logic omitted.) assign my_net = a[0]; // This is a driver, driving my_net. // The output of inst_name is a driver, driving my_other_net. some_module inst_name(my_8b_net,a); initial begin // my_net = 1; // Would be an error if not commented out. my_var = 3; // This is an assignment. 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_sv; // 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_other_var_2 = a; // Okay, can assign to variables. // my_wire = 13; // Error because can't assign wires. end some_module sm( my_wire, my_var[7:0] ); // 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. module net_v_var_example #( int w = 1 ) ( output uwire [w-1:0] x, output logic [w-1:0] y, input uwire [w-1:0] a, b, c ); // Notice that x is of kind net (uwire) and y is of kind var. // // So, x should be used for module outputs and continuous assignments, // and y should be assigned in procedural code. `define MISUSE_EXAMPLES `ifndef MISUSE_EXAMPLES // This code is correct. assign x = a + b; always_comb y = b + c; `else // The code below is wrong because a net kind, x, is being assigned // in procedural code. // always_comb x = a + b; // // Error message (xmvlog 24.03, 20 September 2024, 10:39:35 CDT): // // xmvlog: *E,WANOTL (l020-types.v,227|15): A net is not a legal lvalue in this context [9.3.1(IEEE)]. // This should be an error (IMHO) because a var kind, y, is being driven. // assign y = b + c; // // But Cadence xmvlog is fine with it. `endif endmodule //////////////////////////////////////////////////////////////////////////////// /// Integer Data Types /// // :SV: Section 6.11 // :SV: Section 6.3.1 Logic Values /// Four-State Integer Types // // Used for simulation of hardware. // Bit values: 0, 1, x, z. // :Keyword: logic - 1-bit, unsigned, 4-state -- Often used // :Keyword: integer - 32-bit, signed, 4-state -- Legacy, don't use. // :Keyword: time - 64-bit, unsigned, 4-state -- Legacy, don't use. // 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. // /// But, what are they? // // First, if all bits are 0 or 1 (none are x or z), then // the ordinary rules apply, including the rules for integer // arithmetic. // // Second, think about them as arrays, where each array element // is either 0, 1, x, z. Don't think about values as numbers. // Operations are defined on these four values. For example: // 1 & 1 = 1, (0 and 1 work in the usual way) // 1 & x = x, 0 & x = 0, ... // 1 | x = 1, 0 | x = x, ... // :SV: See 11.4.8 for tables defining some operations on 4-bit types. /// Integer Four-State Comparison Operators // // Ordinary comparison: ==, !=, >, >=, <, <= // - If either operand is x in any bit, the result is x. // 1 == 1 = 1 // 0 == 0 = 1 // 0 == x = x // 1 == x = x // x == x = x // // Four-state compare: ===, !==, ==?, !=? // :Keyword: ===,!== // To be equal each element must be identical. // :Example: 2'b0x === 2'b0x is true. // :Example: 2'b0x === 2'b00 is false. // :Example: 2'b0x === 2'b0z is false. // :Example: 2'b0z === 2'b0z is true. // // :Keyword: ==?,!=? // The x and z values act as wildcard (don't care) values. // :Example: 2'b0x ==? 2'b0x is true. // :Example: 2'b0x ==? 2'b00 is true. // :Example: 2'b0x ==? 2'b01 is true. // :Example: 2'b0x ==? 2'b10 is false. /// 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. -- Often used. // :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. /// Signed and Unsigned Qualifiers. // // Can be used to modify 2- and 4-state integer types. // // See max_mod for an example of where correct use of signed and // unsigned is important. // :Keyword: signed // :Keyword: unsigned // :Sample: logic signed [7:0] a; // :Sample: int unsigned b; // 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. module int_types; int a_int, b_int, c_int; bit [31:0] a_bit, b_bit, c_bit; // Similar to int, but unsigned. logic [31:0] a_logic, b_logic, c_logic; initial begin // Bit values are either 0 or 1, no x's or z's here. // Therefore arithmetic and logical operators have their usual meaning. // a_logic = 123; b_logic = 555; c_logic = a_logic + b_logic; $write("Logic types: (shown in decimal)\n %4d\n + %4d\n = %4d\n", a_logic, b_logic, c_logic); $write("Logic types: (shown in hexadecimal)\n %4h\n + %4h\n = %4h\n", a_logic, b_logic, c_logic); $write("Logic types: (shown in binary)\n %16b\n + %16b\n = %16b\n", a_logic, b_logic, c_logic); // Logic types: (shown in decimal) // 123 // + 555 // = 678 // Logic types: (shown in hexadecimal) // 007b // + 022b // = 02a6 // Logic types: (shown in binary) // 0000000001111011 // + 0000001000101011 // = 0000001010100110 // Bits assigned all four values, 0, 1, x, z. // Result of arithmetic is x if any bit is x or z. (Even x*0, x-x.) // Result of bitwise logical operation on a bit // .. is 0 or 1 if x doesn't matter, as in x && 0 = 0. // a_logic = 32'b00001111xxxxzzzz; b_logic = 32'b01xz01xz01xz01xz; c_logic = a_logic + b_logic; $write("Logic types:\n %16b\n + %16b\n = %16b\n", a_logic, b_logic, c_logic); // // 00001111xxxxzzzz a_logic // + 01xz01xz01xz01xz b_logic // = xxxxxxxxxxxxxxxx c_logic: All bits x because of arithmetic op. c_logic = a_logic & b_logic; $write("Logic types:\n %16b\n & %16b\n = %16b\n", a_logic, b_logic, c_logic); // // 00001111xxxxzzzz a_logic // & 01xz01xz01xz01xz b_logic // = 000001xx0xxx0xxx c_logic: "& 0" operation changes x or z to 0. c_logic = a_logic | b_logic; $write("Logic types:\n %16b\n | %16b\n = %16b\n", a_logic, b_logic, c_logic); // // 00001111xxxxzzzz a_logic // | 01xz01xz01xz01xz b_logic // = 01xx1111x1xxx1xx c_logic: "| 1" operation changes x or z to 1. // With two-state types, x and z in literals are changed to 0. // a_int = 32'b00001111xxxxzzzz; b_int = 32'b01xz01xz01xz01xz; c_int = a_int + b_int; $write("Int types:\n %16b\n + %16b\n = %16b\n", a_int, b_int, c_int); // // 0000111100000000 a_int // + 0100010001000100 b_int // = 0101001101000100 c_int = a_int & b_int; $write("Int types:\n %16b\n & %16b\n = %16b\n", a_int, b_int, c_int); // // 0000111100000000 // & 0100010001000100 // = 0000010000000000 c_int = a_int | b_int; $write("Int types:\n %16b\n | %16b\n = %16b\n", a_int, b_int, c_int); // // 0000111100000000 // | 0100010001000100 // = 0100111101000100 end endmodule // :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_mod 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; // // It would also be okay to make shadow_max logic signed [7:0]. a = $random; // Returns a 32-bit signed int type, cast to logic. b = $random; shadow_max = a >= b ? a : b; #1; if ( shadow_max !== m ) $write("Wrong result max(%d,%d) %d != %d (correct)\n", a, b, m, shadow_max); // // Note: use of !== instead of != is important here .. // .. because !== returns either true (correct) or false (wrong) .. // .. whereas != might return x due to an x or z in m. end $write("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_mod 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 ) $write("Different result max(%d,%d) %d != %d (correct)\n", a, b, m, shadow_max); end $write("Done with %d tests.\n",num_tests); end endmodule //////////////////////////////////////////////////////////////////////////////// /// Vectors and other Packed Arrays, Unpacked Arrays /// // :SV: 6.9 // :SV: 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. // // :Sample: logic x[10]; // :Sample: logic y[9:0]; // // 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. // // :Sample: logic [9:0] y; // // 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. // // :Sample: logic [19:0] y; // y is 20 bits // :Sample: logic [9:0][1:0] w; // w is 20 bits (10 * 2). // // 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. // // :Sample: logic [39:0] vector1; // 1D packed, aka, vector. // :Sample: 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. // // :Sample: 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 4b. logic unpacked3[39:0]; // 40 1-bit variables. initial begin // Conveniently assign vector types in one step. // vector1 = 40'h123456789a; vector2 = vector1; unpacked3[0] = 1'b0; unpacked3[1] = 1'b1; unpacked3[2] = 1'b0; // ... twenty minutes later .. unpacked3[39] = 1'b0; /// Can operate on packed array as though it were an integer. // // Operate on entire vector. // vector2 += 1; // LSD changes from a to b. // Operate only on a 4-bit slice. // vector2[0] += 1; // Hex digit 0 changes from a to b. 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. (Could copy with 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. nib2 = vector2[ b ]; nib1 = vector1[ b*4 +: 4 ]; // Bits b*4 to b*4+3. nib3 = { unpacked3[ b*4 + 3 ], unpacked3[ b*4 + 2 ], unpacked3[ b*4 + 1 ], unpacked3[ b*4 + 0 ] }; $write ("Nibble %d is %h or %h or %h or %h\n", b, nib1, nib2, nib3, nib4); end end /// Examples of both packed and unpacked dimensions. // // c e a b logic [9:0][3:0] vector_2d_2d[19:0][2:0]; logic [9:0][3:0] vector_2d_1d [2:0]; logic [9:0][3:0] vector_2d; logic [3:0] nib; logic b; initial begin // a vector_2d_1d = vector_2d_2d[11]; // Evaluates to 3-element unpacked. // a b vector_2d_2d[11][2] = 40'habcd; // Write a 40-bit quantity. // a b vector_2d = vector_2d_2d[11][1]; // a b c nib = vector_2d_2d[11][1][9]; // Evaluates to 4-bit vector. // a b c e b = vector_2d_2d[11][1][9][2]; // Evaluates to one bit. vector_2d_1d[2] = 40'hef01; end endmodule //////////////////////////////////////////////////////////////////////////////// /// Some More Operators // :SV: Section 11 -- Operators and Expressions Chapter // :SV: 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) // :Sample: real'(an_int+5) // // :Syntax: WID'(EXPRESSION) // WID is a positive constant integer expression // EXPRESSION is a packed vector. // The width of EXPRESSION is changed to WID. // :Sample: 10'(some_12_bit_packed_vector) // :Syntax: signed'(EXPRESSION) // :Syntax: unsigned'(EXPRESSION) // EXPRESSION is an integer type. (bit, logic, shortint, ...) // The signedness of EXPRESSION is changed as indicated. // /// Note: // // Don't forget the parentheses around EXPRESSION, they are part // of the cast operator. // :Example: // // Use the cast operator to widen the a+b expression to 8 bits so that // it will not overflow. module less_than ( output uwire lt, input uwire [6:0] a, b, amt ); assign lt = 8'(a + b) < amt; endmodule /// Slicing Operators // // :Syntax: ARRAY[ MSB_IDX : LSB_IDX ] // :Syntax: ARRAY[ POS +: WIDTH ] // :Sample: ARRAY[ POS+WIDTH-1 : POS ] // // 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. //////////////////////////////////////////////////////////////////////////////// /// Binary Floating-Point Representation and Arithmetic // /// Binary Floating-Point (FP) Representations // // The floating-point (FP) representations in this section (before // IEEE 754) are NOT computer representations. // // Among other things, that means the number of bits needed to store a // number is not specified. // // Computer representations for FP numbers covered in the next section, // IEEE 754. /// Binary Fixed Point Representation // // // Each digit position has a multiplier. // // // FiP Binary Number: 1 0 1 0 1. 1 0 0 1 // Digit Position: 4 3 2 1 0 -1 -2 -3 -4 // Multiplier: 16 8 4 2 1 1/2 1/4 1/8 1/16 // // Value of number: 1*16 + 0*8 + 1*4 + 0*2 + 1*1 + 1/2 + 0/4 + 0/8 + 1/16 // = 21.5625 // // Other Examples: // // 1.1 = 1.5 // 1.01 = 1.25 // 1.11 = 1.75 // 1.001 = 1.125 // 1111.1111 = 15.9375 // // // Fixed Point Decimal to Binary Conversion // // To convert decimal number x, 0 < x < 1. // // Method 1: // // For digit position -1: // if x >= 1/2, digit is 1, x = x - 1/2; // if x < 1/2, digit is 0, x unchanged. // For digit position -2: // if x >= 1/4, digit is 1, x = x - 1/4 // if x < 1/4, digit is 0, x unchanged. // Etc. // // Method 2: // // Let r be the number of digits past decimal point desired. // // Convert x * 2^r to binary. // // MSB is first digit past binary point, etc. // // Example: // r = 4, x = .75 // Convert .75 * 2^4 = 12 to binary: 1100 // x in binary is: .1100 // // Examples to 12 digits: // // 1.1 = 1.000110011001... 1.1 * 2^12 = 4505 = 1000110011001 // 1.2 = 1.001100110011... // 1.3 = 1.010011001100... // 1.4 = 1.011001100110... // 1.5 = 1.1 // // Note: // // Common decimal numbers such as 0.2 do not have exact binary representations. /// Binary Scientific Notation // // Binary Scientific Representation Similar to Decimal Scientific Notation // // Decimal: SIGN SIGNIFICAND x 10^{EXPONENT} // Binary: SIGN SIGNIFICAND x 2^{EXPONENT} // // Decimal Examples: // // 1.23 x 10^{2} = 123 // 1.23 x 10^{0} = 1.23 // 1.23 x 10^{-1} = .123 // Examples above are normalized, examples below are not. // 12.3 x 10^{1} = 123 // .123 x 10^{1} = 1.23 // 123 x 10^{-3} = .123 // // Binary Examples // // 1 x 2^{0} = 1 = 1 // 1 x 2^{1} = 10 = 2 // 1 x 2^{2} = 100 = 4 // 1.1 x 2^{2} = 110 = 6 // 1.1 x 2^{1} = 11 = 3 // 1.1 x 2^{0} = 1.1 = 1.5 // 1.1 x 2^{-1} = .11 = .75 // Examples above are normalized, examples below are not. // 11 x 2^{1} = 110 = 6 // 11 x 2^{0} = 11 = 3 // 11 x 2^{-1} = 1.1 = 1.5 // 11 x 2^{-2} = .11 = .75 /// Addition Using Scientific Notation // // Consider: // // a_scand x 2^{a_exp} // b_scand x 2^{b_exp} // // Assume that a > b in magnitude. (That is, |a| > |b|.) // // To add: // // Set b'_exp = a_exp. // // Set b'scand = b_scand / 2^(a_exp - b_exp) // That is, right-shift b_scand by (a_exp - b_exp) bits. // // Note that: b_scand x 2^{b_exp} == b'_scand x 2^{b'_exp} // // Set s_scand = a_scand + b'_scand // // Set s_exp = a_exp // // We now have the sum: s_scand x 2^{s_exp} // :Example: // // Multiply: // a = 1.1 x 2^4 // b = 1.0 x 2^3 // // a_scand -> 1.1, a_exp -> 4 // b_scand -> 1.0, b_exp -> 3 // // Step 1: Scale b so its exponent, 3, matches b's, 4. // // b = 1.0 x 2^3 = 0.1 x 2^4 // // b'_scand => 0.1, b'_exp = 4 // // Step 2: Add significands. // // a_scand + b'_scand = 1.1 + 0.1 = 10.0 // // a+b = 10.0 x 2^4 // // Step 3: Normalize the number (optional) // // a + b = 10.0 x 2^4 = 1.0 x 2^5 // // Answer is: 1.0 x 2^5 // // Subtraction is similar. /// Multiplication Using Scientific Notation // // Consider: // // a_scand x 2^{a_exp} // b_scand x 2^{b_exp} // // To multiply these: // // Set p_scand = a_scand x b_scand // Set p_exp = a_exp + b_exp // Optional: Normalize p // // Product is p_scand x 2^{p_exp} // :Example: // // a = 1.1 x 2^4 // b = 1.0 x 2^3 // // Step 1: Multiply significands and add exponents. // // p_scand = a_scand * b_scand = 1.0 * 1.1 = 1.1 // p_exp = a_exp + b_exp = 4+3 = 7 // // a * b = 1.1 x 2^7 // // Step 2: Normalize the number. // // In this case result is already normal. // // a * b = 1.1 x 2^7 //////////////////////////////////////////////////////////////////////////////// /// IEEE 754 FP Standard // :FP: IEEE 754-2013 -- IEEE Floating Point Standard // https://ieeexplore.ieee.org/document/8766229 /// Standard Specifies // // Formats of FP numbers. (There are several sizes.) // Results of arithmetic operations, including rounding. /// Objectives of Standard // // Represent range of numbers in common use. (Of course.) // Predictable rounding behavior. // Compare as integers. // // The following is NOT an objective: // // Keep things simple for an introductory computer class. // Nevertheless, it's not that bad. /// Features // // Can Represent: // Floating-point number. // + and - Infinity, and other special values. // // Special Properties // Representation of positive zero is all zeros. // Can use signed integer magnitude and equality tests. /// Sizes // // Half: 16 bits. Called FP16. Not the same as BP16. // Single: 32 bits. // Double: 64 bits. // Extended: Varies, not shown here. /// Key Ideas // // Format Specifies: // // Sign. // Exponent. // Significand (Fraction) // // Slight Complications (but for good reason): // // Exponent is biased. // Significand usually omits the MSB (it's assumed to be 1). /// IEEE 754 Single Format // // Format: SeeeeeeeeFFFFFFFFFFFFFFFFFFFFFFF // 31: S: Sign bit: 1 negative, 0 positive. // 30-23: e: Biased Exponent. (Exponent is e-127) // 22-0: F: Significand (Fraction) // // Typical Case : Value is ± ( 1.0 + F / 2^{23} ) 2^{e-127} // Special Cases: +0, -0, +∞, -∞, NaN (not a number), etc. // // Case Value formula. // 0 < e < 255, S = 0 : ( 1.0 + F / 2^{23} ) 2^{e-127} // 0 < e < 255, S = 1 : - ( 1.0 + F / 2^{23} ) 2^{e-127} // e = 0, S = 0, F = 0 : 0 // e = 0, S = 1, F = 0 : - 0 // e = 255, S = 0, F = 0: Infinity // e = 255, S = 1, F = 0: - Infinity // e = 255, S < 2, F != 0: NaN /// IEEE 754 Double Format // // Format: SeeeeeeeeeeeFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF // 63: S: Sign bit: 1 negative, 0 positive. // 62-52: e: Biased Exponent. (Exponent is e-1023) // 51-0: F: Significand (Fraction) // // Typical Case : Value is ± ( 1.0 + F / 2^{52} ) 2^{e-1023} // Special Cases: +0, -0, +∞, -∞, NaN (not a number), etc. // // Case Value formula. // 0 < e < 1023, S = 0 : ( 1.0 + F / 2^{52} ) 2^{e-1023} // 0 < e < 1023, S = 1 : - ( 1.0 + F / 2^{52} ) 2^{e-1023} // e = 0, S = 0, F = 0 : 0 // e = 0, S = 1, F = 0 : - 0 // e = 1023, S = 0, F = 0 : Infinity // e = 1023, S = 1, F = 0 : - Infinity // e = 1023, S < 2, F != 0: NaN /// IEEE 754 Rounding Modes // // Format specifies four rounding modes. // Hardware set to use desired rounding mode. // // Rounding Modes: // // Round to even. (Nearest LSB zero.) Most popular. // Round towards zero. // Round towards infinity. // Round towards -infinity. /// IEEE 754 Single Format Examples: IEEE 754 to Value // // Single FP: 32h'3fc00000 // = 32b'00111111110000000000000000000000 // SEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFF // // 0 01111111 10000000000000000000000 // S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF // // S = 0, E = 0x7f = 127, F 0x400000 = 4194304 // // Based on value of S and E, the following case applies: // // 0 < E < 255, S = 0 : ( 1.0 + F / 2^{23} ) 2^{E-127} // // Value = ( 1.0 + 4194304 / 2^{23} ) 2^{127-127} // = ( 1.0 + 0.5 ) // = 1.5 // Single FP: 32h'456ab000 // = 32b'01000101011010101011000000000000 // SEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFF // // 0 10001010 11010101011000000000000 // S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF // // S = 0, E = 8a = 138, F 6ab000 = 6991872 // // Based on value of S and E, the following case applies: // // 0 < E < 255, S = 0 : ( 1.0 + F / 2^{23} ) 2^{E-127} // // Value = ( 1.0 + 6991872 / 2^{23} ) 2^{138-127} // = ( 1.0 + 0.833496 ) 2048 // = 3755 // Single FP: 32h'c0490fdb // = 32b'11000000010010010000111111011011 // SEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFF // // 1 10000000 10010010000111111011011 // S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF // // S = 1, E = 80 = 128, F 490fdb = 4788187 // // Based on value of S and E, the following case applies: // // 0 < E < 255, S = 1 : - ( 1.0 + F / 2^{23} ) 2^{E-127} // // Value = - ( 1.0 + 4788187 / 2^{23} ) 2^{128-127} // = - ( 1.0 + 0.570796 ) 2 // = -3.14159 // Single FP: 32h'0 // = 32b'00000000000000000000000000000000 // SEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFF // // 0 00000000 00000000000000000000000 // S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF // // S = 0, E = 0, F = 0 // // Based on value of S and E, the following case applies: // // E = 0, S = 0, F = 0 : 0 // // Value = 0 // Single FP: 32h'7f800000 // = 32b'01111111100000000000000000000000 // SEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFF // // 0 11111111 00000000000000000000000 // S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF // // S = 0, E = 255, F = 0 // // Based on value of S and E, the following case applies: // // E = 255, S = 0, F = 0: Infinity // // Value = Infinity. /// IEEE 754 Single Format Examples: Value to IEEE 754 // // Value (decimal): 12.75 // Convert to binary: 1100.11 // Convert to normalized binary scientific notation: 1.10011 x 2^3 // // S = 0 (it's positive) // E = 127 + 3 = 130 = 100 0010 // F = 10011 000000000000000000 // // Single: 0 1000 0010 10011 000000000000000000 // = 0100 0001 0100 1100 0000 0000 0000 0000 // = 0x414c0000 //////////////////////////////////////////////////////////////////////////////// /// Floating Point Data Types /// // :SV: 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 in class assignments ... // ... because our synthesis programs won't accept them. /// Punning Functions -- A.K.A. Reinterpretation // // :Keyword: $realtobits // :Keyword: $bitstoreal // :Keyword: $shortrealtobits // :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. /// :Practice Problems: // // 2023 Homework 2 -- Compute (1-b/c)/a efficiently. (ChipWare modules.) // https://www.ece.lsu.edu/v/2023/hw02.pdf // https://www.ece.lsu.edu/v/2023/hw02-sol.v.html // // 2017 Homework 2 -- Use ChipWare modules to perform linear interpolation. // https://www.ece.lsu.edu/v/2017/hw02.pdf // https://www.ece.lsu.edu/v/2017/hw02.v.html // https://www.ece.lsu.edu/v/2017/hw02-sol.v.html // // 2017 Midterm Exam Problem 6c -- Synthesizability of reals. // https://www.ece.lsu.edu/v/2017/mt.pdf // https://www.ece.lsu.edu/v/2017/mt_sol.pdf // // 2018 Homework 3 Problem 2 -- Sorting network operating on FP keys. // https://www.ece.lsu.edu/v/2018/hw03.pdf // https://www.ece.lsu.edu/v/2018/hw03-sol.v.html // :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 nets can only be declared using four-value integer types. // // 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 representation. // 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 ); // Display Output: // // After +1: from_dbits 3.40000000000000035527 dval 0x0004, dbits 0x400b333333333334 $write("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; $write("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 uwire [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); $write("Sum is %f + %f = %f\n",a,b,sum); end end endmodule //////////////////////////////////////////////////////////////////////////////// /// Net Object Kinds // :SV: Section 6.6 // Used to interconnect devices. // Net objects are driven, they are not assigned. // // Can NOT ordinarily be assigned in procedural code. // Net types are driven by: // Connecting them to the output of a module (in an instantiation). // Using an assign statement (to be covered). // Assigning in the declaration. // // The default type for an object of a net kind is logic. // But any four-state type can be used. uwire [7:0] i_am_logic; uwire logic [7:0] i_am_logic_too; uwire integer i_am_an_integer_which_is_a_thirty_two_element_array_of_logic; // Some net kinds: // // :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 :SV: 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; // Can have multiple drivers. Avoid this if you can. uwire wire_2; // Can have at most one driver. Otherwise a compile error. logic r1; // Defaults to var kind, not a net kind. // Example of a continuous assignment. assign wire_1 = a | b; and a1(wire_2,a,b); // This declaration is a continuous assignment, NOT an initialization. uwire 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 @* begin r1 = a; // wire_4 = b; // Not allowed because wire_4 is a net kind. end endmodule ////////////////////////////////////////////////////////////////////////////// /// Array Ranges /// /// :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. // These are called unpacked arrays in the material above. // Synthesizable. // // :Example: int a[4]; // Valid indices: 0, 1, 2, 3 // // :Example: int a[S]; // Synthesizable if S is constant. // Valid indices are: 0, 1, ..., S - 1 // // /// Static Arrays: Range. // These too are called unpacked arrays in the material above. // Synthesizable. // // :Syntax: ELEMENT_TYPE IDENT[LAST:FIRST]; // Element indices are FIRST, FIRST+1, ..., LAST if LAST > FIRST // Element indices are FIRST, FIRST-1, ..., LAST if LAST < FIRST `ifdef xxx // int a[10:8]; // Valid indices: 10, 9, 8. // In other words, a[9] is okay, but a[7] is out of range. // int a[8:10]; // Valid indices: 10, 9, 8. // real I_love_FORTRAN[5:1]; // Valid indices: 5, 4, 3, 2, 1 real C_rules_rule[4:0]; // Valid indices: 4, 3, 2, 1, 0 real because_I_can[1:-3]; // Valid indices: 1, 0, -1, -2, -3 `endif // /// Zero v. One for First Index // https://xkcd.com/163/ ////////////////////////////////////////////////////////////////////////////// /// Array Variations: Static, Dynamic, Associative /// /// Array Variations: Static, Dynamic, Associative // // The arrays covered so far are static arrays. // Dynamic and associative arrays are used in testbenches. /// Dynamic Arrays // // :Def: Dynamic Array // An array whose size can change. // Usually declared without a size. // // Not realistically 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 :SV: Section 7.5 // // /// Associative Arrays // // :Def: Associative Array // An array that is indexed by a value that does not indicate // the element's exact position in the array. // // Associative arrays often used in scripting languages like // JavaScript, Perl, and Python. // // // Not realistically 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 array_examples #( int num_elts2 = 6 ) (input int num_elts); logic [7:0] array1b[4:10]; 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 array3b[integer]; int array4[10:1]; // Ten elements, indices 1..10 // string step[real]; // Not [yet?] implemented in xmvlog. initial begin #0; // Wait for num_elts to be set. $write("Array examples num_elts = %0d\n",num_elts); array1 = new [ num_elts ]; // Hope that num_elts >= 6. // array1[ 5 ] = 123; array3b[ 5 ] = 123; array2[ "5" ] = 1; array2[ "five" ] = 2; array2[ "six" ] = 3; array2[ "five" ] = 5; if ( array2.exists( "seven" ) ) $write("It has element 7."); // At this point array2 has 3 elements. array2.delete("5"); // At this point array2 has 2 elements. for ( int i=0; i<10; i++ ) array3[i] = i; for ( int i=0; i<20; i++ ) array3b[1<<i] = i; // Perfectly fine, same size and type. // // It doesn't matter that index numbering different. // array4 = array3; begin int x1, x2; x1 = array1[5]; x2 = array2["five"]; end `ifdef REAL_INDICES step[1] = "Unlock door."; step[2] = "Enter room."; step[3] = "Sit down."; step[1.5] = "Turn off alarm."; `endif end endmodule module array_examples_test; array_examples ae(6); endmodule /// Dynamic Arrays // // :SV: 7.5 // // :Keyword: new[NUM_ELTS](init) -- // :Keyword: size() -- Member function, return number of elements. // :Example: int s = a.size(); // :Keyword: delete() -- Member function, delete all elements. /// Associative Arrays // // :SV: 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). // // :Sample: foreach ( a[i] ) b[i] = a[i]; /// Other Topics // // Streaming operators. // { << 4 { packed_expr } } `include "/apps/linux/cadence/DDIEXPORT23/GENUS231/share/synth/lib/chipware/sim/verilog/CW/CW_fp_add.v"