390 likes | 408 Views
This course covers behavioral modeling of ASM, repetitive algorithms, tasks and functions, register file, memory unit, and file I/O system functions and tasks.
E N D
COE 405 Logic Design with Behavioral Models of Combinational & Sequential Logic II Dr. Aiman H. El-Maleh Computer Engineering Department King Fahd University of Petroleum & Minerals
Outline • Behavioral Modeling of ASM • Repetitive Algorithms • Tasks and Functions • Register File • Memory Unit • File I/O System Functions and Tasks
Behavioral Modeling of ASM module Controller (output reg Clr_P1_P0, Ld_P1_P0, Ld_R0, input En, Ld, clk, rst); parameter S_idle = 2'b00, S_1=2'b01, S_full=2'b10, S_wait=2'b11; reg [1:0] state, next_state; always @(posedge clk) if (rst) state <= S_idle; else state <= next_state; always @(state, En, Ld) begin Clr_P1_P0 = 0; Ld_P1_P0=0; Ld_R0=0; case (state) S_idle: if (En) begin next_state=S_1; Ld_P1_P0=1; end else next_state=S_idle;
Behavioral Modeling of ASM S_1: begin next_state=S_full; Ld_P1_P0=1; end S_full: if (!Ld) next_state=S_wait; else begin Ld_R0=1; if (En) begin next_state=S_1; Ld_P1_P0=1; end else begin next_state=S_idle; Clr_P1_P0=1; end end S_wait: if (!Ld) next_state=S_wait; else begin Ld_R0=1; if (En) begin next_state=S_1; Ld_P1_P0=1; end else begin next_state=S_idle; Clr_P1_P0=1; end end endcase end endmodule
Linear Feedback Shift Register (LFSR) module LFSR #(parameter Length=8, initial_state = 8'b1001_0001, //91h parameter [Length: 1] Tap_Coefficient = 8'b1100_1111) (input clock, reset_b, output reg[1:Length] Y); always@ (posedge clock) if (reset_b == 1'b0) Y<= initial_state; else begin Y[1] <= Y[8]; Y[2] <= Tap_Coefficient[7]? Y[1]^Y[8]:Y[1]; Y[3] <= Tap_Coefficient[6]? Y[2]^Y[8]:Y[2]; Y[4] <= Tap_Coefficient[5]? Y[3]^Y[8]:Y[3]; Y[5] <= Tap_Coefficient[4]? Y[4]^Y[8]:Y[4]; Y[6] <= Tap_Coefficient[3]? Y[5]^Y[8]:Y[5]; Y[7] <= Tap_Coefficient[2]? Y[6]^Y[8]:Y[6]; Y[8] <= Tap_Coefficient[1]? Y[7]^Y[8]:Y[7]; end endmodule
Repetitive Algorithms • for loop: • for (initial_statement; control expression; index_statement) statement_for_execution; • initial_statementexecutes once to initialize a register variable (i.e. an integer or reg) that controls the loop • If control_expression is true the statement_for_execution will execute • After the statement_for_executionhas executed, the index_statement will execute (usually to increment a counter) • Then the control expression is checked again and if false the loop terminates.
Linear Feedback Shift Register (LFSR) module LFSR2 #(parameter Length=8, initial_state = 8'b1001_0001, //91h parameter [Length:1] Tap_Coefficient = 8'b1100_1111) (input clock, reset_b, output reg [1:Length] Y); integer k; always@ (posedge clock) if (reset_b == 1'b0) Y<= initial_state; else begin for (k = 2; k <= Length; k = k+1) if (Tap_Coefficient[Length-k+1]==1) Y[k] <= Y[k-1]^Y[Length]; else Y[k] <= Y[k-1]; Y[1] <= Y[Length]; end endmodule
Linear Feedback Shift Register (LFSR) module LFSR3 #(parameter Length=8, initial_state = 8'b1001_0001, //91h parameter [Length:1] Tap_Coefficient = 8'b1100_1111) (input clock, reset_b, output reg [1:Length] Y); integer k; always@ (posedge clock) if (reset_b == 1'b0) Y<= initial_state; else begin for (k = 2; k <= Length; k = k+1) Y[k] <= Tap_Coefficient[Length-k+1]?Y[k-1]^Y[Length]:Y[k-1]; Y[1] <= Y[Length]; end endmodule
Majority module Majority #(parameter size=8, max=4, majority=5) (input [size-1:0] Data, output reg Y); reg [max-1:0] count; integer k; always@ (Data) begin count = 0; for (k=0; k < size; k = k+1) if (Data[k] == 1) count = count + 1; Y = (count >= majority); end endmodule
Adder/Subtracter module ADDSUB #(parameter n=4)(output reg [n-1:0] S, output Cout, input [n-1:0] A, B, input OP); integer i; reg [n:0] C; reg [n-1:0] G,P; assign Cout = C[n]; always @ (A, B, OP) begin C[0] = OP; for (i=0; i<n; i=i+1) begin G[i] = A[i] & (B[i] ^ OP); P[i] = A[i] ^ (B[i] ^ OP); S[i] = P[i] ^ C[i]; C[i+1] = G[i] | P[i] & C[i]; end end endmodule
Signed Multiplication module MULC #(parameter N=4) (output reg [N-1:0] HI, LO, input [N-1:0] A, B); integer i; reg [N-1:0] C; reg OVF, Sign; always @(A, B) begin HI = 0; LO = B; for (i=0; i<N; i=i+1) begin if( LO[0] ) begin if ( i==N-1 ) begin C = HI - A; OVF = (HI[N-1] ^ A[N-1]) & (HI[N-1]^C[N-1]); end else begin C = HI + A; OVF = (HI[N-1] ~^ A[N-1]) & (HI[N-1]^C[N-1]); end
Signed Multiplication Sign = OVF ^ C[N-1]; HI = C; end else Sign = HI[N-1]; // LO[0]==0 {HI, LO} = {Sign, HI, LO[N-1:1]}; end // for loop end // always endmodule
Unsigned Division module DIV #(parameter N=4) (output reg [N-1:0] Q, R, input [N-1:0] dividend, diviser); integer i; reg [N-1:0] diff; always @(dividend, diviser) begin Q = dividend; R = 0; for (i=0; i<N; i=i+1) begin {R,Q} = {R,Q} << 1; diff = R - diviser; if( !diff[N-1] ) begin R = diff; Q[0]= 1; end end end endmodule
Repetitive Algorithms • repeat loop • repeat (expression) statement; • executes an associated statement or block of statements a specified number of times unless it is terminated by a disable statement within the activity flow • Example: a repeat loop is used to initialize a memory array word_address = 0; repeat (memory_size) begin memory[word_address]=0; word_address = word_address+1; end
Repetitive Algorithms • while loop • while (expression) statement; • Executes repeatedly while a Boolean expression is true module CountOnes (input [7:0] reg_a, output reg [3:0] count); always@ (reg_a) begin: count_of_1s // declares a named block of statements reg [7:0] temp_reg; count = 0; temp_reg = reg_a; // load a data word while (temp_reg) begin if (temp_reg[0]) count = count + 1; // count = count + temp_reg[0]; temp_reg = temp_reg >> 1; end end endmodule
Clock Generators module clockgen (output reg clock); parameter half_cycle = 50; parameter stop_time = 350; initial begin: clock_loop clock = 0; forever begin #half_cycle clock = 1; #half_cycle clock = 0; end end initial #stop_time disable clock_loop; endmodule
Disable Statement • Disable statement is used to prematurely terminate a named block of procedural statements. Execution is transferred to the statement that immediately follows the named block. module find_first_one (output reg [3:0] index_value, input [15:0] A_word, input trigger); always @(posedge trigger) begin: search_for_1 for (index_value=0; index_value<15; index_value=index_value+1) if (A_word[index_value] == 1) disable search_for_1; end endmodule Not Supported by Synthesis Tools
Tasks and Functions • Tasks create a hierarchical organization of proceduralstatements within a Verilog behavior. • Functions substitute for an expression. • Tasks and functions facilitate a readable style of code, with a single identifier conveying the meaning of many lines of code. • Encapsulation of Verilog code into tasks or functions hides the details of an implementation. • Overall, tasks and functions improve the readability, portability and maintainability of a model.
Tasks • Tasks are declared within a module and they may be referenced from within a cyclic or single-pass behavior. • A task can have parameters passed to it and results of executing a task can be passed back to environment. • When a task is called, copies of parameters in environment are associated with inputs, outputs, and inouts within the task according to order of declaration • The variables in environment are visible to a task. • Local variables may be declared within a task. • A task can call itself.
Tasks • A task must be named and may include declarations of any number of: parameter, input, output, inout, reg, integer, real, time, realtime. • Arguments of a task retain the type they hold in the environment that invokes the task. • All arguments of a task are passed by a value. • The basic syntax of a task is: task [task-id] ([arg]) ; begin [statements] ; end endtask • [arg] is the argument declaration and is similar to port declaration except that the default output data type is reg and the wire data type can not be used as output.
Tasks module adder_task (output reg c_out, output reg [3:0] sum, input [3:0] data_a, data_b, input c_in, clk, reset); always @(posedge clk, posedge reset) if (reset == 1’b1) {c_out, sum} <= 0; else add_values(c_out, sum, data_a, data_b, c_in); taskadd_values (output c_out, output [3:0] sum, input [3:0] data_a, data_b, input c_in); {c_out, sum} <= data_a + data_b + c_in; endtask endmodule
Tasks module adder_task2 (output reg [5:0] sum, input [3:0] data_a, data_b, data_c, data_d, input clk, reset); reg [5:0] sum1, sum2; always @(posedge clk, posedge reset) if (reset == 1) sum = 0; else begin add_values2(sum1, data_a, data_b); add_values2(sum2, data_c, data_d); add_values2(sum, sum1, sum2); end taskadd_values2 (output [5:0] sum, input [5:0] data_a, data_b); sum = data_a + data_b; endtask endmodule
Tasks module eq2task (output reg aeqb, input [1:0] a,b); reg e0, e1; always @* begin equ_task(2, a[0], b[0], e0); equ_task(2, a[1], b[1], e1); aeqb = e0 & e1; end taskequ_task(input integer delay, input i0, i1, output eq); #delay eq = i0 ~^ i1; endtask endmodule
Functions • Verilog functions are declared within a parent module and can be referenced in any valid expression. • A function is implemented by an expression and returns a value at the location of the function’s identifier. • Functions may implement only combinational behavior. • A function may not contain timing controls (#, @, wait), non-blocking statements. • Functions may not invoke a task but they may call other functions but not recursively. • Functions are expanded during synthesis and "flattened”.
Functions • Basic syntax of a function is shown below: • [result-type] is the data type of the returned result (usually reg or integer; default is reg). • Function name is specified by [func-id] • Function value is returned by a statement like: func-id = … ;
Functions • Consider the following module snippet: with one operation repeated 2 times • We can define a function INC and use it. • INC takes a 4-bit input argument and returns a 4-bit result module … … assign X = (A>4) ? A+3 : A; assign Y = (B>4) ? B+3 : B; …. endmodule module … …. assign X = INC(A); assign Y = INC(B); …. function [3:0] INC(input [3:0] T); INC = (T>4) ? T+3 : T; endfunction …. endmodule
Functions • A function could be used to calculate the constants whose values depend on other parameters: Note that the function in this example is evaluated during pre-processing, it does not infer any hardware module modulo_counter #(parameter M=10)( input clk, reset, output reg [log2 (M)-1:0] q ); always @ (posedge clk) if (reset || q==M) q <= 0; else q <= q + 1; functionintegerlog2 (input integer n); integer i; begin log2 = 1; for (i=0; 2**i<n; i=i+1) log2 = i+1; end endfunction endmodule
Functions module arithmetic_unit ( output [4:0] result_1, output [3:0] result_2, input [3:0] operand_1, operand_2); assign result_1 = sum_of_operands (operand_1, operand_2); assign result_2 = largest_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 [3:0] largest_operand (input [3:0] operand_1, operand_2); largest_operand = (operand_1 >= operand_2) ? operand_1: operand_2; endfunction endmodule
Register File module Register_File #(parameter word_size=32, addr_size=5) ( output [word_size-1:0] Data_Out_1, Data_Out_2, input [word_size-1:0] Data_In, input [addr_size-1:0] Read_Addr_1, Read_Addr_2, Write_Addr, input Write_Enable, Clock); reg [word_size-1:0] Reg_File[2**addr_size-1:0]; initial begin Reg_File[0]=10; Reg_File[1]=20; end assign Data_Out_1 = Reg_File[Read_Addr_1]; assign Data_Out_2= Reg_File[Read_Addr_2]; always @(posedge Clock) if (Write_Enable==1'b1) Reg_File[Write_Addr] <= Data_In; endmodule
Memory Unit module Memory_Unit #(parameter word_size=8, address_size=4, memory_size=16) (output [word_size-1:0] data_out, input [word_size-1:0] data_in, input [address_size-1:0] address, input clk, write); reg [word_size-1:0] memory[memory_size-1:0]; assign data_out = memory[address]; always @ (posedge clk) if (write) memory[address] <= data_in; endmodule
File I/O System Functions and Tasks • Verilog provides a set of functions and tasks to access external data files. These are extremely useful in testbenches. • A file can be opened and closed by the $fopen and $fclose functions. The simplified syntax of using $fopen is: [mcd_name] = $fopen ( " [file_name] " ) ; • The $fopen returns a 32-bit multichannel descriptor associated with the file. • The descriptor is a 32-bit flag, in which each bit represents a file (i.e., a channel). The LSB is reserved for the standard output (i.e., the console). • When the file is opened successfully, a descriptor is returned with one bit asserted. • For example, 0 . . .0010 is returned for the first opened file, 0 . . .0100 is returned for the second opened file, and so on. The function returns all 0's if the open operation fails.
Writing to an Opened File • Data can be written to an opened file using the following system tasks: $fdisplay, $fwrite, $fstrobe, and $fmonitor. The syntax is: $fdisplay ([mcd_name] , [format_string] , arguement1, arguement2 , …) ; • [format_string] term contains regular characters and "escape sequences“ to specify the format of the corresponding arguments. When the string is displayed, the values of the corresponding arguments are substituted into the string and shown in the designated format. • The commonly used escape sequences include %d, %b, %o, %h, %c, %s, and %g, which are for decimal, binary, octal, hexadecimal, character, string, and real number, respectively.
File Write Example • In the example below a descriptor (both_file) is created by a bitwise or operation over 2 multichannel descriptors. This is used to write text to the console and the log file. module filetest (); integer log_file, both_file; localparam con_file=16'h0000_0001; initial begin log_file = $fopen("my_log"); if (log_file==0) $display("Fail to open file"); // write to console both_file = log_file | con_file; // write to both console and log file $fdisplay(both_file, "Simulation Started"); // write to log file only $fdisplay(log_file, "Writing to log file only"); // write to both console and log file $fdisplay(both_file, "Simulation Ended"); $fclose(log_file); end endmodule
Reading Data from External Files • There are two simple system tasks to retrieve data from an external file: $readmemb and $readmemh • These tasks assume that the external file stores the content of a memory array and reads the content into a variable. • The $readmemb and $readmemh tasks further assume that the content is in the binary and hexadecimal formats, respectively. • The simplified syntax is: $readmemb (" [file-name] " , [mem-variable]) ; $readmemh (“ [file-name] " , [mem-variable]) ; • The following code segment illustrates the retrieval of an 8 by 4 memory array: reg [3:0] v_mem [0:7] ; . . . $readmemb ( “vector.txt" , v_mem) ; The vector.txt file should contain eight 4-bit binary data separated by white spaces.
Example: Using External Files to Specify Test Patterns and to Record Simulation Results module eq2_file_tb(); reg [1:0] test_in0, test_in1; wire test_out; integer log_file, console_file, out_file; reg [3:0] v_mem [0:7]; integer i; eq2 uu (test_in0, test_in1, test_out); initial begin log_file = $fopen("eqlog.txt"); if (!log_file) $display("Cannot open log file"); console_file=32'h0000_0001; out_file = log_file | console_file; $readmemb("vector.txt", v_mem); module eq2(input [1:0] a, b, output equal); assign equal = (a[0] ~^ b[0]) & (a[1] ~^ b[1]); endmodule
Example, Contd. for (i=0; i<8; i=i+1) begin {test_in0, test_in1} = v_mem[i]; #200; end $fclose(log_file); $stop; end initial begin $fdisplay(out_file, " time test_in0 test_in1 test_out"); $fmonitor(out_file, "%10d %b %b %b", $time, test_in0, test_in1, test_out); end endmodule
Example, Contd. • The vector.txt file stores the test patterns specified in 4-bit binary format. The content of the file is: • The simulated result is written to the console and the log file, eqlog.txt • The content of the log file is: 00_00 01_00 01_11 10_10 10_00 11_11 11_01 00_10 # time test_in0 test_in1 test_out # 0 00 00 1 # 200 01 00 0 # 400 01 11 0 # 600 10 10 1 # 800 10 00 0 # 1000 11 11 1 # 1200 11 01 0 # 1400 00 10 0