ENEE 408C Lab Capstone Project: Digital System Design Spring 2006 Class Web Site: http://www.ece.umd.edu/class/enee408c TA’s Information Alessandro Geist ageist@umd.edu Office Hours: TBD Parameters: An N-bit Register module register (Q,D, clk, reset_n); parameter size=2; input [size-1:0] D; input clk; input reset_n; output[size-1:0] Q; reg [size-1:0] Q; always @ (posedge clk or negedge reset_n) begin if (!reset_n) // active low reset Q<=0; else Q<=D; end endmodule Parameters register r1(Q,D, clk, reset_n); register #(16) r2(Q2,D2, clk, reset_n); If multiple parameters: Some_module #(10,8) mod(a,b,c); Define Statements `define IN_SIZE 8 // num bits of input module some_module(A,B); input [`INPUT_SIZE-1:0] A,B; . . . endmodule Concatenation /////////////////////////// BCD to 7-Segment Display /////////////////////////////////// module sevensegment(a,b,c,d,e,f,g,W,X,Y,Z); output a,b,c,d,e,f,g; input W,X,Y,Z; reg a,b,c,d,e,f,g; always @ (W or X or Y or Z) begin case ({W,X,Y,Z}) 4'b0000: {a,b,c,d,e,f,g} = 7'b1111110; 4'b0001: {a,b,c,d,e,f,g} = 7'b0110000; 4'b0010: {a,b,c,d,e,f,g} = 7'b1101101; 4'b0011: {a,b,c,d,e,f,g} = 7'b1111001; 4'b0100: {a,b,c,d,e,f,g} = 7'b0110011; 4'b0101: {a,b,c,d,e,f,g} = 7'b1011011; 4'b0110: {a,b,c,d,e,f,g} = 7'b1011111; 4'b0111: {a,b,c,d,e,f,g} = 7'b1110000; 4'b1000: {a,b,c,d,e,f,g} = 7'b1111111; 4'b1001: {a,b,c,d,e,f,g} = 7'b1111011; default: {a,b,c,d,e,f,g} = 7'b0000000; endcase end// end always block endmodule // 0 // 1 // 2 // 3 // 4 // 5 // 6 // 7 // 8 // 9 // BLANK Loops A loop is synthesizable if it is static: The number of iterations is fixed and independent of the data Example: reg [2:0] i; reg [3:0] out; wire [3:0] a,b; always @ (a or b) begin for (i=0; i<=3; i=i+1) out[i] = a[i] & b[i]; end endmodule Example Unrolled: out[0] = a[0] & b[0]; out[1] = a[1] & b[1]; out[2] = a[2] & b[2]; out[3] = a[3] & b[3]; Generate Statements Generate Loop Generate loops can be used to create multiple instances of instances within a for loop. The for loop in a generate statement is similar to the regular for loop except for the following conditions: – The loop index variable must be a genvar variable, a special integer variable used in for loops. – The assignments in the loop control must assign to the same genvar. – The contents of the loop must be within a named begin–end block. The genvar variable is a special integer variable used in generate loops, which can be assigned only 0 or positive numbers. A genvar can only be assigned a value as part of a generate loop statement. A genvar variable can be defined outside of the generate block or within a generate block. If declared outside, the genvar variable can be used by any number of generate blocks. Conditional Generates Conditional generates can be created in two ways: – if –else statements – case statements Both of these statements can be used within the generate block. Here is an example of using a generate statement to control the type of adder used: // if-else generate if (adder_width < 8) ripple_carry # (adder_width) u1 (a, b, sum); else carry_look_ahead # (adder_width) u1 (a, b, sum); endgenerate Note: The expression inside the if statement must evaluate to a static value. Conditional Generates The following example uses the case statement to determine which adder is used, based on the parameter WIDTH: // case parameter WIDTH=1; generate case (WIDTH) 1: adder1 x1 (c0, sum, a, b, ci); 2: adder2 x1 (c0, sum, a, b, ci); default: adder # WIDTH (c0, sum, a, b, ci); endcase endgenerate Generate Statement Example Generating multiple module instances of otherModule and connecting them: module some_module(mod_out, A); parameter SIZE = 8; input [SIZE-1:0] A; output [SIZE-1:0] mod_out; wire [SIZE:0] intermediate; generate genvar I; for (i=0; i<SIZE; i=i+1) begin: instance_name otherModule name(mod_out[i], intermediate[i+1], A[i], intermediate[i]); end endgenerate endmodule Finite State Machines A finite state machine (FSM) is a circuit designed to sequence through specific patterns of states in a sequential manner. Each state is represented by a binary value. An FSM contains a sequential state register, and combinational next-state and output logic. Mealy and Moore Machines A Mealy state machine has outputs that are a function of the current state AND the primary inputs. A Moore state machine has outputs that are a function of the current state only. A combined Mealy/Moore state machine has … both. Numerous FSM ‘dialects’ exist in design tools. State Table Input A Input Hold Curren t State Next State Output Output Y_Me Y_Mo 0 1 0 X X X 000 000 001 000 001 000 1 0 0 0 0 1 1 X X 0 X X 1 0 001 010 011 011 010 011 011 000 1 0 0 1 1 0 1 1 1 X 0 X 011 100 100 000 1 0 1 1 State Diagram 0X/1 0 XX/0 1 000 1X/0 001 100 1 0X/0 00/1 1X/1 10/1 1 X1/0 011 010 XX/0 0 HDL Coding Style Typically the synchronous portion is simply the state <= next_state assignment. The next_state portion is done in a purely combinational block (or multiple blocks). – This is best done using a case statement rather than an if/else tree. – Defaults are easily set and excepted by unique cases. Resets Using an asynchronous reset – Ensures that the state machine is always initialized to a known valid state. – No need to decode any unused current state values and thus minimizes next state logic. Using a synchronous reset or no reset – Cannot predict initial state. – Must decode all states in the next state logic. – Can be a win if the next state logic is small compared to the additional size of the reset HW. State Encoding Sequential (“binary”) – Simple assignment Gray – Assigns states by the minimum logic difference in the state transition graph. – I.e., assign adjacent codes to adjacent states – Reduces the amount of logic needed to decode each state. Johnson – Only one bit changes. One-hot – One bit in the state register for each state. – A large number of flip-flops, but no decoding for states. – Can result in smaller and faster FSMs, especially for ASICs with large amounts of sequential logic relative to combinational logic resources. State Encoding (Example) Number Sequential Gray Johnson One-Hot 0 0000 0000 00000000 0000000000000001 1 0001 0001 00000001 0000000000000010 2 0010 0011 00000011 0000000000000100 3 0011 0010 00000111 0000000000001000 4 0100 0110 00001111 0000000000010000 5 0101 0111 00011111 0000000000100000 6 0110 0101 00111111 0000000001000000 7 0111 0100 01111111 0000000010000000 8 1000 1100 11111111 0000000100000000 9 1001 1101 11111110 0000001000000000 10 1010 1111 11111100 0000010000000000 11 1011 1110 11111000 0000100000000000 12 1100 1010 11110000 0001000000000000 13 1101 1011 11100000 0010000000000000 14 1110 1001 11000000 0100000000000000 15 1111 1000 10000000 1000000000000000 Coding a One-hot always @ (state) begin next_state = 0; case (1’b1) reg [15:0] state, state[S0]: next_state[S1] = next_state; 1; state[S1]: …; always @ (posedge clk) state[S2]: …; begin ... state <= #1 next_state; default: next_state = end 16’bx endcase end parameter S0=0, S1=1, S2=2, S3=3, …; Why to Use a One-hot Advantages – Easy to decode – one bit active for each state. – Compact decoding logic. – Fast. Disadvantages – Size But, compare the size of any state machine to the datapath that it is controlling. In many circumstances, the datapath dwarfs the state machine and the speed advantage is worth the tradeoff. `define STATE_BITS 2 `define S0 2'b00 `define S1 2'b01 `define S2 2'b11 module moore (z, state, reset_n, clk, x); output z; // data output output [`STATE_BITS-1: 0] state; input reset_n; input clk; input x; // single data input reg z; reg [`STATE_BITS-1:0] state; always @(state) begin case (state) `S0: z = 1'b0; `S1: z = 1'b0; `S2: z = 1'b1; default: z = 1'bX; // for all cases to be covered endcase end always @(negedge reset_n or posedge clk) begin if (~reset_n) state <= `S0; // initial state else begin case (state) `S0: begin if (x) state <= `S1; else state <= `S2; end `S1: begin if (!x) state <= `S2; end `S2: state <= `S0; default: state <= 2'b00; endcase end // end else end // end always endmodule `timescale 1ns/1ns `define PERIOD `define STATE_BITS 2 `define S0 2’b00 `define S1 2'b01 `define S2 2'b11 module test_state(); wire z; wire [`STATE_BITS-1:0] state; reg reset_n; reg clk; reg x; integer error_count; // instantiate module moore m1(z, state, reset_n, clk, x); initial begin clk = 0; error_count = 0; reset_n =0; # `PERIOD reset_n = 1; end always #(`PERIOD/2) clk = ~clk; task error_check; input [`STATE_BITS-1:0] expectedState, actualState; input expectedZ, actualZ; begin if (!((actualState == expectedState) && (actualZ == expectedZ))) begin error_count = error_count +1; $display(“ERROR: state = %0b, output = %0b at time %0t.”, actualState, actualZ, $time); end end endtask initial begin $display(“start simulation”); x = 0; // initial data value, check reset #`PERIOD; error_check(`S0, state, 1’b0, z); x = 0; // next go to state `S2 #`PERIOD; error_check(`S2, state, 1’b0, z); x = 1; // next go to state `S0 #`PERIOD; error_check(`S0, state, 1’b0, z); x = 1; // next, go to state `S1 #`PERIOD; error_check(`S1, state, 1’b0, z); end endmodule Functions and Tasks Functions and Tasks are similar to functions and subroutines in C Why? – More readable – More portable – More compact Functions Used to implement combinational behavior Cannot call tasks Cannot call functions recursively Cannot contain timing control (delay, event, wait) zero elapsed simulated time Must have at least one input Cannot have output or inout ports Must return a value (syntax: function_name = returned_value) Are invoked as operands in expressions Can be invoked from procedural and continuous assignment statements Functions: Example // Function Declaration function [7:0] twoscomplement; input [7:0] operand; operand = (~operand) + 1; endfunction // Calling the function assign A_comp = twoscomplement(A); Tasks Can call tasks and functions Can contain timing control can consume time Can only be called from procedural statements Any combination of input, output, and inout ports is allowed Can return values through the output and inout ports Are invoked as separate procedural statements Cannot be invoked from continuous assignment statements Recursion can be used All calls of a task use the same memory for variables Tasks: Example module task_example (a,b,c); input [7:0] a,b; output [7:0] c; reg [7:0] c; task adder; input [7:0] a,b; output [7:0] adder; reg c; integer i; begin c = 0; for (i = 0; i <= 7; i = i+1) begin adder[i] = a[i] ^ b[i] ^ c; c = (a[i] & b[i]) | (a[i] & c) | (b[i] & c); end end endtask always adder (a,b,c); // Call Task endmodule