In T* DRC deck, it is based on the voltage recognition CAD layer and
net connection to calculate the voltage difference between two
neighboring nets by the following formula:
\[
\Delta V = \max(V_H(\text{net1})-V_L(\text{net2}),
V_H(\text{net2})-V_L(\text{net1}))
\]
where \[
V_H(\text{netx}) = \max(V(\text{netx}))
\] and \[
V_L(\text{netx}) = \min(V(\text{netx}))
\]
The \(\Delta V\) will be
0 if two nets are connected as same potential
If \(V_L \gt V_H\)on a
net, DRC will report warning on this net
Voltage recognition CAD
Layer
Two method
voltage text layer
You place specific voltage text on specific drawing layer
voltage marker layer
Each voltage marker layer represent different voltage for specific
drawing layer
voltage text layer has higher priority than voltage
marker layer and is recommended
voltage text layer
For example M3
Process Layer
CAD Layer#
Voltage High
Voltage High Top (highest priority)
Voltage Low
Voltage Low Top (highest priority)
M3
63
110
112
111
113
where 63 is layer number,
110 ~ 113 is datatype
voltage marker layer
Different data type represent different voltage, like
class uvm_reg_map extends uvm_object; // Function: add_submap // // Add an address map // // Add the specified address map instance to this address map. // The address map is located at the specified base address. // The number of consecutive physical addresses occupied by the submap // depends on the number of bytes in the physical interface // that corresponds to the submap, // the number of addresses used in the submap and // the number of bytes in the // physical interface corresponding to this address map. // // An address map may be added to multiple address maps // if it is accessible from multiple physical interfaces. // An address map may only be added to an address map // in the grand-parent block of the address submap. // externvirtualfunctionvoid add_submap (uvm_reg_map child_map, uvm_reg_addr_t offset);
// Function: add_mem // // Add a memory // // Add the specified memory instance to this address map. // The memory is located at the specified base address and has the // specified access rights ("RW", "RO" or "WO"). // The number of consecutive physical addresses occupied by the memory // depends on the width and size of the memory and the number of bytes in the // physical interface corresponding to this address map. // // If ~unmapped~ is TRUE, the memory does not occupy any // physical addresses and the base address is ignored. // Unmapped memories require a user-defined ~frontdoor~ to be specified. // // A memory may be added to multiple address maps // if it is accessible from multiple physical interfaces. // A memory may only be added to an address map whose parent block // is the same as the memory's parent block. // externvirtualfunctionvoid add_mem (uvm_mem mem, uvm_reg_addr_t offset, string rights = "RW", bit unmapped=0, uvm_reg_frontdoor frontdoor=null); endclass: uvm_reg_map
Control-oriented coverage uses SystemVerilog Assertion (SVA) syntax
and the cover directive. It is used to cover sequences of signal values
over time. Assertions cannot be declared or "covered" in a class
declaration, so their use in an UVM verification environment is
restricted to interface only
Covergroup for
data-oriented coverage
CAN be declared as a class member and created in class
constructor
Data-oriented coverage uses the covergroup construct.
Covergroups can be declared and created in classes, therefore they are
essential for explicit coverage in a UVM verification environment.
Interface Monitor Coverage
covergroup new constructor is called
directly off the declaration of the covergroup, it does
not have a separate instance name
1
collected_pkts_cq = new();
The covergroup instance must be created in the class
constructor, not a UVM build_phase or other phase
method
Routing - packets flow from input ports to all legal outputs
latency - all packets received within speicied delay
The module UVC monitor receives data from the interface monitors via
analysis ports, so the implementation of the analysis write function is
a convenient place to put coverage code
Blocking methods are defined with
get() or put()tasks to allow
them to consume time
Non-blocking methods are defined with
try_get() or try_put()functions as they execute in zero time
Uni-Directional TLM
Methods Reference
Method
Description
Syntax
put()
Blocking put
virtual task put(iput TR t);
try_put()
Nonblocking put -return 1 if successful -return 0 if
not
virtual function bit try_put(input TR t);
can_put()
Nonblocking test put -return 1 if put would be
successful -return 0 if not
virtual function bit can_put();
get()
Blocking get
virtual task get(output TR t);
try_get()
Nonblocking get -return 1 if successful -return 0 if
not
virtual function bit try_get(output TR t);
can_get()
Nonblocking test get -return 1 if get would be
successful -return 0 if not
virtual function bit can_get();
peek()
Blocking peek
virtual task peek(output TR t);
try_peek()
Nonblocking peek -return 1 if successful -return 0 if
not
virtual function bit try_peek(output TR t);
can_peek
Nonblocking test peek -return 1 if peek would be
successful -return 0 if not
virtual function bit can_peek();
The peek() methods are similarly to the
get() methods, but copy the transaction
instead of removing it. The transaction is not
consumed, and a subsequent get or peek
operation will return the same transaction
Selected Connector and
Method Options
put
try_put
can_put
get
try_get
can_get
peek
try_peed
can_peek
uvm_put_*
✓
✓
✓
uvm_blocking_put_*
✓
uvm_nonblocking_put_*
✓
✓
uvm_get_*
✓
✓
✓
uvm_blocking_get_*
✓
uvm_nonblocking_get_*
✓
✓
uvm_get_peek_*
✓
✓
✓
✓
✓
✓
uvm_blocking_get_peek_*
✓
✓
uvm_nonblocking_get_peek_*
✓
✓
✓
✓
in the connectors above, * can be replaced by
port, imp, or export
All the methods for a specific connector type MUST
be implemented. If you define an uvm_put connection between
two compoents, then the component with the uvm_put_imp
object must provide implementations of ALL three put
methods, put, try_put and
can_put, even if these methods are not explicitly
called
TLM FIFO
The TLM FIFO is a FIFO component wrapped in get and
putimp connectors. This has the benefit of
data storage as well as providing implementations of the communication
methods. Components connected to the TLM FIFO are in control of data
transfer and can simply defined port connectors to initiate read and
write operations on the FIFO
uvm_tlm_fifo
The TLM FIFO object is effectively a FIFO component instantiated
between and connected to two components. The FIFO contains
imp connectors for the standard TLM put and
get/peek interfaces, therefore the user does
not have to defineimp ports or communication methods and
the FIRO takes care of data storage
The advantages are:
The user does not need to define communication methods or
imp connectors
The FIFO provides data storage between the write
(put) and read
(get/peek) components
There are a number of built-in methods for checking FIFO status
The disadvantages are:
The user must now initiate both sides of the transfer (both
get/peek and put) to complete the
transaction
Two connections must be made (both sides of the FIFO) rather than
one
The put_export and get_peek_export
connection objects have alternatives which provide subsets of the full
connector. For example, blocking_put_export and
nonblocking_put_export can replace put_export.
blocking_get_export, nonblocking_get_export
and get_export (as well as others) can replace
get_peek_export.
built-in methods
Method
Description
Syntax
new
Standard component constructor with an additional third argument,
size, which sets the maximum FIFO size. Default size is 1.
A size of 0 is an unbounded FIFO
function new(string name, uvm_component parent=null, int size=1);
size
Return size of FIFO. 0 indicates unbounded
FIFO
virtual function int size()
used
Return number of entries written to the FIFO
virtual function int used();
is_empty
Return 1 if used() is 0, otherwise
0
virtual function bit is empty();
is_full
Return 1 if used() is equal to size,
otherwise 0
virtual function bit is_full()
flush
Delete all entries from the FIFO, upon which used() is
0 and is_empty() is
1
virtual funciton void flush();
1 2 3
class uvm_tlm_fifo #(type T=int) extends uvm_tlm_fifo_base #(T); ... endclass
virtualclass uvm_tlm_fifo_base #(type T=int)extends uvm_component; uvm_put_imp #(T, this_type) put_export; uvm_get_peek_imp #(T, this_type) get_peek_export; uvm_analysis_port #(T) put_ap; uvm_analysis_port #(T) get_ap; // The following are aliases to the above put_export uvm_put_imp #(T, this_type) blocking_put_export; uvm_put_imp #(T, this_type) nonblocking_put_export; // The following are all aliased to the above get_peek_export, which provides // the superset of these interfaces. uvm_get_peek_imp #(T, this_type) blocking_get_export; uvm_get_peek_imp #(T, this_type) nonblocking_get_export; uvm_get_peek_imp #(T, this_type) get_export; uvm_get_peek_imp #(T, this_type) blocking_peek_export; uvm_get_peek_imp #(T, this_type) nonblocking_peek_export; uvm_get_peek_imp #(T, this_type) peek_export; uvm_get_peek_imp #(T, this_type) blocking_get_peek_export; uvm_get_peek_imp #(T, this_type) nonblocking_get_peek_export; functionnew(string name, uvm_component parent = null); super.new(name, parent); put_export = new("put_export", this); blocking_put_export = put_export; nonblocking_put_export = put_export; get_peek_export = new("get_peek_export", this); blocking_get_peek_export = get_peek_export; nonblocking_get_peek_export = get_peek_export; blocking_get_export = get_peek_export; nonblocking_get_export = get_peek_export; get_export = get_peek_export; blocking_peek_export = get_peek_export; nonblocking_peek_export = get_peek_export; peek_export = get_peek_export; put_ap = new("put_ap", this); get_ap = new("get_ap", this); endfunction
Analysis FIFO
uvm_tlm_analysis_fifo
uvm_tlm_analysis_fifo is a specialization of
uvm_tlm_fifo
Intended to buffer write transactions between the UVC monitor
analysis port and scoreboard
It has the following characteristics:
Unbounded (size=0)
analysis_export connector replaces
put_export
Support the analysis write method
By declaring the FIFO in the scoreboard, we can get
directly from the FIFO output.
However the write side connection of the FIFO to the interface UVC
monitor analysis port must be made (usually) in the testbench which has
visibility of both UVC and scoreboard components. The connection is made
using a connect method call inside the connect phase method
class uvm_tlm_analysis_fifo #(type T = int) extends uvm_tlm_fifo #(T);
// Port: analysis_export #(T) // // The analysis_export provides the write method to all connected analysis // ports and parent exports: // //| function void write (T t) // // Access via ports bound to this export is the normal mechanism for writing // to an analysis FIFO. // See write method of <uvm_tlm_if_base #(T1,T2)> for more information.
// Function: new // // This is the standard uvm_component constructor. ~name~ is the local name // of this component. The ~parent~ should be left unspecified when this // component is instantiated in statically elaborated constructs and must be // specified when this component is a child of another UVM component.
functionnew(string name , uvm_component parent = null); super.new(name, parent, 0); // analysis fifo must be unbounded analysis_export = new("analysis_export", this); endfunction
functionvoid write(input T t); void'(this.try_put(t)); // unbounded => must succeed endfunction
endclass
Analysis Port Broadcast
Analysis ports can (uniquely) be connected to any number of
imp connectors, including zero
The analysis port in component yapp_monitor will be
connected to both monitor_one and monitor_two
components. Each of the receiving components has an analysis imp
object and a write communication method declared. The write method
must have the same signature - i.e., they must be void functions called
write with a single input argument of type yapp_packet, but
their implementations can be completely different.
When the send_yapp port is connected to both
mone_in and mtwo_in imps, then a single write call
from yapp_monitor executes both write implementations in
monitor_one and monitor_two
For non-analysis connections
the TLM object must be connected to a single destination only, i.e.,
each non-analysis object has exactly one connect call.
An object may be used multiple times as the argument to a
connect, but exactly once as the caller. This is called
many-to-one connection. For example, many ports can be
connected to a one imp connector
For analysis connections
a TLM object can be connected to any number of destinations i.e.,
one analysis object can call connect many times. This is called
one-to-many connection. For example, one port can be
connected to many imp connectors. one-to-many is only
allowed for analysis connections. An analysis connection can also
NOT call connect. An unconnected TLM object is also only
allowed for analysis connections
Bi-Directional TLM
Transport Connection
Connector syntax
1 2 3
uvm_blocking_transport_XXX #(type REA, type RSP) uvm_nonblocking_transport_XXX #(type REQ, type RSP) uvm_transport_XXX #(type REQ, type RSP)
FIFO/analysis FIFOs do not perform any cloning on input transactions.
Therefore, you will need to check that the UVC monitors collect every
transaction into a different instance to avoid overwriting data in the
FIFOs
imp and port connectors are sufficient for
modeling most connections, but there is third connector,
export, which is used exclusively in hierarchical
connections.
Normally hierarchical routing is not required. A port on
an UVC monitor can be connected directly to an imp in a
scoreboard by specifying full hierarchical pathname (e.g.,
env.agent.monitor.analysis_port). The structure of an UVC is fixed and
so the user knows to look in the monitor component for the analysis
ports.
However the internal hierarchy of a module UVC is more arbitrary, and
it may be convenient to route all the module UVC connectors to the top
level to allow use without knowledge of the internal structure.
On the initiator side
ports are routed up the hierarchy via other port instances
On the target side
only the component which defines the communication method is allowed
to have an imp instance. So we need a third object to route
connections up the target side hierarchy - export
The hierarchical route must be connected at each level in the
direction of control flow:
1
initiator.connect(target)
Connection rules are as follows:
port initiators can be connected to port,
export or imp targets
export initiators can be connected to
export or imp targets
imp cannot be connection initiator. imp is
target only and is always the last connection object on a route
uvm_analysis_port
1 2 3
class uvm_analysis_port # ( type T = int ) extends uvm_port_base # (uvm_tlm_if_base #(T,T))
uvm_analysis_port
1 2 3
class uvm_analysis_port # ( type T = int ) extends uvm_port_base # (uvm_tlm_if_base #(T,T))
uvm_analysis_imp
1 2 3 4
class uvm_analysis_imp #( type T = int, type IMP = int ) extends uvm_port_base #(uvm_tlm_if_base #(T,T))
QA
What are the three distinct functions of a scoreboard?
Reference model, expected data storage and comparison
To how many consumer components can an analysis port be
connected?
Any number, including zero
What are the names of the two declarations made available by the
following macro:
1
`uvm_analysis_imp_decl(_possible)
Subclass uvm_analysis_imp_possible
Method write_possible
Why should a scoreboard clone the data received by a
write method?
The write method only sends a reference, therefore if
the sending component uses the same reference for every data item,
overwriting of data in the storage function of the scoreboard is
possible without cloning.
// YAPP Interface to the DUT yapp_if in0(clock, reset); // reset will now be generated by the Clock and Reset UVC // input: clock; output: reset, run_clock, clock_period clock_and_reset_if clk_rst_if(.clock(clock), .reset(reset), .run_clock(run_clock), .clock_period(clock_period)); hbus_if hif(.clock(clock), .reset(reset));
Error-[SV-LCM-PND] Package not defined ../sv/yapp_if.sv, 18 yapp_if, "yapp_pkg::" Package scope resolution failed. Token 'yapp_pkg' is not a package. Originating module 'yapp_if'. Move package definition before the use of the package.
As you can see , the uvm_config_db::set is after the
factory type_id::create, but receive_value
is still 100, I believe that build_phase
of upper hierarchy finish first before create lower components.
But the recommendation in literature and many web is place
uvm_config_db::set before create
a generalization of the continuous-time Fourier
transform
converts integro-differential equations into algebraic
equations
z-transforms
a generalization of the discrete-time Fourier
transform
converts difference equations into algebraic
equations
system function,transfer function: \(H(s)\)
frequency response: \(H(j\omega)\), if the ROC of \(H(s)\) includes the imaginary axis,
i.e.\(s=j\omega \in \text{ROC}\)
Laplace Transform
To specify the Laplace transform of a signal, both the
algebraic expression and the ROC are
required. The ROC is the range of values of \(s\) for the integral of \(t\) converges
bilateral Laplace transform
where \(s\) in the ROC and \(\mathfrak{Re}\{s\}=\sigma\)
The formal evaluation of the integral for a general \(X(s)\) requires the use of contour
integration in the complex plane. However, for the class of
rational transforms, the inverse Laplace transform can be
determined without directly evaluating eq. (9.56) by using the technique
of partial fraction expansion
ROC Property
The range of values of s for which the integral in converges is
referred to as the region of convergence (which we
abbreviate as ROC) of the Laplace transform
i.e. no pole in RHP for stable LTI sytem
System Causality
For a causal LTI system, the impulse response is zero for \(t \lt 0\) and thus is right
sided
causality implies that the ROC is to the right of the rightmost pole,
but the converse is not in general true, unless the system function is
rational
System Stability
The system is stable, or equivalently, that \(h(t)\) is absolutely
integrable and therefore has a Fourier transform, then the ROC
must include the entire \(j\omega\)-axis
all of the poles have negative real
parts
Unilateral Laplace transform
analyzing causal systems and, particularly,
systems specified by linear constant-coefficient differential equations
with nonzero initial conditions (i.e., systems
that are not initially at rest)
A particularly important difference between the properties of the
unilateral and bilateral transforms is the differentiation
property\(\frac{d}{dt}x(t)\)
Laplace Transform
Bilateral Laplace Transform
\(sX(s)\)
Unilateral Laplace Transform
\(sX(s)-x(0^-)\)
Integration by parts for unilateral Laplace transform
in Bilateral Laplace Transform
In fact, the initial- and final-value
theorems are basically unilateral transform
properties, as they apply only to signals \(x(t)\) that are identically \(0\) for \(t \lt
0\).
\(z\)-Transform
The \(z\)-transform for
discrete-time signals is the counterpart of the Laplace transform for
continuous-time signals
where \(z=re^{j\omega}\)
The \(z\)-transform evaluated on the
unit circle corresponds to the Fourier transform
ROC Property
system stability
The system is stable, or equivalently, that \(h[n]\) is absolutely
summable and therefore has a Fourier transform, then the ROC
must include the unit circle
Unilateral \(z\)-Transform
The time shifting property is different in the
unilateral case because the lower limit in the unilateral transform
definition is fixed at zero, \(x[n-n_0]\)
bilateral \(z\)-transform
unilateral \(z\)-transform
Initial rest condition
Initial Value Theorem
& Final Value Theorem
Laplace Transform
Two valuable Laplace transform theorem
Initial Value Theorem, which states that it is always possible to
determine the initial value of the time function \(f(t)\) from its Laplace transform \[
\lim _{s\to \infty}sF(s) = f(0^+)
\]
Final Value Theorem allows us to compute the constant
steady-state value of a time function given its Laplace
transform \[
\lim _{s\to 0}sF(s) = f(\infty)
\]
If \(f(t)\) is step response, then
\(f(0^+) = H(\infty)\) and \(f(\infty) = H(0)\), where \(H(s)\) is transfer function
\(z\)-transform
Initial Value Theorem \[
f[0]=\lim_{z\to\infty}F(z)
\] final value theorem \[
\lim_{n\to\infty}f[n]=\lim_{z\to1}(z-1)F(z)
\]
Staszewski, Robert Bogdan, and Poras T. Balsara. All-digital
frequency synthesizer in deep-submicron CMOS. John Wiley & Sons,
2006.
Connection between the Laplace transform
and the \(z\)-transform
A continuous-time system with transfer function \(H(s)\) that is identical in
structure to the discrete-time system \(H[z]\) except that the delays in \(H[z]\) are replaced by elements that delay
continuous-time signals.
The \(z\)-transform of a
sequence can be considered to be the Laplace transform of
impulses sampled train with a change of variable \(z = e^{sT}\) or \(s = \frac{1}{T}\ln z\)
Note that the transformation \(z =
e^{sT}\) transforms the imaginary axis in the \(s\) plane (\(s =
j\omega\)) into a unit circle in the \(z\) plane (\(z =
e^{sT} = e^{j\omega T}\), or \(|z| =
1\))
Note: \(\bar{h}(t)\) is the impulse
sampled version of \(h(t)\)
Note \(\bar{h}(t)\) is impulse
sampled signal, whose CTFT is scaled by \(\frac{1}{T}\) of continuous signal \(h(t)\), \(\overline{H}[e^{sT}]=\overline{H}(e^{sT})\)
is the approximation of continuous time system response, for example
summation \(\frac{1}{1-z^{-1}}\)\[
\frac{1}{1-z^{-1}} = \frac{1}{1-e^{-sT}} \approx \frac{1}{j\omega \cdot
T}
\] And we know transform of integral \(u(t)\) is \(\frac{1}{s}\), as expected there is ratio
\(T\)
impulse invariance
A straightforward and useful relationship between the
continuous-time impulse response\(h_c(t)\) and the discrete-time impulse
response\(h[n]\)
When \(h[n]\) and \(h_c(t)\) are related through the above
equation, i.e., the impulse response of the discrete-time system is a
scaled, sampled version of \(h_c(t)\), the discrete-time system
is said to be an impulse-invariant version of the continuous-time
system
we have \[
H(e^{j\hat{\omega}}) =
H_c\left(j\frac{\hat{\omega}}{T}\right),\space\space |\hat{\omega}| \lt
\pi
\]
\(h[n] = Th_c(nT)\) & \(T\) is small enough
only guarantees the output equivalence only at the
sampling instants, that is, \(y_c(nT) = y_r(nT)\)
Provided \(H_c(j\Omega)\) is
bandlimited & \(T \lt 1/2f_h\)
guarantees \(y_c(t) =
y_r(t)\)
The scaling of \(T\) can
alternatively be thought of as a normalization of the time domain, that
is average impulse response shall be same i.e., \(h[n]\times 1 = h(nT)\times T\)
Transfer function
& sampled impulse response
continuous-time filter designs to discrete-time designs through
techniques such as impulse invariance
useful functions
using fft
The outputs of the DFT are samples of the DTFT
using freqz
modeling as FIR filter, and the impulse response sequence of an FIR
filter is the same as the sequence of filter coefficients, we can
express the frequency response in terms of either the filter
coefficients or the impulse response
fft is used in freqz internally
freqz method is straightforward, which apply impulse
invariance criteria. Though fft is used for signal
processing mostly,
%% continuous system s = tf('s'); h = 2*pi/(2*pi+s); % First order lowpass filter with 3-dB frequency 1Hz [mag, phs, wout] = bode(h); fct = wout(:)/2/pi; Hct_dB = 20*log10(mag(:));
%% unit impulse response from transfer function subplot(2, 1, 2) z = tf('z', Ts); h = -0.131 + 0.595*z^(-1) -0.274*z^(-2); [y, t] = impulse(h); stem(t*1e10, y*Ts); % !!! y*Ts is essential grid on; title("unit impulse response"); xlabel('Time(\times 100ps)'); ylabel('mag');
impulse:
For discrete-time systems, the impulse response is the response to a
unit area pulse of length Ts and height 1/Ts,
where Ts is the sample time of the system. (This pulse
approaches \(\delta(t)\) as
Ts approaches zero.)
Scale output:
Multiply impulse output with sample period
Ts in order to correct 1/Ts height of
impulse function.
PSD transformation
If we have power spectrum or power spectrum density of both edge's
absolute jitter (\(x(n)\)) , \(P_{\text{xx}}\)
Then 1UI jitter is \(x_{\text{1UI}}(n)=x(n)-x(n-1)\), and Period
jitter is \(x_{\text{Period}}(n)=x(n)-x(n-2)\), which
can be modeled as FIR filter, \(H(\omega) =
1-z^{-k}\), i.e. \(k=1\) for 1UI
jitter and \(k=2\) Period jitter
\[\begin{align}
P_{\text{xx}}'(\omega) &= P_{\text{xx}}(\omega) \cdot \left|
1-z^{-k} \right|^2 \\
&= P_{\text{xx}}(\omega) \cdot \left| 1-(e^{j\omega
T_s})^{-k} \right|^2 \\
&= P_{\text{xx}}(\omega) \cdot \left| 1-e^{-j\omega T_s
k} \right|^2
\end{align}\]
an algebraic transformation between the variables \(s\) and \(z\) that maps the entire
imaginary\(j\Omega\)-axis in the
\(s\)-plane to one revolution of the
unit circle in the \(z\)-plane
\[\begin{align}
z &= \frac{1+s\frac{T_s}{2}}{1-s\frac{T_s}{2}} \\
s &= \frac{2}{T_s}\cdot \frac{1-z^{-1}}{1+z^{-1}}
\end{align}\]
where \(T_s\) is the sampling
period
frequency warping:
The bilinear transformation avoids the problem of
aliasing encountered with the use of impulse
invariance, because it maps the entire imaginary axis of the \(s\)-plane onto the unit circle in the \(z\)-plane
impulse invariancecannot be used to
map highpass continuous-time designs to high pass
discrete-time designs, since highpass continuous-time filters are
not bandlimited
Due to nonlinear warping of the frequency axis introduced by the
bilinear transformation, bilinear transformation applied to a
continuous-time differentiator will not result in a
discrete-time differentiator. However, impulse invariance can be applied
to bandlimited continuous-time differentiator
The feature of the frequency response of discrete-time
differentiators is that it is linear with frequency
The simple approximation \(z=e^{sT}\approx1+sT\), the first
equal come from impulse invariance
essentially
reference
Alan V. Oppenheim, Alan S. Willsky, and S. Hamid Nawab. 1996. Signals
& systems (2nd ed.)
Alan V Oppenheim, Ronald W. Schafer. 2010. Discrete-Time Signal
Processing, 3rd edition
B.P. Lathi, Roger Green. Linear Systems and Signals (The Oxford
Series in Electrical and Computer Engineering) 3rd Edition