part 1 – PDC Tutorial

advertisement
VHDL in digital circuit
synthesis (tutorial)
dr inż. Miron Kłosowski
EA 309
klosowsk@ue.eti.pg.gda.pl
Library declaration
library IEEE;
use IEEE.std_logic_1164.all;
Obligatory IEEE
library
all - use all elements of the
package
std_logic_1164 package usage
Other IEEE packages:
IEEE.std_logic_signed.all
For example std_logic_vector adder
IEEE.std_logic_unsigned.all
IEEE.std_logic_arith.all For example: type conversion: integer to std_logic_vector
std.text_io.all
For text file support (for simulation).
IEEE.numeric_std.all;
For example: function std_match for vector compare.
Package included by default: std.standard.all contains
definitions of basic types like: boolean, bit, character,
string, integer, real, natural, positive,
bit_vector.
Sample design
library IEEE;
use IEEE.std_logic_1164.all;
entity multiplexer is
port (
signal s : in std_logic;
signal x0,x1 : in std_logic_vector(7 downto 0);
signal y : out std_logic_vector(7 downto 0)
);
end entity multiplexer;
architecture data_flow of multiplexer is
begin
y <= x1 when ( s = '1' ) else x0;
end architecture data_flow;
Design simulation – testbench (1)
Sample TESTBENCH (template is usually generated automatically):
LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_unsigned.all;
USE ieee.numeric_std.ALL;
Testbench generates and tests all
signals therefore port is not used.
ENTITY fpgalab1_tb_vhd IS
END fpgalab1_tb_vhd;
ARCHITECTURE behavior OF fpgalab1_tb_vhd IS
-- Component Declaration for the Unit Under Test
COMPONENT projekt1
PORT(
clk : IN std_logic;
reset : IN std_logic;
sw0 : IN std_logic;
sw1 : IN std_logic;
sw2 : IN std_logic;
sw3 : IN std_logic;
an : OUT std_logic_vector(3 downto 0);
seg : OUT std_logic_vector(7 downto 0);
ld0 : OUT std_logic;
ld1 : OUT std_logic
);
END COMPONENT;
(UUT)
Simulated Circuit (UUT) is connected
using component declaration
mechanism (described in details
later). Here declaration of names,
types and directions of tested signals
is present (signals which would be
visible at the pins of synthesized
physical FPGA circuit).
Design simulation – testbench (2)
SIGNAL
SIGNAL
SIGNAL
SIGNAL
SIGNAL
SIGNAL
SIGNAL
SIGNAL
sw0
sw1
tst
trx
an
seg
ld0
ld1
:
:
:
:
:
:
:
:
std_logic := '0';
std_logic := '0';
std_logic := '1';
std_logic := '1';
std_logic_vector(3 downto 0);
std_logic_vector(7 downto 0);
std_logic;
std_logic;
signal clk : std_logic := '0';
signal clk_p : std_logic := '0';
signal clk_n : std_logic := '1';
constant PERIOD : time := 10 ns;
constant DUTY_CYCLE : real := 0.25;
signal reset : std_logic := '1';
BEGIN
-- Instantiate the Unit Under Test (UUT)
uut: projekt1 PORT MAP(
clk => clk,
reset => reset,
sw0 => sw0,
sw1 => sw1,
sw2 => tst,
sw3 => trx,
an => an,
seg => seg,
ld0 => ld0,
ld1 => ld1
);
Definitions of signals used for
component testing – signals are
connected to the inputs of UUT
and should be initialized.
Definitions of additional signals and
constants needed for testing.
Initialization of those signals is
usually recommended.
Real connection to UUT component
is created here, PORT MAP
describe connections between
component signals and testbench
signals.
Design simulation – testbench (3)
-- clk_simple <= not clk_simple after PERIOD/2;
clk_p <= not clk_p after PERIOD/2;
clk_n <= not clk_n after PERIOD/2;
Simple clock generator.
Differential clock generator.
clk <= '1' after (PERIOD - (PERIOD * DUTY_CYCLE)) when clk = '0'
else '0' after (PERIOD * DUTY_CYCLE);
Arbitrary duty cycle clock
generator.
Reset signal
reset <= '0' after 5 ns;
deassertion.
tb : PROCESS
BEGIN
-- Wait 10 ns for global reset to finish
wait for 10 ns;
-- Place stimulus here
sw0 <= '1';
sw1 <= '0';
Delay.
wait for 20 ns;
sw0 <= '0';
wait for 10 ns;
sw1 <= '1';
wait; -- will wait forever
END PROCESS;
Process – instructions inside
the process are executed
sequentailly.
Signal values are
sequentially modified.
wait instruction without
arguments waits forever (without
it the process would start again).
Design simulation – testbench (4)
tb1 : PROCESS
Infinite loop instruction
variable x : integer;
BEGIN
wait for 10 ns;
loop
x := 1;
while x < 11 loop
Conditional loop instruction
tst <= not tst;
wait for x*2 ns;
Assert instruction – generates warning
x := x + 1;
and error messages and stops a
assert x /= 11
simulation
report „Last iteration finished"
severity NOTE;
end loop;
wait until rising_edge(clk);
‘wait until’ instruction waits for a logic
end loop;
condition to be satisfied (in this
‘wait
on’
instruction
END PROCESS;
example it waits for the rising edge of
waits for a signal
the clk signal)
change
tb2 : PROCESS
BEGIN
wait on clk_p;
This assign instruction
trx <= transport clk_p after (PERIOD/2)+3 ns;
registers future change of the
END PROCESS;
signal trx. This is an example
of a transport delay.
END;
Design simulation – testbench (5)
Signal assignment (concurrent)
signal_1 <= signal_2;
signal_2 <= signal_3;
signal_2 <= signal_3;
signal_1 <= signal_2;
Sequence of assignments is irrelevant
(architecture body is an concurrent
instruction area).
Is this a problem  ?
Signal_1 <= Signal_2;
Signal_1 <= Signal_3;
It can be solved using the
resolution function  !
Signal_2
Signal_1
Signal_3
Important !!!
Resolution function is defined for type
std_logic and std_logic_vector.
Type std_ulogic is a logic type
without resolution function. Using
std_logic and std_logic_vector
types is recommended.
Resolution function
Resolution function for std_logic:
U X 0 1 Z W L H --------------------------------U U U U U U U U U | U |
U X X X X X X X X | X |
U X 0 X 0 0 0 0 X | 0 |
U X X 1 1 1 1 1 X | 1 |
U X 0 1 Z W L H X | Z |
U X 0 1 W W W W X | W |
U X 0 1 L W L W X | L |
U X 0 1 H W W H X | H |
U X X X X X X X X | - |
• Std_logic is a most frequently used logic type. It can be assigned 0 i 1
values and more:
Z high impedance state (for top level signals only);
- don't care (for example used by function std_match);
Other values are useful in simulation:
U uninitialized (at the beginning of simulation);
X undefined strong signal; W undefined weak signal;
L, H week 0 and 1 signals.
Vectors (1)
• Sample vector (one-dimensional array):
signal x: std_logic_vector(7 downto 0);
x <= "11001010";
x(7) <= '1'; x(6) <= '1'; x(5) <= '0'; x(4) <= '0';
x(3) <= '1'; x(2) <= '0'; x(1) <= '1'; x(0) <= '0';
• Vector with rising index:
signal y: std_logic_vector(0 to 7);
y <= "11001010";
y <= x;
Previous assignment automatically perform 8 one-bit assignments:
y(0) <= x(7); y(1) <= x(6); y(2) <= x(5); y(3) <= x(4);
y(4) <= x(3); y(5) <= x(2); y(6) <= x(1); y(7) <= x(0);
• Vector assignment is possible when the number and type of vector’s
elements is the same in RHS and LHS.
Vectors (2)
• Because many library functions use vectors with MSB on the left side it is
recommended to use downto indexing (index 0 is a LSB)
signal q: std_logic_vector(1 downto 0);
• You can address smaller subvectors using parenthesis:
x(7 downto 6) <= q;
x(5 downto 3) <= y(2 to 4);
• You cannot change index direction of the vector, assignment:
x(5 downto 3) <= y(4 downto 2); is not correct!!!
• You can create vectors from logic signals or smaller vectors using the
concatenation operator &:
signal s: std_logic;
x <= "10" & q & '1' & q & '0';
vec_from_sig <= s & s & s & s & s & s & s & s;
Vectors (3)
• You can create vectors using aggregates:
x <= ('1', '0', q(1), q(0), '1', q(1), q(0), others => '0');
x <= (6=>'0', 7=>'1', 4=>q(0), 5=>q(1), 0=>'0', 2=>q(1),
1=>q(0), others => '1');
Word ‘others’ can be
zero_vector <= ( others => '0');
used only at the end
vec_from_sig <= ( others => s );
of the aggregate.
vec_from_sig1 <= ( s1, s2, s3, s4, others => s );
vec_from_sig2 <= ( 0=>'1', 6 downto 2 => s2, 7|1 => s1 );
Logical operations on vectors:
signal temp: std_logic_vector(7 downto 0);
temp <= ( others => s );
y <= ( temp and x1 ) or ( not temp and x0 );
• You can perform following logic operations: not, and, or, nand, nor, xor, xnor.
• Logic operation is performed on bits taken from the same position of operand
vectors. Result is placed in the same position in the LHS vector.
• Vectors used for the operation must have the same length.
Sample design (2)
library IEEE;
use IEEE.std_logic_1164.all;
entity multiplexer is
port (
signal s : in std_logic;
signal x0,x1 : in std_logic_vector(7 downto 0);
signal y : out std_logic_vector(7 downto 0)
);
end entity multiplexer;
architecture logic of multiplexer is
signal temp : std_logic_vector(7 downto 0);
begin
temp <= ( others => s );
y <= ( temp and x1 ) or ( not temp and x0 );
end architecture logic;
Arithmetic operations on vectors
• You can perform arithmetic operations not only on integer types.
Std_logic_vector type can be also used for this task but only when one the
following packages are used:
use IEEE.std_logic_unsigned.all;
use IEEE.std_logic_signed.all;
• If in the same design entity signed and unsigned vectors are present you
can use the signed and unsigned vector types defined in package
IEEE.std_logic_arith
• You can also used the IEEE.std_logic_signed.all package for all
vectors but prefix the unsigned vectors with ‘0’ & to make them non
negative.
• Carry in and out synthesis (in addition and subtraction output vector length
is equal to the longest input vector):
signal res, x, y: std_logic_vector(7 downto 0);
signal a: std_logic_vector(8 downto 0);
signal cin, cout : std_logic;
a <= ('0' & x) + ('0' & y) + cin;
res <= a(7 downto 0);
cout <= a(8);
Type conversion (examples)
Frequently used type conversion functions:
• Packages IEEE.std_logic_unsigned and IEEE.std_logic_signed contain
conversion function from type Std_logic_vector to type Integer:
CONV_INTEGER(ARG: STD_LOGIC_VECTOR)
• Package IEEE.std_logic_arith contains following conversion functions:
- Integer to Std_logic_vector (you have to specify the size of the vector):
CONV_STD_LOGIC_VECTOR(ARG: INTEGER; SIZE: INTEGER)
- Integer to Unsigned/Signed:
CONV_UNSIGNED(ARG: INTEGER; SIZE: INTEGER)
CONV_SIGNED(ARG: INTEGER; SIZE: INTEGER)
- Unsigned/Signed to Integer:
CONV_INTEGER(ARG: UNSIGNED)
CONV_INTEGER(ARG: SIGNED)
- Std_logic_vector extension to length size:
EXT(ARG: STD_LOGIC_VECTOR; SIZE: INTEGER)
- Std_logic_vector signed extension to length size:
SXT(ARG: STD_LOGIC_VECTOR; SIZE: INTEGER)
• Package IEEE.std_logic_1164 contains conversion functions for Bit_Vector:
TO_BITVECTOR(S: STD_LOGIC_VECTOR)
TO_STDLOGICVECTOR(B: BIT_VECTOR)
Processes
Processes are used for behavioral description of the hardware.
Instructions inside the process are executed sequentially to calculate
the values of signals assigned in the process.
architecture behavioral of multiplexer is
begin
Sensitivity list
Optional
label
mux: process (x0, x1, s) is
begin
if s = '1' then
y <= x1;
else
y <= x0;
end if;
end process mux;
end architecture behavioral;
Combinatorial processes (1)
• In processes describing combinatorial logical circuits the body of the
process (the algorithm) should be started only at the change of the signals
present on the sensitivity list.
• If the process has to describe the behavior of combinatorial circuit (i.e.
Logic circuit without the latches or flip-flops) two conditions must be satisfied:
1. All the input signals of the logic circuit must be present on the
sensitivity list of the process.
2. All the output signals of the logic circuit must be assigned a value at
least once in every possible flow of the algorithm used in the body of the
process.
Good process example:
Bad process example:
mux: process (x0, x1, s) is
mux: process (s) is
Begin
begin
y <= x0;
if s = '1' then
if s = '1' then
y <= x1;
y <= x1;
end if;
end if;
end process mux;
end process mux;
Combinatorial processes (2)
• When there is a possibility that output signal is not assigned during the
process run as the result of the synthesis a latch will be inferred – the
process is no longer combinatorial.
• You can use for example ‘else’ construct to avoid this.
• Another method is to use default value for all output signals (see below):
mux: process (x0, x1, s) is
begin
y <= x0;
if s = '1' then
y <= x1;
end if;
end process mux;
Signal assignment in the process is not
performed at the same time when the <=
instruction is found. This assignment is rather
programmed to be performed later – when the
process finishes the algorithm and stops
waiting for signals change. Therefore if more
than one assignment to the signal is found
during the process run only the last
assignment is valid. All the programmed
assignments are finalized at the same time
when the process stops. Read value of the
signal cannot be changed during the process
run – when the signal is read it always returns
the same value (even when the new value
has been asserted).
Combinatorial processes (3)
• It is possible that output signal of the process is used at the same time as
input signal of the same process. It is of course not allowed to create the
combinatorial loop in any way – but you can create the chain logic.
• In this situation remember that output signals which are used as an input
also should satisfy both conditions from the previous page (sensitivity list and
obligatory assignment):
• Sometimes process is started
process(a, b, c, x, y) is
several times because the input signal
begin
has been changed during process
x <= a and b;
execution (delta cycles). In this
y <= x or c;
situation last assignment to the output
z <= y xor a;
signal along all the delta cycles is
valid.
end process;
Delta cycles for process execution:
t=0 ns
(a=0, b=1, c=0, x=0, y=0, z=0)
t=1 ns
(a=1, b=1, c=0, x=0, y=0, z=0)
t=1 ns + 1d (a=1, b=1, c=0, x=1, y=0, z=1)
t=1 ns + 2d (a=1, b=1, c=0, x=1, y=1, z=1)
t=1 ns + 3d (a=1, b=1, c=0, x=1, y=1, z=0)
• Interesting property: different signals
can be assigned in any sequence in
the process.
• Remember that you cannot assign
the same signal in different processes
without activating the resolution
function (which is usually prohibited
during synthesis).
Variables (1)
• Signals are used to communicate between the process and the rest of the
system (concurrent). But to create algorithms inside the process you will
need variables.
• Variables are defined at the beginning of the process (just before ‘begin’)
and they are visible within the process only.
• Variables preserve its value between process runs (they can model
the memory).
• Variables change at the same time when the variable assignment is
found (they behave like you would expect in a software programming
language).
process(a, b, c) is
variable x,y:std_logic_vector(7 downto 0);
begin
x := a and b;
y := x or c;
z <= y xor a;
end process;
Delta cycles for process execution:
t=0 ns
(a=0, b=1, c=0, x=0, y=0, z=0)
t=1 ns
(a=1, b=1, c=0, x=0, y=0, z=0)
t=1 ns + 1d (a=1, b=1, c=0, x=1, y=1, z=0)
Contrary to signals, different
variables must be assigned in the
proper sequence – like in a software
programming language.
When you exchange the first line
with the second the result will be
different:
(x=1, y=0, z=1)
Variables (2)
• You can assign the initial value to the variable:
variable x: std_logic_vector(7 downto 0) := "00100010";
variable i: integer range 0 to 7 := 4;
• The initial value is assigned to the variable only at the beginning of
the synthesis or simulation. It means that if you want to assign the initial
value every time the process is started you have to do it in the process body
just after the ‘begin’ word.
• Be careful – when you use the variable’s value before you assign the initial
value to it you read the value from the previous process execution. This
creates a memory element (usually latch).
Sequential processes (1)
• Processes can be used to describe sequential circuits if they use signals or
variables as a memory.
• signal memory – if signal assertion is not continuous but depends on input
signals’ states (for example if you forgot about default value assertion like in
the example below).
• variable memory – if variable is not initialized at the beginning of the
process and contents of the variable from previous process run is used.
• Sequential processes use memory elements available in the target FPGA
(FF, latches, etc.) – you cannot build them using gates. It is important to use
strict design rules to achieve synthesizable sequential processes.
Example of sequential process (q is not asserted continuously):
latch: process (ena, d) is
begin
D-latch. When the ena signal is active
if ena = '1' then
q output is connected to the d input,
q <= d;
when ena is inactivated q output is
end if;
frozen.
end process latch;
Sequential processes (2)
• Latches are not used frequently, but they are available in most FPGAs.
• Usually latch synthesis is caused by an error (for example: not asserting
default value to the output signal in combinatorial process). Synthesizer
software usually warns about latches: „Warning: latch inferred ... ”
• Classic method to build sequential circuits is to use edge-triggered flipflops.
• Recall all the properties of the synchronous digital circuits, we will use this
design paradigm in the VHDL and FPGAs.
Edge triggered D-type flip-flop:
dff: process (clk) is
begin
if clk = '1' and clk'event then
q <= d;
end if;
end process dff;
'0' for falling edge
D-type flip-flop (rising edge
triggered). If clk logic value
changes, value of the attribute
‘event of the clk signal is set to the
‘1’ for the moment. If clk changes
to ‘1’ (i.e. rising edge occurred) the
signal value from the d input is
transferred to the q output, and q is
frozen till the next clk edge.
Sequential processes (3)
’event attribute detects every change of the signal, it is suggested to use
rising_edge() or falling_edge() functions which detect only changes from ‘0’
to ‘1’ or ‘1’ to ‘0’ respectively. Those functions do not detect changes between
other std_logic type’s values like ‘U’,’X’,’Z’, etc.
dff: process (clk) is
begin
if rising_edge(clk) then
q <= d;
end if;
end process dff;
dff: process (clk) is
begin
if falling_edge(clk) then
q <= d;
end if;
end process dff;
Synchronous circuit clocked with rising /
falling edge of the clock:
circuit_name: process (clock) is
begin
if rising/falling_edge(clock) then
.. –- Sequential description of
.. –- the digital synchronous circuit,
.. –- all signals assigned here are
.. -- synthesized as D-type flip-flops’
.. -- outputs.
.. –- All logic expressions, conditionals,
.. -- etc. are synthesized as combinatorial
.. -- logic driving D inputs of flip-flops.
end if;
end process circuit_name;
Sequential processes (4)
• Usually flip-flops in FPGAs are designed with asynchronous reset and set
inputs.
Synchronous circuit clocked with rising /
falling edge of the clock
drff: process (clk, rst) is
begin
with asynchronous reset input:
if rst = '1' then
q <= '0';
elsif rising_edge(clk) then
q <= d;
end if;
end process drff;
dsff: process (clk, set) is
begin
if set = '1' then
q <= '1';
if rising_edge(clk) then
q <= d;
end if;
end process dsff;
circuit_name: process (clock, reset) is
begin
if reset = '1' then
-- or '0'
sig1 <= "00110";
sig2 <= const;
.. –- Values asserted to flip-flops
.. –- when reset is active.
.. –- Must be constant because
.. –- asynchronous load of the signal
.. –- is not usually supported.
elsif rising/falling_edge(clock) then
.. -- Sequential description of
.. –- the digital synchronous circuit:
sig1 <= ...
sig2 <= ...
end if;
end process circuit_name;
Sequential processes (5)
• Global reset of entire system should be performed with disabled clock.
When clock is running some flip-flops can miss a clock when the reset signal
deassertion is delayed because of signal propagation.
• Using the hardware reset input of the FPGA (PROG input in Xilinx) is
recommended (use initial values of the signals).
• Never drive asynchronous set/reset from combinatorial logic – it can contain
hazards and therefore spikes are possible – use direct output of the flip-flops
or use synchronous reset.
• Avoid suggesting clock gating in process description (it is usually not
supported), rather use synchronous clock enable:
-- clock gating process !!!
dceff: process (clk, rst) is
begin
if rst = '1' then
q <= '0';
elsif clk_ena = '1' then
if rising_edge(clk) then
q <= d;
end if;
end if;
end process dceff;
Synchronous reset and clock enable:
circuit_name: process (clock) is
begin
if rising/falling_edge(clock) then
if sync_reset=‘1’ then
q <= ‘0’;
elsif clock_enable=‘1’ then
q <= .....
end if;
end if;
end process circuit_name;
Initial values of signals
• You can assign initial value to the signal:
signal x: std_logic_vector(7 downto 0) := "00100010";
signal i: integer range 0 to 7 := 4;
• When the signal with initial value is used as the output of the sequential
process (i.e. it represents the flip-flop) the initial value will be assigned to
the flip-flop during the configuration process of the FPGA.
• Most FPGAs have got the flip-flop initialization during configuration
functionality.
• When the initial value is not defined the flip-flop will be set to ‘0’ (Xilinx FPGAs’
property).
Conditional instruction (1)
In processes conditional instruction execution is performed using following
reserved words: if, then, elsif, else, end if. See example below:
-- signal s:std_logic_vector(2 downto 0);
-- signal y:std_logic_vector(1 downto 0);
priority_encoder: process (s) is
begin
if s(2) = '1' then
y <= "11";
elsif s(1) = '1' then
y <= "10";
elsif s(0) = '1' then
y <= "01";
else
y <= "00";
end if;
end process priority_encoder;
Case instruction (1)
Expression after the ‘case’ word is calculated and then instructions assigned to
the calculated value are executed. All possible expression values must be
enumerated. If it is impossible or difficult use word ‘others’ (especially needed for
std_logic_vector type).
-- signal s:std_logic_vector(2 downto 0);
-- signal y:std_logic_vector(1 downto 0);
priority_encoder: process (s) is
begin
case s is
when "111" | "110" | "101" | "100" => y <= "11";
when "011" | "010" => y <= "10";
when "001" => y <= "01";
when others => y <= "00";
end case;
end process priority_encoder;
Case instruction (2)
For scalar and enumeration types it is allowed to use ranges (to, downto). It is
allowed to use many instructions in one choice but at least one instruction must
be used (it may be null instruction). After ‘when’ only constants or constant
expressions can be used.
-- signal s:std_logic_vector(2 downto 0);
-- signal y:std_logic_vector(1 downto 0);
-- constant jeden:integer := 1;
priority_encoder: process (s) is
begin
y <= "10";
case CONV_INTEGER(s) is
when 7 downto 4 => y <= "11";
if s = 5 then
z <= 123;
end if;
when 3 downto jeden + 1 => null;
when jeden => y <= "01";
when others => y <= "00";
end case;
end process priority_encoder;
Null
instruction
Loop instruction (1)
• Loop instruction is used for easy synthesis of many similar elements.
• Synthesizable loop must have limited number of iterations.
• While loop is executed when condition after ‘while’ word is true.
• While and For loops can be broken using ‘exit’ instruction.
priority_encoder: process (s) is
variable i:integer;
begin
i := 2;
y <= "00";
L1:
while i >= 0 loop
if s(i) = '1' then
y <= CONV_STD_LOGIC_VECTOR(i+1, 2);
exit L1;
end if;
i := i - 1;
end loop L1;
end process priority_encoder;
Loop instruction (2)
• It is not obligatory to define the iteration variable in the For loop.
• Synthesizable loop must have limited number of iterations.
• You can use 'range attribute to define the loop iteration range.
bit_counter: process (s) is
variable num_bits:integer range 0 to s'length;
begin
num_bits := 0;
L1:
for i in s'range loop
if s(i) = '1' then
num_bits := num_bits + 1;
end if;
end loop L1;
q <= num_bits;
end process bit_counter;
Loop instruction (3)
• Use the ‘next’ instruction to brake single iteration without exiting the loop – just
next iteration is started.
• Synthesizable loop must have limited number of iterations.
-- signal s:std_logic;
-- signal crc_in, crc_out:std_logic_vector(31 downto 0);
crc32_logic: process (s, crc_in) is
constant coef:std_logic_vector(31 downto 0) :=
"0000_0100_1100_0001_0001_1101_1011_0111";
variable tmp:std_logic;
begin
c(x) = 1 + x + x2 + x4 + x5 + x7 + x8 + x10 + x11 + x12
for i in 0 to 31 loop
+ x16 + x22 + x23 + x26 + x32
if i = 0 then
tmp := crc_in(31);
else
tmp := crc_in(i-1);
end if;
crc_out(i) <= tmp;
if coef(i) = '0' then
next;
end if;
crc_out(i) <= tmp xor s;
end loop;
end process crc32_logic;
Shift registers
• Using sll, srl, sla, sra, rol, ror operators for shift registers synthesis is not
recommended (those operators work for bit_vector type only).
• Below a recommended method of shift register synthesis is presented (16-bit right
and left shift registers with serial input/output and parallel load). Rotating registers
can be synthesized in the same way.
shift_regs: process(clk, rst)
begin
if rst = '1' then
reg_l <= (others => '0');
reg_r <= (others => '0');
elsif rising_edge(clk) then
if load = '1' then
reg_l <= data_l;
reg_r <= data_r;
else
serial_data_out_l <= reg_l(15);
reg_l(15 downto 1) <= reg_l(14 downto 0);
reg_l(0) <= serial_data_in_l;
serial_data_out_r <= reg_r(0);
reg_r(14 downto 0) <= reg_r(15 downto 1);
reg_r(15) <= serial_data_in_r;
end if;
end if;
end process;
Clocks in FPGA
Clocks in FPGAs have to be designed carefully:
1.
2.
3.
4.
5.
6.
Use one clock in the design if possible.
Use external clock or generate it using single divider.
When using divider remember to generate clock directly from FF
output not from combinatorial logic!
If you need more clock domains use Digital Clock Managers to
generate needed clocks and keep them synchronous.
Remember that clock resources are limited.
If you need clock domains from independent clock sources
remember to carefully design the data transfer between those clock
domains (use FF synchronizers, asynchronous FIFO, two-port
memories etc.).
State machines (1)
Connection
for Mealy
state
machine
Input
Output
Output
function
Transition
function
State
registers
CLK
RESET
• Moore and Mealy state machines can be easily implemented in VHDL.
• Blue blocks are implemented using sequential synchronous processes.
• Orange blocks are implemented using combinatorial processes.
• Two orange blocks can be implemented in one combinatorial process.
State machines (2)
• To synthesize state variable enumerated type is usually used:
type StateType is (idle, operate, finish);
signal present_state, next_state : StateType;
• It is possible to change the default state representation:
- globally using synthesizer options,
- locally using attributes of state variable (Xilinx example):
attribute fsm_encoding: string;
attribute fsm_encoding of present_state: signal is "compact";
-- "{auto|one-hot|compact|gray|sequential|johnson|user}"
• It is possible to use user defined state representation (for fsm_encoding =
user):
type statetype is (ST0, ST1, ST2, ST3);
attribute enum_encoding of statetype : type is "001 010 100 111";
signal state1 : statetype;
signal state2 : statetype;
State machines (3)
-- Moore state machine
-- example
type StateType is (idle, operate,
finish);
signal present_state : StateType;
signal next_state : StateType;
seq: process (reset, clk) is
begin
if (reset = '1') then
present_state <= idle;
elsif rising_edge(clk) then
present_state <= next_state;
end if;
end process seq;
comb: process (present_state, go, brk, cln,
rdy) is
begin
case present_state is
when idle =>
if (go = '1') then
next_state <= operate;
else
next_state <= idle;
end if;
power <= '0';
direction <= '0';
when operate =>
if (brk = '1') then
next_state <= idle;
elsif (cln = '1') then
next_state <= finish;
else
next_state <= operate;
end if;
power <= '1';
direction <= '0';
when finish =>
if (rdy = '1') then
next_state <= idle;
else
next_state <= finish;
end if;
power <= '1';
direction <= '1';
end case;
end process comb;
State machines (4)
-- Mealy state machine
-- example
type StateType is (idle, operate,
finish);
signal present_state : StateType;
signal next_state : StateType;
seq: process (reset, clk) is
begin
if (reset = '1') then
present_state <= idle;
elsif rising_edge(clk) then
present_state <= next_state;
end if;
end process seq;
comb: process (present_state, go, brk, cln,
rdy, emergency_off) is
begin
-- default value to avoid latches:
next_state <= present_state;
case present_state is
when idle =>
if (go = '1') then
next_state <= operate;
end if;
power <= '0';
direction <= '0';
when operate =>
if (brk = '1') then
next_state <= idle;
elsif (cln = '1') then
next_state <= finish;
end if;
power <= not (emergency_off or brk);
direction <= '0';
when finish =>
if (rdy = '1') then
next_state <= idle;
end if;
power <= not emergency_off;
direction <= '1';
end case;
end process comb;
State machines (5)
input
Output
function
Output
registers
Transition
function
State
registers
CLK
output
RESET
synchronous output Mealy state machine
• Blue blocks are implemented using sequential synchronous processes.
• Orange blocks are implemented using combinatorial processes.
• It is possible to synthesize synchronous output Mealy machine in one sequential process
because machine’s output is taken directly from the FF outputs.
State machines (6)
-- synchronous output Mealy
-- state machine example
-- (coded in single process)
type StateType is (idle, operate,
finish);
signal fsm_state : StateType;
sync_mealy: process (reset, clk)
is
begin
if reset = '1' then
fsm_state <= idle;
power <= '0';
direction <= '0';
elsif rising_edge(clk) then
case fsm_state is
when idle =>
if (go = '1') then
fsm_state <= operate;
power <= '1';
end if;
when operate =>
if (brk = '1') then
fsm_state <= idle;
power <= '0';
elsif (cln = '1') then
fsm_state <= finish;
direction <= '1';
end if;
when finish =>
if (rdy = '1') then
fsm_state <= idle;
power <= '0';
direction <= '0';
end if;
end case;
end process sync_mealy;
State machines (7)
Forbidden states
• Usually state machines are not synthesized to automatically get out of
forbidden states. For mission-critical systems it is possible to enable
special option in synthesizer to enable safe machine synthesis (but
usually that means much larger transition logic and slower clock).
• Other methods for safe state machine synthesis:
1. Use direct state encoding (use user defined states or use
std_logic_vector for state definition) and specify behavior of the
machine for all possible (valid and invalid) states.
2. Add logic for detection of invalid states which triggers reset.
State machines (8)
-- Example of direct state encoding:
signal present_state, next_state : std_logic_vector(2 downto 0);
constant idle
: std_logic_vector(2 downto 0) := "000";
constant prepare : std_logic_vector(2 downto 0) := "001";
constant operate : std_logic_vector(2 downto 0) := "010";
constant finish : std_logic_vector(2 downto 0) := "100";
constant failure : std_logic_vector(2 downto 0) := "111";
-- specify behavior for forbidden states:
when others => next_state <= idle;
-- you can easily revert to optimal (not safe) version of machine:
when others => next_state <= "---";
State machines (9)
-- Detection of forbidden states
-- for state machines with one-hot state encoding:
idl <= state = idle;
prepar <= state = prepare;
operat <= state = operate;
finis <= state = finish;
failur <= state = failure;
inv <= (idl and (prepar or operat or finis or failur)) or
(prepar and (operat or finis or failur)) or
(operat and (finis or failur)) or
(finis or failur) or
(not (idl or prepar or operat or finis or failur));
....
if (inv = '1') then
state <= idle;
else
....
....
State machines (10)
How to avoid entering forbidden or wrong state ?
Most common causes of state machine failures:
• Clock frequency is too high (reduce critical path delay or clock frequency).
• Signals entering state machine are not synchronous to the machine’s
clock (signals generated from external devices like pushbuttons,
sensors, interrupt lines, busses are usually asynchronous).
Different propagation delays of signals inside the combination logic can
cause the asynchronous signal change not to achieve all the FF inputs
before active clock edge.
D
Q
D
Q
1-gate path: low delay
Asynchronous input
3-gate path: high delay
State machines (11)
How to avoid entering forbidden or wrong state ?
To avoid wrong FF operation you have to synchronize the input signal
with the local clock domain. To achieve that you have to send the
asynchronous signal through a pipe of two FFs. One FF is not
enough because the metastability event in FF can occur.
If you send multibit (vector) signals in this way use Gray coding
or use any other method for error checking.
D
Q
D
Q
Asynchronous input
D
Q
D
Q
Constants and aliases
• Constants can be defined inside entities, architectures, processes and packages:
constant x: std_logic_vector(7 downto 0) := "00100010";
constant y: std_logic_vector(7 downto 0) := ( others => '1');
constant i: integer := 4;
type state_type is (clear, initiate, run, stop);
constant state_stop: state_type := stop;
• Constants are useful for design configuration. You can define the size of the
busses, counters, etc.
• Alias names can be assigned to a part of the vector to make a partial vector
access easier:
signal iw: std_logic_vector(28 downto 0);
alias opcode: std_logic_vector(4 downto 0) is iw(28 downto 24);
alias src: std_logic_vector(7 downto 0) is iw(23 downto 16);
alias dst: std_logic_vector(7 downto 0) is iw(15 downto 8);
alias arg: std_logic_vector(3 downto 0) is iw(7 downto 4);
alias option: std_logic_vector(2 downto 0) is iw(3 downto 1);
alias reserved: std_logic_vector(3 downto 0) is iw(3 downto 0);
Attributes
• Attributes can be defined for entities, architectures, types and signals.
type cnt is integer range 0 to 127;
type state is (idle, start, stop, reset);
type word is array(15 downto 0) of std_logic;
• Sample attributes predefined for types and signals: 'left 'right 'high 'low 'length
cnt'left = 0; state'left = idle; word'left = 15;
cnt'right = 127; state'right = reset; word'right = 0;
cnt'high = 127; state'high = reset; word'high = 15;
cnt'low = 0; state'low = idle; word'low = 0;
cnt'length = 128; state'length = 4; word'length = 16;
• Sometimes the 'range attribute can be useful – it reveals the range and direction of
the vector indexing, for example: word'range = 15 downto 0;
• Another useful attribute 'event allows detection of a signal change. It can be used for
synchronous circuits synthesis.
• 'pos attribute returns the position the argument holds on the enumerated type list, for
example: state'pos(start) = 1; character'pos('A') = 65;
• 'val attribute is for the reverse operation, for example:
state'val(2) = stop; character'val(66) = 'B';
• 'pos and 'val attributes can be used for character to ASCII code conversion and for
reverse operation.
3-state buffers implementation
3-state buffers can be implemented on the external interface signals of the FPGA
only. These buffers can be used for a bidirectional communication with external
ICs. For 3-state buffers use a combinatorial process and a metalogic value ‘Z’:
-- signal i,o,ena:std_logic;
process (i,ena) is
begin
if ena = '1' then
o <= i;
else
o <= 'Z';
end if;
end process;
You can also use a conditional assignment:
-- signal i,o:std_logic_vector(127 downto 0);
-- signal ena: std_logic;
o <= i when ena = '1' else 'Z';
Design hierarchy (1)
• Design entities can be used mutiple times in the same project.
• Entire design can be composed of components implemented using different
abstraction levels.
• Design entities – components – can be implemented in separate files,
packages and included in the project when needed.
• „entity” construct is an interface of the design unit.
• „component” construct is a usage declaration of the previously created entity.
• „port map” construct is an instantiation of the previously declared component.
Top level entity
component bufor port(
Entity A
d, enable: in std_logic;
q: out std_logic);
Entity B
end component bufor;
Entity D
.....
begin
.....
buf1: bufor port map(res,ena,outp(0));
buf2: bufor port map(ack,ena,outp(1));
Entity B
Entity C
Entity C
Design hierarchy (2)
• Example – 1-bit adder:
entity fulladder is port(
a,b,cin: in std_logic;
sum,cout: out std_logic);
end entity fulladder;
architecture dataflow of fulladder is
begin
sum <= a xor b xor cin;
cout <= (a and b) or (a and cin) or (b and cin);
end architecture dataflow;
• 1-bit adder used for synthesis of 4-bit adder:
entity adder_4 is
port(a,b: in std_logic_vector(3 downto 0);
cin: in std_logic;
sum: out std_logic_vector(3 downto 0);
cout: out std_logic);
end entity adder_4;
architecture structural of adder_4 is
component fulladder port(a,b,cin:in std_logic;
sum,cout:out std_logic);
end component fulladder;
signal c0,c1,c2: std_logic;
begin
fa0: fulladder port map(a(0),b(0),cin,sum(0),c0);
fa1: fulladder port map(a(1),b(1),c0,sum(1),c1);
fa2: fulladder port map(a(2),b(2),c1,sum(2),c2);
fa3: fulladder port map(a(3),b(3),c2,sum(3),cout);
end architecture structural;
Design hierarchy (3)
• Component port signals can be connected in any sequence when port names are
specified.
• „open” means that component’s output signal is not used.
• Input signals when left open must have a default value defined in the component
declaration.
fa2:
fa2:
fa2:
fa2:
fulladder
fulladder
fulladder
fulladder
port
port
port
port
map(a(2),b(2),c1,sum(2),c2);
map(b=>b(2), a=>a(2), sum=>sum(2), cout=>c2, cin=>c1);
map(a(2),b(2),c1,sum(2),open);
map(b=>b(2), a=>a(2), sum=>sum(2), cout=>open, cin=>c1);
• Types and vector ranges of component signals and local signals must match (like in
assignment instruction).
entity borg is port(a: in std_logic_vector(3 to 7));
end entity borg;
-----------------------------------------------------------component borg
port(a:in std_logic_vector(10 downto 6));
end component borg;
Design hierarchy (4)
• When „unconstrained” vector is used in the component declaration you can connect a
vector of the same base type but with any length and index range.
The architecture of the component must be aware of this and must use attributes to
synthesize the component automatically matched to the input and output vectors.
entity borg is port(a: in std_logic_vector);
end entity borg;
------------------------------------------------------------
component borg
port(a:in std_logic_vector(567 downto 123));
end component borg;
• Remember to properly set the direction specifiers when connecting components (in, out,
inout, buffer).
• You can pass parameters to the components using the „generic” declaration.
Parameters in design units
Default value of the parameter (optional).
entity adder_n is
generic (size : integer := 4 );
port (a,b
:in std_logic_vector(size-1 downto 0);
sum
:out std_logic_vector(size-1 downto 0);
cout :out std_logic);
You can use parameters as soon as
end adder_n;
they are defined.
• Default parameters value can be changed in the component declaration:
component adder_n is
generic (size : integer := 8 );
port (a,b
:in std_logic_vector(size-1 downto 0);
sum
:out std_logic_vector(size-1 downto 0);
cout :out std_logic);
end adder_n;
• You can change parameters during component instantiation:
fa2: adder_n generic map(size=>12)
port map(a,b,sum,carry);
fa3: adder_n generic map(14)
port map(sum=>s, a=>d1, b=>d2, carry=>p);
Generate instruction (1)
• To automatically repeat synthesis of the instructions in the concurent part of the
architecture you can use the „generate” instruction (this is equivalent to the „for” loop in
processes). You can automatically instantiate components this way.
entity fulladder_n is
generic (size : integer := 4);
port(a,b : in std_logic_vector(size-1 downto 0);
sum:out std_logic_vector(size-1 downto 0);
cin:in std_logic;
The label is
cout:out std_logic);
mandatory!
The i variable is
end entity fulladder_n;
automatically defined
architecture dataflow of fulladder_n is
signal c:std_logic_vector(size downto 0);
begin
c(0) <= cin;
G: for i in 0 to size-1 generate
sum(i) <= a(i) xor b(i) xor c(i);
c(i+1) <= (a(i) and b(i)) or (a(i) and c(i)) or (b(i) and c(i));
end generate;
cout <= c(size);
end architecture dataflow;
Generate instruction (2)
• The „generate” instruction is used to automatically generate regular
structures based on a template structure.
• Inside the „generate” loop any concurent instruction can be used including
the „generate” itself.
• There is also the concurent ”if” instruction which can be used for irregular
structures and conditional synthesis.
Generate instruction (3)
• Generation of irregular structures (half adder used for LSB – no carry input):
entity adder_n is
generic (size : integer := 4 );
port (a,b: in std_logic_vector(size-1 downto 0);
sum: out std_logic_vector(size-1 downto 0);
cout: out std_logic);
end adder_n;
architecture dataflow of adder_n is
component fulladder port(a,b,cin:in std_logic;
sum,cout:out std_logic);
end component fulladder;
signal c:std_logic_vector(size downto 1);
begin
G1: for i in 0 to size-1 generate
G2:
if i=0 generate
sum(i)<=a(i) xor b(i);
-- halfadder
c(i+1)<=a(i) and b(i);
end generate;
-- else and elsif are not allowed!!!
G3:
if i>0 generate
fa:
fulladder port map(a(i),b(i),c(i),sum(i),c(i+1));
end generate;
end generate;
cout <= c(size);
end architecture dataflow;
Download