Professional Documents
Culture Documents
Essential VHDL
Essential VHDL
A brief introduction to design with VHDL for ASIC design. Roger Traylor 9/7/01 Version 0.1 All rights reserved. No part of this publication may be reproduced, without the prior written permission of the author. Copyright 2001, Roger Traylor
Revision Record
rev 0.1 : Initial rough entry of material. 9/7/01 RLT
HDL Design
Traditionally, digital design was done with schematic entry. In todays very competitive business environment, building costeffective products in an quick fashion is best done with a top down methodology utilizing hardware description languages and synthesis.
shift_register: PROCESS (clk_50, reset_n, data_ena, serial_data, parallel_data) BEGIN IF (reset_n = 0) THEN parallel_data <= "00000000"; ELSIF (clk_50EVENT AND clk_50 = 1) THEN IF (data_ena = 1) THEN parallel_data(7) <= serial_data; --input gets input data FOR i IN 0 TO 6 LOOP parallel_data(i) <= parallel_data(i+1); --all other bits shift down END LOOP; ELSE parallel_data <= parallel_data; END IF; END IF; END PROCESS shift_register;
synthesis
HDLs - Motivation
Increased productivity shorter development cycles, more features, but........ still shorter time-to-market, 10-20K gates/day/engineer Flexible modeling capabilities. can represent designs of gates or systems description can be very abstract or very structural top-down, bottom-up, complexity hiding (abstraction) Design reuse is enabled. packages, libraries, support reusable, portable code Design changes are fast and easily done convert a 8-bit register to 64-bits........ four key strokes, and its done! exploration of alternative architectures can be done quickly Use of various design methodologies. top-down, bottom-up, complexity hiding (abstraction) Technology and vendor independence. same code can be targeted to CMOS, ECL, GaAs same code for: TI, NEC, LSI, TMSC....no changes! Enables use of logic synthesis which allows a investigation of the area and timing space. ripple adder or CLA?, How many stages of look ahead? HDLs can leverage software design environment tools. source code control, make les Using a standard language promotes clear communication of ideas and designs. schematic standards?... whats that... a tower of Babel.
Essential VHDL for ASICs 4
VHDL - Origins
Roots of VHDL are in the Very High Speed Integrated Circuit (VHSIC) Program launched in 1980 by the US Department of Defense (DOD).
The VHSIC program was an initiative by the DOD to extend integration levels and performance capabilities for military integrated circuits to meet or exceed those available in commercial ICs. The project was successful in that very large, high-speed circuits were able to be fabricated successfully. However, it became clear that there was a need for a standard programming language to describe and document the function and structure of these very complex digital circuits. Therefore, under the VHSIC program, the DOD launched another program to create a standard hardware description language. The result was the VHSIC hardware description language or VHDL.
The rest is history... In 1983, IBM, TI and Intermetrics were awarded the contract to develop VHDL. In 1985, VHDL V7.2 released to government. In 1987, VHDL became IEEE Standard 1076-1987. In 1993, VHDL restandardized to clarify and enhance the language resulting in VHDL Standard 1076-1993. In 1993, development began on the analog extension to VHDL, (VHDL-AMS).
Extends VHDL to non-digital devices and micro electromechanical components. This includes synthesis of analog circuits.
Typical ASIC project: concept to rst silicon about 9 months. 95% of designs work as the specication states. 60% of designs fail when integrated into the system.
The design was not the right one, but it works.
Technology is changing so fast, the only competitive advantage is to learn faster than your competitors. To design more stuff faster, your level of abstraction in design must increase. Using HDLs will help to make digital designers successful. (and employed!)
VHDL Modeling
A VHDL models consist of an Entity Declaration and a Architecture Body. The entity denes the interface, the architecture denes the function. The entity declaration names the entity and denes the interface to its environment. Entity Declaration Format: ENTITY entity_name IS [GENERIC (generic_list);] [PORT (port_list);] END ENTITY [entity_name]; There is a direct correspondence between a ENTITY and a block diagram symbol. For example: ENTITY nand_gate IS PORT( a : in std_logic; b : in std_logic; c : in std_logic; z : out std_logic); END ENTITY nand_gate;
nand_gate
a b c
Port Statement
The entities port statement identies the ports used by the entity to communicate with its environment Port Statement Format: PORT( name_list name_list name_list name_list : : : : mode mode mode mode type; type; type; type);
This is legal but poor form: ENTITY nand_gate IS PORT(a,d,e,f : in std_logic; b,j,q,l,y,v : in std_logic; w,k : in std_logic; z : out: std_logic); END nand_gate; This is much less error prone:
Use one line per signal. This allows adequate comments. Capitalize reserved names.
ENTITY nand_gate IS PORT( a : IN STD_LOGIC; b : IN STD_LOGIC; c : IN STD_LOGIC; z : OUT STD_LOGIC); END ENTITY nand_gate;
Essential VHDL for ASICs
10
Port Mode:
Identies the direction of data ow through the port. The PORT statement is optional. At the top level, none is needed. All ports must have an identied mode. Allowable Modes:
IN OUT INOUT BUFFER
Flow is into the entity Flow is out of the entity Flow may be either in or out An OUTPUT that can be read from
bobs_block
(mode:out) ram_wr_n
11
Architecture Body
The architecture body describes the operation of the component. Format: ARCHITECTURE body_name OF entity_name IS --this is the ->declarative area<--declare signals, variables, components, --subprograms BEGIN --this is the ->statement area<--in here go statements that describe --organization or functional operation of --the component --this is the execution part of the model END [body_name] The entity_name in the architecture statement must be the same as the entity declaration that describes the interface to the outside world. ENTITY entity_name IS
ARCHITECTURE body_name OF entity_name IS The body_name is a user-dened name that should uniquely describe the particular architecture model. ARCHITECTURE beh OF nand_gate IS ARCHITECTURE struct OF nand_gate IS Note: multiple architectures are allowed.
Essential VHDL for ASICs 12
Commenting Code
A double hyphen (--) indicates everything from that point on in that line is to be treated as a comment. ARCHITECTURE example OF xor_gate IS --The following is a silly example of how --to write comments in VHDL. BEGIN --comment from the beginning of a line a <= b XOR c; --or...comment from here on ---each line must have its own --comment marker unlike C -END [body_name] ----this is the end and there aint no more! Comments can be put anywhere except in the middle of a line of code.
Important Note: The tool used to prepare this document sometimes changes the rst
of a pair of quotes. In VHDL, only the quote marks that lean to the right or dont lean at all are used. For example, 1 should only have single quotes that lean to the right like the second one does. The quote mark we use is on the same key as the double quote.
13
You can create VHDL source code in any directory. VHDL source code le may be anything......but, Use the name of the design entity with the extension .vhd The above example would be in the le: nand3.vhd Question: Why the X in the above code?
14
Signal Assignment
The assignment operator (<=) is used to assign a waveform value to a signal. Format: target_object <= waveform; Examples: my_signal <= 0; --ties my_signal to ground his_signal <= my_signal; --connects two wires --vector signal assignment data_bus <= 0010; -- note double quote bigger_bus <= Xa5; -- hexadecimal numbers
15
Declaring Objects
Declaration Format: OBJECT_CLASS Examples: CONSTANT CONSTANT VARIABLE VARIABLE SIGNAL SIGNAL delay size sum voltage clock spam : : : : : : TIME:= 10ns; REAL:=5.25; REAL; INTEGER:=0; BIT; std_logic:=X; identifier: TYPE [:= init_val];
Objects in the port statement are classied as signals by default. Objects may be initialized at declaration time. If an object is not initialized, it assumes the left-most or minimum value for the type
16
Naming Objects
Valid characters:
alpha characters (a-z) numeric characters (0-9) underscore (_)
Names must consist of any number of alpha, numeric, or underline characters. Underscore must be proceeded and followed by alpha or numeric characters. The underscore can be used to separate adjacent digits in bit strings:
CONSTANT big_0 : STD_LOGIC_VECTOR(15 DOWNTO 0) := B"0000_0000_0000_0000";
Names are not case sensitive. (be consistent!, use lowercase!) Coding hints:
Use good names that are meaningful to others. If your code is good, somebody else will want to read it. Name signals by their function. For example, if you have a multiplexor select line that selects addresses, give it a name like address_select instead of sel_32a. Name blocks by their function. If a block generates control signals for a DRAM controller, call the block dram_ctl not something obscure like block_d.
17
18
VHDL Libraries
Before a VHDL design can be simulated, it must be compiled into a machine executable form. The compiled image is placed into a library where the simulator expects to nd the executable image. Therefore we must rst create a special directory called work.
Creating work
At the desired location in your directory tree, type:
vlib work
You will see a directory work created. You cannot create work with the UNIX mkdir command.
19
If you look in the work directory, you will see a subdirectory in work with the entity name aoi4. In there are the les necessary to simulate the design. With a clean compilation, we are ready to simulate.
20
21
force a force b force c force d run 100 force a force b run 100
0 0 0 0 1 1
According to our model we should see the z output assert to a zero when either a and b or c and d both become true. We can see the correct behavior in the wave window. HW: make 2-3 complex algebraic equations and implement them in VHDL. Students simulate and check them. Synthesize them and recheck with VHDL simulator. Print out gate level schematics.
22
We will make heavy usage of the vsim simulator. You are encouraged to explore and discover the different options and way to operate the simulator. For example, the force commands may be applied from a do le. This is a text le containing all the force and run commands. Try to use a force le to exhaustively test the aoi4 design. The documentation for the Model Technology tools may be found at: http://www.ece.orst.edu/comp/doc/tools/mti/mti_documentation.html
23
24
The synthesis tool needs a known library of logic cells (gates) to build the synthesized design from.
analyze src/aoi4.vhd -format vhdl -work work
Map the generic gates to the best ones in the library ami05.
write ./edif/aoi4.edf -format edif write ./vhdlout/aoi4.vhd -format vhdl
25
Create the edif and vhdlout directories where the edif and VHDL netlist will be put.
mkdir edif mldir vhdlout
Then type:
source script_simple
The tool elsyn reads the script le and executes the commands in the script.
26
27
In the statement area, we see this gate is connected to the ports of the entity with a component instantiation statement.
ix13 : aoi22 port map ( Y=>z, A0=>a, A1=>b, B0=>c, B1=>d);
We will study component instantiation in more detail later. Note also, the intermediate signals temp1 and temp2 have optimized away.
28
When design architect is invoked upon the design we see the following:
Here we can see the direct correspondence between the gate pins and the entity pins in the statement:
ix13 : aoi22 port map ( Y=>z, A0=>a, A1=>b, B0=>c, B1=>d);
30
Data Types
Data types identify a set of values an object may assume and the operations that may be performed on it. VHDL data type classications:
Scalar: numeric, enumeration and physical objects Composite: Arrays and records Access: Value sets that point to dynamic variables File: Collection of data objects outside the model
Certain scalar data types are predened in a package called std (standard) and do not require a type declaration statement. Examples:
boolean (true, false) bit (0, 1) integer (-2147483648 to 2147483647) real (-1.0E38 to 1.0E38) character (ascii character set) time (-2147483647 to 2147483647)
Type declarations are used through constructs called packages. We will use the package called std_logic_1164 in our class. It contains the common types, procedures and functions we normally need. A package is a group of related declarations and subprograms that serve a common purpose and can be reused in different parts of many models.
Essential VHDL for ASICs 31
Using std_logic_1164
The package std_logic_1164 is the package standardized by the IEEE that represents a nine-state logic value system known as MVL9. To use the package we say: LIBRARY ieee; USE ieee.std_logic_1164.ALL; The library clause makes a selected library containing desired packages visible to a model. The use clause makes the library packages visible to the model. USE clause format: USE symbolic_library.pkg_name.elements_to_use The name ieee is a symbolic name. It is mapped to: /usr/local/apps/mti/current/modeltech/ieee using the MTI utility vmap. You can see all the currently active mappings by typing: vmap We do not have to declare a library work. Its existence and location ./work is understood.
32
Using std_logic_1164
The nine states of std_logic_1164: (/usr/local/apps/mti/current/modeltech/vhdl_src/ieee/stdlogic.vhd) PACKAGE std_logic_1164 IS ---------------------------------------------- logic state system (unresolved) --------------------------------------------TYPE std_ulogic IS ( U, -- Uninitialized; the default value X, -- Forcing Unknown; bus contention 0, -- Forcing 0; logic zero 1, -- Forcing 1; logic one Z, -- High Impedance; 3-state buffer W, -- Weak Unknown; bus terminator L, -- Weak 0; pull down resistor H, -- Weak 1; pull up resistor - -- Dont care; used for synthesis); Why would we want all these values for signals?
33
VHDL Operators
Object type also identies the operations that may be performed on an object. Operators dened for predened data types in decreasing order of precedence:
Miscellaneous: **, ABS, NOT Multiplying Operators: *, /, MOD, REM Sign: +, Adding Operators: +, -,& Shift Operators: ROL, ROR, SLA, SLL, SRA, SRL Relational Operators: =, /=, <, <=, >, >= Logical Operators: AND, OR, NAND, NOR, XOR, XNOR
34
Overloading
Overloading allows standard operators to be applied to other user-dened data types. An example of overloading is the function AND, dened as: (/usr/local/apps/mti/current/modeltech/vhdl_src/ieee/stdlogic.vhd)
FUNCTION and (l : std_logic; r : std_logic) RETURN UX01; FUNCTION and (l, r: std_logic_vector ) RETURN std_logic_vector;
For Examples SIGNAL SIGNAL SIGNAL SIGNAL result0, signal1, signal2 : std_logic; result1 : std_logic_vector(31 DOWNTO 0); signal3 : std_logic_vector(31 DOWNTO 0); signal4 : std_logic_vector(31 DOWNTO 0);
BEGIN result0 <= signal1 AND signal2; -- simple AND result1 <= signal3 AND signal4; -- many ANDs END; If we synthesize this code, what gate realization will we get?
35
Concurrency
To model reality, VHDL processes certain statements concurrently. Example:
a
0 0
out1
0 0
out2
b
0 0
out3
0 0
out4
ARCHITETURE example of concurrent IS BEGIN out1 <= a AND b; out2 <= a NOR b; out3 <= b OR c; out4 <= b XOR c; END example;
36
Statement Activation
Signals connect concurrent statements. Concurrent statements activate or re when there is an event on a signal entering the statement. Example:
a
0 0
c
0 0
out1
b
ARCHITECTURE example OF concurrent IS SIGNAL c : std_logic; BEGIN c <= a NAND b; --nand gate out1 <= c XOR b; --xor gate END example; The NAND statement is activated by a change on either the a or b inputs. The XOR statement is activated by a change on either the b input or signal c. Note that additional signals (those not dened in the PORT clause) are dened in the architectures declarative area.
37
Concurrency Again
VHDL is inherently a concurrent language. All VHDL processes execute concurrently. Basic granularity of concurrency is the process. Concurrent signal assignments as actually one-line processes. c <= a NAND b; out1 <= c XOR b; --one line process --one line process
VHDL statements execute sequentially within a process. ARCHITECTURE example OF concurrency IS BEGIN hmmm: PROCESS (a,b,c) BEGIN c <= a NAND b; --do sequentially out1 <= c XOR b; --do sequentially END PROCESS hmmm;
How much time did it take to do the stuff in the process statement?
38
Concurrency
The body of the ARCHITECTURE area is composed of one or more concurrent statements. The concurrent statements we will use are:
Process - the basic unit of concurrency Assertion - a reporting mechanism Signal Assignment - communication between processes Component Instantiations - creating instances Generate Statements - creating structures
ARCHITECTURE showoff OF concurrency_stmts IS BEGIN ------concurrent club members only-----------BLOCK --PROCESS --ASSERT --a <= NOT b; --PROCEDURE --U1:nand1 PORT MAP(x,y,z); --instantiation --GENERATE ------concurrent club members only---------END showoff;
39
40
This creates:
big_bus big_bus(0) big_bus(1) big_bus(2) big_bus(3) big_bus(4) big_bus(5) big_bus(6) big_bus(7)
When we dene a bus as above, the width of the bus is dened by 7 DOWNTO 0. The position of the MSB is to the left of the DOWNTO keyword. The LSB bit is to the right of DOWNTO. The usual convention is to use DOWNTO. We will use this convention. UPTO is seldom used.
41
yellow_bus
red_bus
7:4 3:0
short_bus tall_bus
red_bus <= short_bus & tall_bus; -- bus concatenation --& is the concatenation operator -- MSBs of red_bus come from left most signal
red_bus
2
front_seat
red_bus
7:4
short_bus
shift_bus
7:4 3:0 3:0
red_bus
shift_bus <= red_bus(3 DOWNTO 0) & 0000; -- one bus created from ripping of one bus and -- concatenation of signals connected to ground -- shift bus is red_bus multiplied by 16
42
The binary format may include underscores to increase readability. The underscores do not effect the value. Values of bit string literals are inclosed in double quotes. For example: 1101 Values of bit literals are inclosed in single quotes. For example: Z
43
44
--sel input
45
When synthesized:
latch, not FF
What happened?
- How does a transparent latch operate? - What is the truth table for the decoder to the latch clk pin? sel(2:0) latch enable pin behavior 000 1 latch is transparent 001 1 ditto 010 1 ditto 011 1 ditto 100 1 ditto 101 0 latch is in hold state 110 0 hold state 111 0 hold state
46
This statement executes when any the discriminant, value or choice expressions changes value. When it does execute, the choice clauses are evaluated. The target signal is assigned the value corresponding to the choice that matches the discriminant.
About OTHERS
The keyword OTHERS can be powerfully used in many situations. In general it is used to allow matching to an unspecied number of possible values of a variable. There ay be only one alternative that uses the others choice and if included in a list, it must be the last choice. In essence, it says, if a match has not yet been found and the value of the variable is within range of its type, then match with OTHERS. We will see several other uses of OTHERS in the future.
47
OTHERS again
Here we see OTHERS used to match cases where sel is not 1 or 0 in the WHEN OTHERS clause. i.e.: (OTHERS => X) WHEN OTHERS; OTHERS is also used to provide a shorthand method of saying, make all the bits of the target signal X for however many bits are in target signal. (OTHERS => X) WHEN OTHERS;
48
How will this circuit react to sel(2:0) values greater than 100?
49
Making Choices
When we want the same target signal assignment to happen for several discriminant choices how do we specify it? Lets alter the function of our mux example as follows. The entity declaration is identical to before.
ARCHITECTURE beh OF mux5_1_1wide IS BEGIN WITH sel SELECT z_out <= a_input WHEN 000 | 001 | 111, b_input WHEN 011 | 101, c_input WHEN 010, d_input WHEN 100, e_input WHEN 110, X WHEN OTHERS; END beh;
The signal z_out gets the value of a_input when sel is equal to 000, 001 or 111. Signal z_out gets the value of b_input when sel is equal to 011 or 101. The synthesized version of this mux looks like this:
As you can see, once a model is synthesized it can be hard to gure out how it works.
50
Example: u1 : reg1 PORT MAP(d=>d0,clk=>clk,q=>q0); label the pin clk on reg1 wire that pin clock is connected to
component type
Locally declared signals do not have an associated mode and can connect to a local port of any mode.
51
Labels
Labels are used to provide internal documentation. May be used with:
Concurrent Assertion Statements Concurrent Signal Assignments Process Statements Loop Statements Generate Statements
52
Component Instantiation
5:1 mux using component instantiaion:
--5:1 mux, 1 bit wide LIBRARY ieee; USE ieee.std_logic_1164.ALL; LIBRARY adk; USE adk.all; ENTITY mux5_1_1wide IS PORT( a_input : IN STD_LOGIC; --input a b_input : IN STD_LOGIC; --input b c_input : IN STD_LOGIC; --input c d_input : IN STD_LOGIC; --input d e_input : IN STD_LOGIC; --input e sel : IN STD_LOGIC_VECTOR(2 DOWNTO 0); z_out : OUT STD_LOGIC --data out ); END mux5_1_1wide; ARCHITECTURE beh OF mux5_1_1wide IS SIGNAL temp0, temp1, temp2, temp3 : STD_LOGIC; COMPONENT mux21 PORT( a0,a1,s0 : IN STD_LOGIC; y : OUT STD_LOGIC); END COMPONENT; COMPONENT inv01 PORT( a : IN STD_LOGIC; y : OUT STD_LOGIC); END COMPONENT; BEGIN U1 : mux21 PORT MAP(a0 => a_input, a1 => b_input, s0 => sel(0), y => temp0); U2 : mux21 PORT MAP(a0 => c_input, a1 => d_input, s0 => sel(0), y => temp1); U3 : mux21 PORT MAP(a0 => temp0, a1 => temp1, s0 => sel(1), y => temp2); U4 : mux21 PORT MAP(a0 => temp2, a1 => e_input, s0 => sel(2), y => temp3); U5 : inv01 PORT MAP(a => temp3, y => z_out); END beh;
--sel input
53
54
Before we can use the cells in an instantiation statement, we must declare them. This is seen in the statements:
COMPONENT mux21 PORT( a0,a1,s0 : IN STD_LOGIC; y : OUT STD_LOGIC); END COMPONENT; COMPONENT inv01 PORT( a : IN STD_LOGIC; y : OUT STD_LOGIC); END COMPONENT;
To wire the mux21 cells together, temporary signals, temp0, temp1, temp2 and temp3 were declared.
SIGNAL temp0, temp1, temp2, temp3 : STD_LOGIC;
The PORT MAP statement describes the connections between pins of the cell and the signals. The connections are described by the format:
pin_on_module => signal_name,
The rst name is the module pin name, the second is the name of the signal the pin is to be connected to. This format is called named association. With named association, the order of associations is not required to be in the same order as port declaration in the component.
55
This form is not preferred because any change in the port list (it often happens in the design phase) will be difcult to incorporate. Try doing it for entities with 50 or more signals and youll begin to appreciate the point. For example, some real code.......
56
57
Now, lets say you need to add an extra signal in the module dramfo. You want to put it just after ueol_cntr_wen. But lets say your signals do not necessarily have the same names as the pins. This means you would have to manually count through the list of signals to nd out where to put the new one in the port map in exactly the same order. How would you know for sure its in the right position? Count through the list again! Do you have time to do this?
58
The initalization expression := 0 in the port declaration states that the input signals a_input , b_input, c_input and d_input will take on the default value0 if they are left unconnected by a component instantiation. Thus we could instantiate the 4:1 mux as follows:
U1 : mux41 PORT MAP(a0 a1 a2 a3 sel z_out => a_input, => b_input, => c_input, => OPEN, --a3 is assigned the value 0 => sel_input), => data_out);
Unconnected output ports are also designated by using the keyword OPEN. However, the associated design entity does not have to supply a default port value. Here is an adder with a unused carry output.
U17 : adder PORT MAP(a_in => a_data, b_in => b_data, sum => output, carry_out => OPEN);
59
The expressions supplied as connections to the module or cell pins must be constant values only.
60
FOR Scheme Format: label : FOR identifier IN range GENERATE concurrent_statements; END GENERATE [label];
61
62
63
q(0)
q(1)
q(2)
q(7)
Suppose furthermore that we had previously dened the following components: ENTITY dff IS PORT(d, clk, en q, qn END ENTITY dff; : IN : OUT std_logic; std_logic);
std_logic; std_logic);
64
Using GENERATE
From the block diagram we know what the entity should look like.
ENTITY sr8 IS PORT( din : IN sel : IN shift : IN scan_in : IN clk : IN enable : IN dout : OUT
std_logic_vector(7 DOWNTO 0); std_logic; std_logic; std_logic; sed_logic; std_logic; std_logic_vector(7 DOWNTO 0));
Within the architecture statement we have to declare the components within the declaration region before using them. This is done as follows:
ARCHITECTURE example OF sr8 IS --declare components in declaration area COMPONENT dff IS PORT(d, clk, en : IN std_logic; q, qn : OUT std_logic); END COMPONENT; COMPONENT mux21 IS PORT(a, b, sel : IN std_logic; z : OUT std_logic); END COMPONENT;
Component declarations look just like entity clauses, except COMPONENT replaces ENTITY. Use cut and paste to prevent mistakes!
65
Using Generate
After the component declarations, we declare the internal signal.
SIGNAL mux_out : IN std_logic_vector(7 DOWNTO 0);
66
Simulators stop when the severity level matches or exceeds the specied severity level. Simulators generally default to a severity level of failure
67
Assert Statements
Assert statements may appear within:
concurrent statement areas sequential statement areas statement area of entity declaration
Example: ENTITY rs_flip_flop IS PORT(r, s : IN std_logic; q, qn : OUT std_logic); END rs_flip_flop; ARCHITECTURE behav OF rs_flip_flop IS BEGIN ASSERT NOT (r = 1 AND s = 1) REPORT race condition! SEVERITY FAILURE; * * * END behav;
Remember, the ASSERT statement triggers when the specied condition is false.
68
Example:
ARCHITECTURE example OF nand_gate IS BEGIN nand_gate: PROCESS (a,b) BEGIN IF a = 1 AND b = 1 THEN z <= 0; ELSE z <= 1; END IF; END PROCESS nand_gate;
Why use a process? Some behavior is easier and more natural to describe in a sequential manner. The next state decoder in a state machine is an example.
69
What happens if a signal is left out of the sensitivity list? What does the synthesis tool do with the sensitivity list?
Avoid problems with sensitivity list omissions by compiling with sythesis check on. Like this:
vcom -93 -check_synthesis test.vhd
70
71
Delay Types
VHDL signal assignment statements prescribe an amount of time that must transpire before a signal assumes its new value. This prescribed delay can be in one of three forms:
Transport:
input
delay
output
Signal assignment is actually a scheduling for a future value to be placed on the signal. Signals maintain their original value until the time for the scheduled update to occur. Any signal assignment will incur a delay of one of the three types above.
72
input
output
10
15
20
25
30
35
40
45
73
input
output
10
15
20
25
30
35
40
45
When not used, the REJECT clause defaults to the value of the AFTER clause. Inertial delay acts like a real gate. It eats pulses narrower in width than the propagation delay.
74
input
output
10
15
20
25
30
35
40
45
75
minimum time step necessary so that some signals can take on their new values
processes then determine if the new signal values satisfy the
76
Sequential Operations
Statements within processes are executed in the order in which they are written. The sequential statements we will look at are:
Variable Assignment Signal Assignment* If Statement Case Statement Loops Next Statement Exit Statement Return Statement Null Statement Procedure Call Assertion Statement*
77
ARCHITECTURE example OF funny_gate IS SIGNAL c : STD_LOGIC; BEGIN funny: PROCESS (a,b,c) VARIABLE temp : std_logic; BEGIN temp := a AND b; z <= temp OR c; END PROCESS funny; END ARCHITECTURE example; Variables assume value instantly. Variables simulate more quickly since they have no time dimension. Remember, variables and signals have different assignment operators:
a <= new_value; --signal assignment a := new_value; --variable assignment
78
--simple IF (latch)
--IF-ELSE
IF condition THEN -- sequential statements ELSIF condition THEN -- sequential statements ELSE -- sequential statements END IF;
--IF-ELSIF-ELSE
79
--a very simple gate IF (lucky = 1) THEN buy_lottery_tickets <= 1; ELSE buy_lottery_tickets <= 0; END IF;
--a edge triggered 4-bit counter with enable --and asynchronous reset IF (reset = 1) THEN cnt <= 0000; ELSIF (clkEVENT AND clk = 1) THEN IF enable = 1 THEN cnt <= cnt + 1; END IF ; END IF;
80
81
IF Implies Priority
The if statement implies a priority in how signals are assigned to the logic synthesized. See the code segment below and the synthesized gates.
ARCHITECTURE tuesday OF example IS BEGIN wow: PROCESS (a, b, c, d, potato, carrot, beet, spinach, radish) BEGIN IF (a = 1) THEN vegatable <= potato; ELSIF (b = 1) THEN vegatable <= carrot; ELSIF (c = 1) THEN vegatable <= beet; ELSIF (d = 1) THEN vegatable <= spinach; ELSE vegatable <= radish; END IF; END PROCESS wow; END ARCHITECTURE tuesday;
what are the delays for each path? Note how signal with the smallest gate delay through the logic was the rst one listed. You can use such behavior to your advantage. Note that use of excessively nested IF statements can yield logic with lots of gate delay. Beyond about four levels of IF statement, the CASE statement will typically yield a faster implementation of the circuit.
82
In area_report.txt, we see:
******************************************************* Cell: example View: tuesday Library: work ******************************************************* Cell Library References Total Area ao21 ami05_typ 2 x 1 2 gates mux21 ami05_typ 2 x 2 4 gates nor02 ami05_typ 2 x 1 2 gates Number of gates : 8
83
84
Relational Operators
The IF statement uses relational operators extensively. Relational operators return Boolean values (true, false) as their result. OperatorOperation = /= < <= > >= equal not equal less than less than or equal greater than greater than or equal
The expression for signal assignment and less than or equal are the same. They are distinguished by the usage context.
85
CASE Statement
Controls execution of one or more sequential statements. Format: CASE expression IS WHEN expression_value0 => sequential_stmt; WHEN expression_value1 => sequential_stmt; END CASE; Example: --a four to one mux mux: PROCESS (sel, a, b, c, d) BEGIN CASE sel IS WHEN 00 => out <= a; WHEN 01 => out <= b; WHEN 10 => out <= c; WHEN 11 => out <= d; WHEN OTHERS => out <= X; END CASE ; END PROCESS mux; Either every possible value of expression_value must be enumerated, or the last choice must contain an OTHERS clause.
86
With the exception of spinach, the number of gate delays from each signal input to output is four. The gate delays in the IF example varied from 1 to 8 gate delays. However, this function for CASE could be coded better.
87
This encoding of the desired function is much cleaner, faster and smaller. Its seldom you get all three, so take it when you can. Examining the area and delay numbers between this and the IF implementation shows the superiority of CASE for this situation. Be careful however, sometimes CASE may loose depending upon the circumstances! Blanket statements about synthesis results with different constructs should not be made. Examine each situation individually, and THINK!
Essential VHDL for ASICs 88
From delay_report.txt
Critical Path Report
Critical path #1, potato Critical path #2, beet Critical path #3, carrot Critical path #4, radish Critical path #5, select_bus(0) Critical path #6, select_bus(0) Critical path #7, select_bus(1) Critical path #8, spinach Critical path #9, select_bus(2) to to to to to to to to to vegatable vegatable vegatable vegatable vegatable vegatable vegatable vegatable vegatable 1.83 1.83 1.82 1.81 1.72 1.72 1.19 0.74 0.64
89
90
The gate realization of this overly specied mux is obviously a little messy. This also seen in the reports from synthesis. The worst case path from the delay_report.txt gives us:
Critical path #1, beet to vegatable, 2.17ns
This less than optimal solution leads to the second reason for the use of X here; logic minimization.
91
What does the synthesis tool do? There is no gate that can produce a X output except when malfunctioning. How can it make a set of gates to produce an X output? The answer is that it doesnt. The synthesizer treats the X in this case as a dont care. This is just like the dont care in a Karnough map. It allow the synthesis to optimize (reduce) the gate count if possible. The simulator treats the X as a value to be propagated in simulation if an error happens. In fact, we can use another value in the mux statement; the dont care value, . So we could have coded the mux as follows:
--dont do this! CASE select_bus WHEN "000" => WHEN "001" => WHEN "010" => WHEN "011" => WHEN "100" => WHEN OTHERS => END CASE; IS vegatable vegatable vegatable vegatable vegatable vegatable
This would allow the same optimizations as the X for the OTHERS case but the behavior of the simulation in the case of a - being propagated could be library and simulator dependent. This would NOT be a be a good way to code a mux even though the synthesized circuit is identical to the mux with the OTHERS statement using X.
92
the synthesizer can create a small, fast circuit that behaves properly. One basic premise of how we want to code our designs is that we want the simulation of our code to act exactly as the gate implementation. If a real mux had a metastable (think X) input, the output would be metastable (X), not some valid (0 or 1) state. The proper use of the dont care operator is found in creating complex combinatorial logic and in state machine state assignments. In that context, the dont care operator really shines. We will see some examples of this soon.
93
Loops
Sequences of statements that are executed repeatedly. Types of loops:
For (most common usage) While Loop with exit construct (we skip this)
General Format: [loop_label:] iteration_scheme --FOR, WHILE LOOP --sequence_of_statements; END LOOP[loop_label];
94
For Loop
Statements are executed once for each value in the loop parameters range Loop parameter is implicitly declared and may not be modied from within loop or used outside loop. Format: [label:] FOR loop_parameter IN discrete_range LOOP --sequential_statements END LOOP[label]; Example: PROCESS (ray_in) BEGIN --connect wires in a two busses label: FOR index IN 0 TO 7 LOOP ray_out(index) <= ray_in(index); END LOOP label; END PROCESS;
95
While Loop
Execution of statements within loop is controlled by Boolean condition. Condition is evaluated before each repetition of loop. Format: WHILE boolean_expression LOOP --sequential_expressions END LOOP; Example: p1: PROCESS (ray_in) VARIABLE index : integer := 0; BEGIN from_in_to_out: WHILE index < 8 LOOP ray_out(index) <= ray_in(index); index := index + 1; END LOOP from_in_to_out; END PROCESS p1;
96
Attributes
Attributes specify extra information about some aspect of a VHDL model. There are a number of predened attributes provide a way to query arrays, bit, and bit vectors. Additional attributes may be dened by the user. Format: object_nameattribute_designator The is referred to as tick. Example: ELSIF (clkEVENT AND clk = 1) THEN
97
98
Using Attributes
Rising clock edge: clkEVENT and clk = 1 OR: NOT clkSTABLE AND clk =1 Falling clock edge: clkEVENT AND clk = 0 Checking for too short pulse width: ASSERT (resetLAST_EVENT >= 3ns) REPORT reset pulse too short!; Checking stability of a signal: signalSTABLE(10ns)
99
Generic Clause
Generics may be used for readability, maintenance and conguration. They allow a component to be customized by creating a parameter to be passed on to the architecture. Format: GENERIC (generic_name:type[:= default_value]); If default_value is missing, it must be present when the component is instantiated.
Example: ENTITY half_adder IS GENERIC( tpd_result : delay := 4ns; tpd_carry : delay := 3ns); PORT( x IN : std_logic; y IN : std_logic; z OUT : std_ulogic); END nand_gate; ARCHITECTURE dataflow OF half_adder BEGIN I result <= x XOR y AFTER tpd_result; carry <= x AND y AFTER tpd_carry; END dataflow;
100
ARCHITECTURE wed OF storage IS BEGIN infer_latch: PROCESS (enable, data_in) BEGIN IF enable = 1 THEN data_out <= data_in; END IF; --look ma, no else! END PROCESS infer_latch; END ARCHITECTURE wed;
101
Latch Inference
In our library, the enable is shown as going to the CLK input of the latch. This is misleading as the input should properly be called EN or something like that. If I nd the time maybe Ill change these someday. The small size of the latches is reected in the area report:
Cell latch Library ami05_typ References 4 x 2 Total Area 10 gates 10
Number of gates :
This is of course relative to the size of a 2-input NAND gate. In other words, the area of each latch is about the same as 2, 2-input NAND gates! When we synthesized, the transcript told of the impending latch inference:
-- Compiling root entity storage(wed) "/nfs/guille/u1/t/traylor/ece574/src/storage.vhd",line 8: Warning, data_out is not always assigned. latches could be needed.
Always watch tool transcripts. They can be very informative. Sometime they can save your bacon.
102
103
Sometime back we stated that IF with ELSE infers a latch. Well... that is usually true. Here is an exception. The line:
IF (clockEVENT AND clock = 1) THEN
is special to the synthesis tool. The conditional statement for the IF uses the attribute which looks for a change in the signal clock (clockEVENT). This is ANDed with the condition that clock is now 1 (AND clock = 1). The conditional is looking for a rising edge of the signal clock. Therefore, if there is a rising edge, the statement under the IF will be executed and at no other time. So when the clock rises, data_out will get the value present at data_in. Since a D ip-op is the only cell that can satisfy this condition and can hold the value once it is acquired it is used to implement the circuit. The conditional (clockEVENT AND clock = 1)really forms the recipe for a D-type rising edge ip op. A ELSE clause could be added to the IF statement that explicitly tells the old value to be held. This is not at all harmful, but is redundant and is ignored by the synthesis tool. An example of this is shown below:
infer_dff: PROCESS (clock, data_in) BEGIN IF (clockEVENT AND clock = 1) THEN data_out <= data_in; --get new value ELSE data_out <= data_out; --hold old value...UNNECESSARY END IF; END PROCESS infer_dff;
104
105
This looks a little shy. 24 + 1 = 24? At any rate, (assuming round off error) the ip ops are roughly 6 gates a piece. So to summarize the relative sizes of latches and ip ops : CASE CELL SIZE latch no reset latch 2 gates latch with reset latchr 3 gates ip op with no reset dff 5 gates ip op with reset dffr 6 gates These numbers are valid only for our library. Other libraries will vary. However, the relative sizes are consistent with most any CMOS library.
106
D Q
output signals
R clock reset The Moore state machine consists of two basic blocks, next state decode (or steering) logic, and some state storage usually (always for our case) D-type ip ops. Inputs are applied to the next state decode block along with the present state to create the next state output. The ip ops simply hold the value of the present state. In the example above, the only output signals are the outputs of the state ip ops. Alternatively, the ip op outputs could be decoded to create the output signals. For a rst example we will look at the state machine in TAS which holds the state of what type of header is being received, waiting or temp_pkt. First we look at the state diagram.
107
reset waiting byte_assembled* byte_cnt_ps = byte4 All your state machines should be documented in roughly this fashion. The name of the process holding the code for the state machine is the name of the state machine. In this case it is header_type_sm. Every state machine has an arc from reset. This indicates what state the state machine goes to when a reset is applied. The diagram is worthless without knowing what the initial state is. Each state in this example is given a name. In this case we are using a type for the states that is an enumerated state type. We will see what this means to the code later. For now, it provides a easy way to understand and to talk about what and how the state machine works. Each possible transition between states is shown via an arc with the condition for the transition to occur shown. The condition need not be in VHDL syntax but should be understandable to the reader. Typically (highly recommended) logic expressions are given with active high assertion assumed. It should be understood that all transitions occur on the clock edge. Outputs from the state machine should be listed. The only outputs from this state machine are its present state. Most likely, some other state machine is watching this ones state to determine its next state. temp_pkt
108
The TYPE declaration states that we have a type called header_type_type and that the two only states for this type are waiting and temp_pkt. Having done this we can declare two signals for our present state and next state vectors called header_type_ps and header_type_ns. Note that the vectors get their names from the state machine they are apart of plus the _ps or _ns to distinguish present or next state vectors. This style of state machine state coding is called enumerated state encoding. It is exible in the sense that the synthesis tool is left to make the decision about how to assign a bit pattern to each state. More about this later. Now using these state declarations, lets make the process that creates the state machine.
109
First we see that the process label is consistent with the documentation and the signal names we have assigned. Also all the signals that may be read are listed in the process sensitivity list for the process.
header_type_sm: PROCESS (clk_50, reset_n, a5_or_c3, byte_assembled, byte_cnt_ps, header_type_ps, header_type_ns)
Next the ip ops are created to hold the present state. This is what is commonly called the clocked or synchronous part since it is controlled by the clock.
110
Here we see an active low asynchronous reset that forces the present state to become the state called waiting. It does so without regard to the clock. That is why it is called an asynchronous reset. Following the reset clause, the clock tick event clause identies the following statements to be generating ip ops. The ip ops it creates are rising edge sensitive and cause the signal header_type_ps to take on the value of header_type_ns at the rising clock edge. This concludes the clocked part of the process. We have created the necessary state ip ops and connected the D inputs to header_type_ns and the Q outputs to header_type_ps. Now we will create the next state steering logic. It consists only of gates, i.e.; combinatorial logic. This part of the process is thus commonly called the combinatorial part of the process.
111
To clearly make the next state logic, a structure is created where IF statements are tucked under each distinct CASE state possibility. Each CASE possibility is a state in the state machine. Given a present state the IF statements determine from the input conditions what the next state is to be. To further illustrate this: The CASE statement enumerates each possible present state:
CASE header_type_ps IS WHEN waiting => --bla, bla, bla WHEN temp_pkt => --bla, bla, bla END CASE
In any given state the IF determines the input conditions to steer the machine to the next state. For example:
WHEN temp_pkt => IF (byte_assembled = 1) AND (byte_cnt_ps = byte4) THEN header_type_ns <= waiting; --go to waiting if IF is true ELSE header_type_ns <= temp_pkt; --else, stay put END IF ;
112
This tells us that the synthesis tool selected the value of 0 to represent the state waiting, and the value 1 to represent the state temp_pkt. This makes since because we have only two states, thus 0 and 1 can represent them. We would furthermore expect only one ip op to be needed. So the schematic looks like this: (clock and reset wiring omitted)
You might say, Thats not the way I would do it. But for the circuit this state machine was taken from, this was what it considered an optimal realization. Study the tas design le ctrl_50m.vhd and you can probably gure out some of the how and why the circuit was built this way. The next state steering logic can be clearly seen to the left of the state storage (ip op).
113
Enumerated Encoding
Using enumerated state encoding allows the synthesis tool to determine the optimal encoding for the state machine. If speed is the primary concern, a state machine could be created using one hot encoding. If minimum gate count is the most important criterion, a binary encoding might be best. For minimum noise or to minimize decoding glitching in outputs, grey coding might be best. Four different ways to encode a 2 bit state machine might be like this: binary one hot grey random 00, 01, 10, 11 0001, 0010, 0100, 1000 00, 01, 11, 10 01, 00, 11,10
While enumerated encoding is the most exible and readable, there are cases where we want to create output signals that have no possibility of output glitches. Asynchronous FIFOs and DRAMs in particular. As an example of a glitching state machine, lets build a two bit counter that has an output which is asserted in states 01 or 10 and is deasserted for states 00 and 11. We will allow binary encoding of the counter.
114
115
In this example, the default value for the output decode_out is logic zero. The synthesis tool sees that decode_out is to be logic zero unless it is redened to be logic one. In state cnt1, no value is assigned to decode_out.
WHEN cnt1 => --output signal takes default value IF (enable = 1) THEN byte_cnt_ns <= cnt2; ELSE byte_cnt_ns <= cnt1; END IF ;
Thus if the present state is cnt1, decode_out remains zero. However, if the present state is cnt2, the value of decode_out is redened to be logic one.
WHEN cnt2 => decode_out <= 1; --output signal assigned IF (enable = 1) THEN byte_cnt_ns <= cnt3; ELSE byte_cnt_ns <= cnt2; END IF ; WHEN cnt3 =>
We could have omitted the default assignment before the CASE statement and specied the value of decode_out in each state. But for state machines with many outputs, this becomes cumbersome and more difcult to see what is going on.
116
117
Typically, (i.e., may change with tool and/or vendor) with binary encoding, the state assignments will occur following a binary counting sequence in the order in which the states are named. i.e., cnt1 = 00, cnt2 = 01, etc. Surely enough when the synthesis is done, we see the transcript say:
-- Loading entity sm1 into library work "/nfs/guille/u1/t/traylor/ece574/src/sm1.vhd",line 23: Info, Enumerated type byte_cnt_type with 4 elements encoded as binary. Encodings for byte_cnt_type values value byte_cnt_type[1-0] =================================== cnt1 00 cnt2 01 cnt3 10 cnt4 11
Note the output decoding created by the XOR and NOT gate. If there are unequal delays from the ip op outputs to the XOR gate, glitches will result at the XOR output. In a delayless simulation this would not be seen. In a fabricated chip however, the glitches are almost a certainty.
118
Glitches
If we simulate this circuit without delays, we see the following. Note that the signal decode_out has no glitches.
When we are rst creating a circuit, we usually simulate without delays. However, when we synthesize, we can specify that a sdf le be created. The sdf (standard delay format) le contains the delay information for the synthesized circuit. Once the synthesized circuit is created, we can invoke the vsim simulator with the sdf switch used to apply the delays to the circuit. More about this process in the latter part of the class. So, when we invoke vsim like this:
vsim -sdfmax ./sdfout/sm1.sdf sm1
delays are added to the circuit. To make the glitch clearer, I added an additional 1ns delay to one of the ip ops by editing the sdf le. The simulation output now looks like this:
119
So using enumerated types when coding state machines is a clear and exible coding practice. However,.........the synthesizer may eat your lunch in certain situations! As folks often say, It depends.. If you state machine outputs go to another state machine as inputs, the glitches wont make a bit of difference. The glitches will come only after the clock edge and will be ignored by the ip op. But, if the outputs go to edge sensitive devices, BEWARE. So, lets see how we can make outputs that are always clean, without glitches for those special cases. grey coding, choosing states wisely, following ip ops, explicit states
120
121
output decode
D Q
output signals
R clock reset
R reset
The cleanup ip op effectively removes the glitches created by the output decoder logic by re-registering the glitchy output signal. This is an effective solution but it delays the output signal by one clock cycle. It would be better to place the ip op so that somehow the next state logic could create the output signal one cycle early. This is analogous to placing the output decode inside the next state decoder and adding one state bit. The nal solution alluded to above is to add one state bit that does not represent the present state but is representative only of the output signal. For the counter example we would encode the state bits like this: 000, 101, 110, 011. Thus the present state is actually broken into two parts, one representing the state (two LSBs) and the other part representing the output signal (the MSB). This will guarantee that the output signal must come from a ip op. An another advantage is that the output signal becomes available in the same cycle as the state becomes active and with no decoder delay Lets see how we can code this style of state machine so that the synthesis tool gives us what we want
122
0 1 1 0
00 01 10 11
The present state vector we will declare as STD_LOGIC_VECTOR actually consists of a one bit vector that represents the value that decode_out should have in the state we are in, plus two bits representing the present count state Now, to create the present and next state vectors and assign their values as we have just stated, we do the following in the declaration area of our architecture.
--declare the vectors SIGNAL byte_cnt_ns : SIGNAL byte_cnt_ps : STD_LOGIC_VECTOR(2 DOWNTO 0); STD_LOGIC_VECTOR(2 DOWNTO 0); DOWNTO DOWNTO DOWNTO DOWNTO 0) 0) 0) 0) := := := := "000"; "101"; "110"; "011";
--state encodings CONSTANT cnt1 : STD_LOGIC_VECTOR(4 CONSTANT cnt2 : STD_LOGIC_VECTOR(4 CONSTANT cnt3 : STD_LOGIC_VECTOR(4 CONSTANT cnt4 : STD_LOGIC_VECTOR(4
The use of CONSTANT here allows us to use the names instead of bit vectors like our enumerated example and not be guessing what state 110 is. This becomes far more important in more complex state machines.
123
This piece of code can be placed in a concurrent area preferably adjacent to the process containing this state machine. Also, since we are not covering every possibility in our CASE statement with our four CONSTANT denitions, we must take care of this. To do so we utilize the OTHERS statement as follows:
WHEN OTHERS => byte_cnt_ns <= (OTHERS => -); END CASE; (OTHERS => -)
This code segment implies when no other case matches, the next state vector may be assigned to any value. As we do not expect (outside of catastrophic circuit failure) any other state to be entered, this is of no concern. By allowing assignment of the next state vector to any value the synthesis tool can use the assigned dont cares to minimize the logic. The mysterious portion of the above: (OTHERS
=> -)
Really just says that for every how many bits are in the vector (all the others) assign a dont care value. Its a handy trick that allows you to modify your code later and add or subtract bits in a vector but never have to change the OTHERS case in your CASE statement. Lets look at the new and improved state machine code and the synthesized output.
124
BEGIN byte_cntr: PROCESS (clk_50, reset_n, enable, byte_cnt_ps, byte_cnt_ns) BEGIN --clocked part IF (reset_n = 0) THEN byte_cnt_ps <= cnt1; ELSIF (clk_50EVENT AND clk_50 = 1) THEN byte_cnt_ps <= byte_cnt_ns; END IF; --combinatorial part CASE byte_cnt_ps IS WHEN cnt1 => IF (enable = 1) THEN byte_cnt_ns <= cnt2; ELSE byte_cnt_ns <= cnt1; END IF ; WHEN cnt2 => IF (enable = 1) THEN byte_cnt_ns <= cnt3; ELSE byte_cnt_ns <= cnt2; END IF ; WHEN cnt3 => IF (enable = 1) THEN byte_cnt_ns <= cnt4; ELSE byte_cnt_ns <= cnt3; END IF ; WHEN cnt4 => IF (enable = 1) THEN byte_cnt_ns <= cnt1; ELSE byte_cnt_ns <= cnt4; END IF ; WHEN OTHERS => byte_cnt_ns <= (OTHERS => -); END CASE; END PROCESS byte_cntr; decode_out <= byte_cnt_ps(2); --output signal assignment
125
Whoa, you say. Thats not what I expected. Here is a case where the synthesis tool did what you meant but not what you said. We sure enough got an output from a ip op that is glitchless but the circuit still only has two ip ops. What the synthesis tool did what to rearrange the state encoding such that the bit that decode_out is tied to is one in states cnt2 and cnt3. In other words, it Grey coded the states to avoid the extra ip op. Other tools may or may not behave in the same way. Once again, it pays to checkout the transcript, take a look at the gates used and take a peek at the schematic. The synthesis transcript did mention what was done in a vague sort of way:
-- Compiling root entity sm1(beh) "/nfs/guille/u1/t/traylor/ece574/src/sm1.vhd", line 27: Info, D-Flipflop reg_byte_cnt_ps(0) is unused, optimizing...
The moral of the story... read the transcripts. Dont trust any tool completely. Double check everything. The paranoid survive....... Andy Grove
Essential VHDL for ASICs 126
127