% 
% The program finds the regions of operation (linear/saturation) for the 
% transistors in a 2-input NAND gate (pseduo-NMOS style) with ints two
% inputs connected together
%
% by Kuntal Roy
% ECE Dept., Purdue University
% royk@purdue.edu
% Date: 24-Sep-2009
%

clear all
close all

% These values are hardcoded in the equations
% However, you can use num2str for all the variables like vin as below

vdd = 1;        % in V
kpd = 30;       % in uA/V2
knd = 60;       % in uA/V2
vtp = -0.3;     % in V
vtn = 0.3;      % in V
wplp = 1;
wnln = 2;

% use vin values more that vtn
vin = [0.31 0.4:0.1:1.0];

cond = ['l' 's'];   % 'l' for linear and 's' for saturation

% current equations for PMOS
p_lin = '30*1*((-1+0.3)*(vout-1)-(vout-1)^2/2)';  % 
p_sat = '30*1*((-1+0.3)^2/2)';

% To be able to simulate for multiple VINs, use num2str inside for loop
% as below

% vin = 0.7;
% n1_lin = '60*2*((0.7-0.3-vx)*(vout-vx) - (vout-vx)^2/2)';
% n1_sat = '60*2*((0.7-0.3-vx)^2/2)';
% 
% n2_lin = '60*2*((0.7-0.3)*vx - vx^2/2)';
% n2_sat = '60*2*((0.7-0.3)^2/2)';

for ivin = vin
    
    matched  = 0;
    
    % current equations for NMOS 1 (upper one)
    n1_lin = strcat( ...
    '60*2*((', num2str(ivin, '%4.2f'),'-0.3-vx)*(vout-vx)-(vout-vx)^2/2)');
    n1_sat = strcat('60*2*((', num2str(ivin, '%4.2f'),'-0.3-vx)^2/2)');

    % current equations for NMOS 2 (below one)
    n2_lin = strcat('60*2*((', num2str(ivin, '%4.2f'),'-0.3)*vx-vx^2/2)');
    n2_sat = strcat('60*2*((', num2str(ivin, '%4.2f'),'-0.3)^2/2)');
    
    for icondp = cond            % pmos
        for icondn1 = cond       % nmos1
            for icondn2 = cond   % nmos2
                
                % selecting the equations for the corresponding region of
                % operation
                switch icondp
                    case  'l'    
                        pmoseq = p_lin; 
                    case  's'
                        pmoseq = p_sat;
                    otherwise
                        disp('Unknown region of opearion!')
                        return
                end
                
                switch icondn1
                    case  'l'    
                        nmos1eq = n1_lin; 
                    case  's'
                        nmos1eq = n1_sat;
                    otherwise
                        disp('Unknown region of opearion!')
                        return
                end
                
                switch icondn2
                    case  'l'    
                        nmos2eq = n2_lin; 
                    case  's'
                        nmos2eq = n2_sat;
                    otherwise
                        disp('Unknown region of opearion!')
                        return
                end
                
                % preparing two equations from three currents
                eq1 = strcat(pmoseq, '=', nmos1eq);
                eq2 = strcat(pmoseq, '=', nmos2eq);
                
                % solving the equations
                [vout_op, vx_op] = solve(eq1, eq2);
                
                % there may be multiple roots
                for ilop = 1:length(vout_op)
                    
                    % converting sym to double
                    svout_op = double(vout_op(ilop));
                    svx_op = double(vx_op(ilop));
                    
                    % ignore if imaginary solution
                    if ~isreal(svout_op) || ~isreal(svx_op)
                        continue;
                    end
                    
                    % ignore if negative or values greated than vdd
                    if svout_op < 0 || svout_op > vdd || ...
                            svx_op < 0 || svx_op > vdd
                        continue;
                    end

                    % determine vgs and vds for all the transistors
                    vgsp = (0-vdd);         vdsp = (svout_op-vdd);
                    vgsn1 = (ivin-svx_op);  vdsn1 = (svout_op-svx_op);
                    vgsn2 = ivin;           vdsn2 = svx_op;

                    % detrmining the region of operation for the
                    % transistors
                    if vgsp >= vtp    % cutoff
                        continue;
                    else
                        if vdsp > vgsp-vtp
                            pmos_cond = 'l';
                        else
                            pmos_cond = 's';
                        end
                    end

                    if vgsn1 <= vtn   % cutoff
                        continue;
                    else
                        if vdsn1 < vgsn1-vtn
                            nmos1_cond = 'l';
                        else
                            nmos1_cond = 's';
                        end
                    end
                    
                    if vgsn2 <= vtn   % cutoff
                        continue;
                    else
                        if vdsn2 < vgsn2-vtn
                            nmos2_cond = 'l';
                        else
                            nmos2_cond = 's';
                        end
                    end

                    % checking the condition that if the assumed conditions
                    % are correct or not
                    if pmos_cond == icondp && nmos1_cond == icondn1 ...
                            && nmos2_cond == icondn2
                        disp(sprintf('Matched: vin: %6.4f\tvout: %6.4f\tvx:%6.4f\tpmos: %c\tnmos1: %c\tnmos2: %c', ...
                            ivin, svout_op, svx_op, pmos_cond, nmos1_cond, nmos2_cond))
                        matched = 1;
                        break;
                    end
                end
                
                if matched == 1
                    break;
                end
            end
            
            if matched == 1
                break;
            end
        end
        
        if matched == 1
            break;
        end
    end
end

