Digital Design with the Verilog HDL - Chapter 7: Parameters, Task, and Function in Verilog
Bạn đang xem 20 trang mẫu của tài liệu "Digital Design with the Verilog HDL - Chapter 7: Parameters, Task, and Function in Verilog", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Tài liệu đính kèm:
- digital_design_with_the_verilog_hdl_chapter_7_parameters_tas.pdf
Nội dung text: Digital Design with the Verilog HDL - Chapter 7: Parameters, Task, and Function in Verilog
- Digital Design with the Verilog HDL Chapter 7: Parameters, Task, and Function in Verilog Dr. Phạm Quốc Cường Computer Engineering – CSE – HCMUT 1
- Elaboration of Verilog Code 2
- Elaboration of Verilog Code • Elaboration is a pre-processing stage that takes place before code is synthesized. • It allows us to automatically alter our code before Synthesis based on Compile-Time information • Uses of Elaboration – Unrolling of FOR Loops – Parameterization – Code Generation – Constant Functions – Macros 3
- Overview • Parameters • Generated Instantiation • Functions and Tasks 4
- Parameters • Compile-time constant parameters in Verilog – In Verilog: parameter N=8’d100; – Values are substituted during Elaboration; parameters cannot change value after synthesis • Can be used for three main reasons – Make code more readable – Make it easier to update code – Improve (re)usability of modules 5
- More Readable, Less Error-Prone parameter ADD=4’b0000; parameter SUB=4’b0100; parameter XOR=4’b0101; parameter AND=4’b1010; parameter EQ=4’b1100; always @(*) begin always @(*) begin case (mode) case (mode) 4’b0000: ADD: 4’b0100: SUB: 4’b0101: VS XOR: 4’b1010: AND: 4’b1100: EQ: default: default: endcase endcase end end 6
- Reusability/Extensibility of Modules module xor_array(y_out, a, b); parameter SIZE = 8, DELAY = 15; // parameter defaults output [SIZE-1:0] y_out; input [SIZE-1:0] a,b; wire #DELAY y_out = a ^ b; endmodule xor_array G1 (y1, a1, b1); // use defaults xor_array #(4, 5) G2(y2, a2, b2); // override default parameters // SIZE = 4, DELAY = 5 • Module instantiations cannot specify delays without parameters – Where would delays go? What type would they be? 7
- Overriding Parameters • Parameters can be overridden – Generally done to “resize” module or change its delay • Implicitly: override in order of appearance – xor_array #(4, 5) G2(y2, a2, b2); • Explicitly: name association (preferred) – xor_array #(.SIZE(4), .DELAY(5)) G3(y2, a2, b2); • Explicitly: defparam – defparam G4.SIZE = 4, G4.DELAY = 15; – xor_array G4(y2, a2, b2); • localparam parameters in a module can’t be overridden – localparam SIZE = 8, DELAY = 15; 8
- Parameters With Instance Arrays module array_of_xor (y, a, b); parameter SIZE=4; input [SIZE-1:0] a,b; output [SIZE-1:0] y; xor G3[SIZE-1:0] (y, a, b); // instantiates 4 xor gates endmodule // (unless size overridden) very common use of parameters module variable_size_register (q, data_in, clk, set, rst); parameter BITWIDTH=8; input [BITWIDTH-1:0] data_in; // one per flip-flop input clk, set, rst; // shared signals output [BITWIDTH-1:0] q; // one per flip-flop // instantiate flip-flops to form a BITWIDTH-bit register flip_flop M [BITWIDTH-1:0] (q, data_in, clk, set, rst); endmodule 9
- Synthesized array_of_xor 10
- Synthesized variable_size_register 11
- Parameterized Ripple Carry Adder module RCA(sum, c_out, a, b, c_in); parameter BITS=8; input [BITS-1:0] a, b; input c_in; output [BITS-1:0] sum; output c_out; wire [BITS-1:1] c; Add_full M[BITS-1:0](sum, {c_out, c[BITS-1:1]}, a, b, {c[BITS-1:1], c_in}); endmodule . Instantiate a 16-bit ripple-carry adder: RCA #(.BITS(16)) add_16(sum, carryout, a, b, carryin); 12
- Parameterized Shift Left Register [1] module shift(out, in, clk, rst); parameter BITS=8; input in, clk, rst; output [BITS-1:0] out; dff shiftreg[BITS-1:0](out, {out[BITS-2:0], in}, clk, rst); endmodule . Instantiate a 5-bit shift register: shift #(.BITS(5)) shift_5(shiftval, shiftin, clk, rst); 13
- Parameterized Shift Left Register [2] module shift_bhv (outbit, out, in, clk, rst); parameter WIDTH = 8; output reg [WIDTH-1:0] out; output reg outbit; input in, clk, rst; always @(posedge clk) begin if (rst) {outbit,out} <= 0; else {outbit,out} <= {out[WIDTH-1:0],in}; end endmodule . Instantiate a 16-bit shift register: shift_bhv #(16) shift_16(shiftbit, shiftout, shiftin, clk, rst); 14
- Synthesized Shift Left Register 15
- Parameters + Generate Statements • Problem: Certain types of logic structures are efficient only in certain scenarios • For example when designing an adder: – Ripple-Carry Adders are better for small operands – Carry Look-ahead Adders are better for large operands • If we change a parameter to use a larger or smaller adder size, we may also want to change the structure of the logic 16
- Generated Instantiation • Generate statements: control over the instantiation/creation of – Modules, gate primitives, continuous assignments, initial blocks, always blocks, nets and regs • Generate instantiations are resolved during Elaboration – Can alter or replace a piece of code based on compile-time information – Before the design is simulated or synthesized – Think of it as having the code help write itself 17
- Special Generate Variables • Index variables used in generate statements; declared using genvar (e.g., genvar i ) • Useful when developing parameterized modules • Can override the parameters to create different-sized structures • Easier than creating different structures for all different possible bitwidths 18
- Generate-Loop • A generate-loop permits making one or more instantiations (pre-synthesis) using a for-loop. module gray2bin1 (bin, gray); parameter SIZE = 8; // this module is parameterizable output [SIZE-1:0] bin; input [SIZE-1:0] gray; How does this differ genvar i; from a standard for generate loop? for (i=0; i<SIZE; i=i+1) begin: bit assign bin[i] = ^gray[SIZE-1:i]; // reduction XOR end endgenerate endmodule 19
- Generate-Conditional • A generate-conditional allows conditional (pre-synthesis) instantiation using if-else-if constructs module multiplier(a ,b ,product); parameter A_WIDTH = 8, B_WIDTH = 8; localparam PRODUCT_WIDTH = A_WIDTH+B_WIDTH; input [A_WIDTH-1:0] a; input [B_WIDTH-1:0] b; These are output [PRODUCT_WIDTH-1:0] product; parameters, generate not variables! if ((A_WIDTH < 8) || (B_WIDTH < 8)) CLA_multiplier #(A_WIDTH,B_WIDTH) u1(a, b, product); else WALLACE_multiplier #(A_WIDTH,B_WIDTH) u1(a, b, product); endgenerate endmodule 22
- Generate-Case • A generate-case allows conditional (pre-synthesis) instantiation using case constructs module adder (output co, sum, input a, b, ci); parameter WIDTH = 8; generate case (WIDTH) 1: adder_1bit x1(co, sum, a, b, ci); // 1-bit adder implementation 2: adder_2bit x1(co, sum, a, b, ci); // 2-bit adder implementation default: adder_cla #(WIDTH) x1(co, sum, a, b, ci); endcase Can have a “default” in endgenerate a generate-case endmodule 23
- Generate a Pipeline [Part 1] module pipeline(out, in, clk, rst); parameter BITS = 8; parameter STAGES = 4; input [BITS-1:0] in; output [BITS-1:0] out; wire [BITS-1:0] stagein [0:STAGES-1]; // value from previous stage reg [BITS-1:0] stage [0:STAGES-1]; // pipeline registers assign stagein[0] = in; generate genvar s; for (s = 1; s < STAGES; s = s + 1) begin : stageinput assign stagein[s] = stage[s-1]; end endgenerate // continued on next slide 24
- Generate a Pipeline [Part 2] // continued from previous slide assign out = stage[STAGES-1]; generate genvar j; for (j = 0; j < STAGES; j = j + 1) begin : pipe always @(posedge clk) begin if (rst) stage[j] <= 0; else stage[j] <= stagein[j]; end end endgenerate endmodule What does this generate? 25
- Functions and Tasks • HDL constructs that look similar to calling a function or procedure in an HLL. • Designed to allow for more code reuse • There are 3 major uses for functions/tasks – To describe logic hardware in synthesizable modules – To describe functional behavior in testbenches – To compute values for parameters and other constants for synthesizable modules before they are synthesized • When describing hardware, you must make sure the function or task can be synthesized! 26
- Functions and Tasks in Logic Design • It is critical to be aware of whether something you are designing is intended for a synthesized module – Hardware doesn’t actually “call a function” – No instruction pointer or program counter – This is an abstraction for the designer • In synthesized modules, they are used to describe the behavior we want the hardware to have – Help make HDL code shorter and easier to read – The synthesis tool will try to create hardware to match that description 27
- Functions and Tasks in Testbenches • Since testbenches do not need to synthesize, we do not have to worry about what hardware would be needed to implement a function • Be careful: This doesn’t mean that we can treat such functions & tasks as software • Even testbench code must follow Verilog standards, including the timing of the Stratified Event Queue 28
- Functions • Declared and called within a module • Used to implement combinational behavior – Contain no timing controls or tasks – Can use behavioral constructs • Inputs/outputs – At least one input, exactly one output – Return variable is the same as function name • Can specify type/range (default: 1-bit wire) • Usage rules: – May be referenced in any expression (RHS) – May call other functions – Use automatic keyword to declare recursive functions 29
- Constant Functions • A special class of functions that can always be used in a synthesizable module • Constant functions take only constant values (such as numbers or parameters) as their inputs. – All inputs are constant, so the output is also constant – The result can be computed at elaboration, so there is no reason to build hardware to do it • Constant functions are useful when one constant value is dependent on another. It can simplify the calculation of values in parameterized modules. 30
- Function Example module word_aligner (word_out, word_in); output [7: 0] word_out; size of return value input [7: 0] word_in; assign word_out = aligned_word(word_in); // invoke function function [7: 0] aligned_word; // function declaration input [7: 0] word; begin aligned_word = word; if (aligned_word != 0) input to function while (aligned_word[7] == 0) aligned_word = aligned_word << 1; end endfunction endmodule What sort of hardware might this describe? Do you think this will synthesize? 31
- Function Example module arithmetic_unit (result_1, result_2, operand_1, operand_2,); output [4: 0] result_1; output [3: 0] result_2; function call input [3: 0] operand_1, operand_2; assign result_1 = sum_of_operands (operand_1, operand_2); assign result_2 = larger_operand (operand_1, operand_2); function [4: 0] sum_of_operands(input [3: 0] operand_1, operand_2); sum_of_operands = operand_1 + operand_2; endfunction function inputs function output function [3: 0] larger_operand(input [3: 0] operand_1, operand_2); larger_operand = (operand_1 >= operand_2) ? operand_1 : operand_2; endfunction endmodule 32
- Constant Function Example module register_file ( ); parameter NUM_ENTRIES=64; localparam NUM_ADDR_BITS=ceil_log2(NUM_ENTRIES); function [31: 0] ceil_log2(input [31: 0] in_val); reg sticky; reg [31:0] temp; begin sticky = 1'b0; for (temp=32'd0; value>32'd1; temp=temp+1) begin if((value[0]) & (|value[31:1])) sticky = 1'b1; value = value>>1; end clogb2 = temp + sticky; end endfunction 33
- Tasks • Declared within a module – Only used within a behavior • Tasks provide the ability to – Describe common behavior in multiple places – Divide large procedures into smaller ones • Tasks are not limited to combinational logic – Can have time-controlling statements (@, #, wait) • Some of this better for testbenches – Use automatic keyword to declare “reentrant” tasks • Can have multiple outputs, inout ports • Local variables can be declared & used 34
- Task Example [Part 1] module adder_task (c_out, sum, clk, reset, c_in, data_a, data_b, clk); output reg [3: 0] sum; output reg c_out; this is NOT conditionally input [3: 0] data_a, data_b; “creating” hardware! input clk, reset, c_in; always @(posedge clk or posedge reset) begin if (reset) {c_out, sum} <= 0; else add_values (c_out, sum, data_a, data_b, c_in); // invoke task end // Continued on next slide 35
- Task Example [Part 2] // Continued from previous slide task add_values; // task declaration output reg c_out; task outputs output reg [3: 0] sum input [3: 0] data_a, data_b; task inputs input c_in; {c_out, sum} <= data_a + (data_b + c_in); endtask endmodule • Could have instead specified inputs/outputs using a port list. task add_values (output reg c_out, output reg [3: 0] sum, input [3:0] data_a, data_b, input c_in); • Could we have implemented this as a function? 36
- Function Example [Part 1] module adder_func (c_out, sum, clk, reset, c_in, data_a, data_b, clk); output reg [3: 0] sum; output reg c_out; this is NOT conditionally input [3: 0] data_a, data_b; “creating” hardware! input clk, reset, c_in; always @(posedge clk or posedge reset) begin if (reset) {c_out, sum} <= 0; else {cout, sum} <= add_values (data_a, data_b, c_in); // invoke task end // Continued on next slide 37
- Function Example [Part 2] // Continued from previous slide function [4:0] add_values;// function declaration input [3: 0] data_a, data_b; input c_in; add_values = data_a + (data_b + c_in); endfunction endmodule How does this differ from the task-based version? 38
- Task Example task leading_1(output reg [2:0] position, input [7:0] data_word); reg [7:0] temp; begin temp = data_word; internal task variable position = 7; while (!temp[7]) begin NOTE: temp = temp << 1; “while” loops usually position = position - 1; not synthesizable! end end endtask • What does this task assume for it to work correctly? • How do tasks differ from modules? • How do tasks differ from functions? 39
- Distinctions between tasks and functions The following rules distinguish tasks from functions: • A function shall execute in one simulation time unit; a task can contain time-controlling statements. • A function cannot enable a task; a task can enable other tasks and functions. • A function shall have at least one input type argument and shall not have an output or inout type argument; a task can have zero or more arguments of any type. • A function shall return a single value; a task shall not return a value. The purpose of a function is to respond to an input value by returning a single value. A task can support multiple goals and can calculate multiple result values. 40