0% found this document useful (0 votes)
38 views

Exercises 3

The document describes a VHDL module for generating a pulse-width modulated (PWM) signal with a specified duty cycle and frequency. It provides uncompleted VHDL code for a PWM module and a testbench. It instructs the reader to develop a top module implementing an 8-bit PWM connected to a prescaler to drive a motor controller from a FPGA board, setting the PWM frequency to 5 kHz from a 100 MHz clock.

Uploaded by

Suleman Malik
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
38 views

Exercises 3

The document describes a VHDL module for generating a pulse-width modulated (PWM) signal with a specified duty cycle and frequency. It provides uncompleted VHDL code for a PWM module and a testbench. It instructs the reader to develop a top module implementing an 8-bit PWM connected to a prescaler to drive a motor controller from a FPGA board, setting the PWM frequency to 5 kHz from a 100 MHz clock.

Uploaded by

Suleman Malik
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 11

Exercises Set#3: VHDL

Exercise 1: DISPLAY Module

1. Create a new project named Exercises_3, and copy into the project folder
the files prescaler.vhd and pack_pract.vhd. Add the previous source VHDL
files to the project

2. Create a new VHDL source file named display.vhd in the current project, in
order to develop the VHDL description of the display module.

3. Execute the given testbench to verify the functionality of the DISPLAY


module when it attaches to a pre-scaler with NPS=10 on a system providing
a clock fCLK=40 MHz.

4. Develop a top module which contains the DISPLAY attached to a pre-scaler


to implement them in the LX9 Microboard. The display should refresh every
20 ms when fCLK=100 MHz, displaying any 16-bit data. Synthetize and
implement the top module. Report and justify the results.

DISPLAY Module

In order to facilitate the development of the DISPLAY, a VHDL uncompleted


code is given at the end of this section. It is also provided a testbench in order
to test it.

The DISPLAY module controls the 4-digit 7-segment (and a dot segment)
display of the interface board. The input port data[15:0] contains the 16-bit
number which is visualized in the four digits of the display. The input port
dot[3:0] individually enables or disables each of the dots at the right of every
digit. For instance, when data[15:0]=0xA20F (0x means hexadecimal format)
and dot[3:0]=”1010”, the display shows A.20.F

DISPLAY
16 8
data segments
4
dot

en 4
digits
rst

Each of the digits is composed of 7 segments{a,b,c,d,e,f,g} and a dot {dp}. A


digit can show any 4-bit number, from 0x0 to 0xF.
0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7

a
f b

g 0x8 0x9 0xA 0xB 0xC 0xD 0xE 0xF

e c
d
dp

The segments and dots of the four digits are short-circuited between them in the
interface board (see the schematic of the interface board). The output port
segments[7:0]= {a,b,c,d,e,f,g,dp} attaches to the segments of all the digits. Each
digit is enabled or disabled individually with the output port digits[3:0], where
digits[3] is the left-side digit and digit[0] the right-side one. The control of the
segments[7:0] and digits[3:0] is negated logic. For instance, the next Figure
shows the control done by the DISPLAY module to visualize a 4-bit number in
one of the digits

digits[3.0]=“1011”
3 2 1 0

a b c d e f g dp a b c d e f g dp a b c d e f g dp a b c d e f g dp

segments[7:0]={a,b,c,d,e,f,g,dp}=“00100101”

Therefore, in order to visualize a 16-bit hexadecimal value at the four digits, it is


necessary the DISPLAY module showing each of the digits individually
multiplexed on time. During a periodic time (from 1ms to 5ms approximately)
the module shows one of the digits controlling the ouput ports segments[7:0]
and digit[3:0], and in the next periodic time it displays the next digit. The enable
port (input port en=’1’) asserts the changing of the digit control synchronously
with the clock.

For instance, when data[15:0]=0xA20F and dot[3:0]=”1010”, and the en port is


attached to a pre-scaler with TPS=5ms, the DISPLAY module changes the digit
control every 5 ms as depicted in the next Figure. Since the en port is
periodically asserted every 5 ms, the four digits of the display are refreshed
every 4*TPS=20ms.

The VHDL code of the DISPLAY is very simple. It is composed of a down-


counter, named idx, which continuously counts from 3 to 0. From idx, two
decoders (described in the functions F_DEC7SEG and F_DECIDX) generate
the 7-bit segments and the 4-bit digits. Since the output ports (segments[7:0]
and digits[3:0]) are negated logic, the results from these decoders are negated
to drive the output ports: The data_4bit arranges the 16-bit data into 4 nibbles
(nibble=4-bit) to easily retrieve one of them from idx.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity display is
port(
clk: in std_logic;
rst: in std_logic;
en: in std_logic;
data: in std_logic_vector(15 downto 0);
dot: in std_logic_vector(3 downto 0);
digits: out std_logic_vector(3 downto 0);
segments: out std_logic_vector(7 downto 0) );
end entity;

architecture a1 of display is

function F_DEC7SEG(a: std_logic_vector(3 downto 0)) return std_logic_vector is


variable o: std_logic_vector(6 downto 0);
begin
... --assign o from a to generate segments
return o;
end function;

function F_DECIDX(a: integer range 0 to 3) return std_logic_vector is


variable o: std_logic_vector(3 downto 0);
begin
... --assign o from a to generate digits
return o;
end function;

type T_DATA_4BIT is array(3 downto 0) of std_logic_vector(3 downto 0);


signal data_4bit: T_DATA_4BIT;
signal idx: integer range 0 to 3;
begin
data_4bit<=(data(15 downto 12), data(11 downto 8),
data(7 downto 4), data(3 downto 0));
segments<=not F_DEC7SEG(data_4bit(idx)) & not dot(idx);
digits<=not F_DECIDX(idx);

process begin
wait until rising_edge(clk);
...—assign idx to count from 3 to 0 (repeatedly)
end process;

end architecture;

----------------------------------------------------------------------
-- synthesis translate_off

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity tb_display is
end entity;

architecture tb1 of tb_display is


constant TCLK: time:=25 ns;
constant PRESCALER_COUNTS: integer:=10;
signal clk: std_logic:='0';
signal rst, dp_en: std_logic;
signal dp_data: std_logic_vector(15 downto 0);
signal dp_segments: std_logic_vector(7 downto 0);
signal dp_digits, dp_dot: std_logic_vector(3 downto 0);
begin
ps0: entity work.prescaler(a1d) generic map(PRESCALER_COUNTS)
port map(clk,rst,dp_en);
dut: entity work.display(a1)
port map(clk,rst,dp_en,dp_data,dp_dot,dp_digits,dp_segments);
clk<=not clk after TCLK/2;
rst<='0', '1' after 2*TCLK, '0' after 10*TCLK;
dp_dot<="1010";
dp_data<=X"A20F";
end architecture;

-- synthesis translate_on
Exercise 2: PWM Module

1. Create a new VHDL source file named pwm.vhd in the current project, in
order to develop the VHDL description of the PWM module

2. Execute the given testbench to verify the functionality of the PWM module
when NPWM=4, and it attaches to a pre-scaler with NPS=10 on a system
providing a clock fCLK=40 MHz. The testbench automatically changes the
d[NPWM+1:0] in all the possible combinations. Run the functional simulation
to test the correct PWM functionality.

3. Change the testbench to test the PWM when NPWM=8 in order to get
fPWM=5KHz when fCLK=100 MHz. Execute the testbench to test the correct
PWM functionality with the new conditions.

4. Develop a top module which contains an 8-bit resolution PWM (NPWN=8)


attached to a pre-scaler, to implement them in the LX9 Microboard to drive
the motor driver with a PWM featured by fPWM=5 KHz when fCLK=100 MHz.
The top module must use three of the user switches of the LX9 Microboard
to change the 3 MSBs bits of the port d (configuring D=0, D=0.25, D=0.5,
D=0.75 and D=1) and another user switch to select forward or reverse
rotation. The user LEDs may be used to visualize the outputs generated by
the PWM. Synthetize and implement the top module. Report and justify the
results.

PWM Module

The circuits generates a pulse width modulated (PWM) signal according the
required duty-cycle (D) which is periodically repeated at fPWM frequency. The
range of the duty-cycle is from 0 to 1 (0  D  1).
TPWM=1/fPWM

DTPWM

In order to facilitate the development of the PWM, a VHDL uncompleted code of


the PWM is given at the end of this section. It is also provided a testbench in
order to test it.

The resolution of the PWM is defined by a parametrized constant (NPWM) which


defines the number of bits of an internal NPWM-bit counter cnt[NPWM-1:0]. The
requested duty-cycle is codified using a (NPWM+1)-bit natural coded number
which is retrieved from the input port d[NPWM:0]. The output port q is the PWM
signal generated.
PWM
(NPWM)
NPWM+1
d q
en
q_new
rst

The PWM can be easily described using a NPWM-bit counter cnt[NPWM-1:0] which
runs 2Npwm counts from 0 to 2Npwm-1 continuously. For instance, when NPWM=4,
the cnt[3:0] counts from 0 to 15 (16 counts, from “0000” to “1111” in binary
format) on every PWM cycle. The en input port enables the counter to
increment its count synchronously with the clock input. Since the enable port is
connected to a pre-scaler output, the counter increments the count every
NPS*TCLK. Therefore, the PWM frequency is:

1 1
= = =
2 ∙ ∙ 2 ∙

For instance, when NPWM=4, NPS=10 and fCLK=40 MHz (TCLK=25 ns), the
fPWM=250 KHz (TPWM=16*10*25 ns=4000 ns=4 µs).

The number of bits of the input port d[NPWM:0] is NPWM+1 in order to achieve the
range from d=0 (D=0) to d>=2Npwm (D=1). Otherwise, if d were a NPWM-bit port,
the PWM could not reach D=1 since it would be limited to a maximum
D=(2Npwm-1)/2Npwm. For instance, when NPWM=4, d=0 (“0_0000” in binary)
generates a D=0/16=0 PWM output, d=15 (“0_1111” in binary) generates a
D=15/16 =0.9375 PWM output, and D=16 (“1_0000” in binary) generates a
D=16/16=1. Since the maximum D=1, the PWM circuit must saturate the output
(D=1) when d ≥ 2Npwm (”1_XXXX” in binary).
This way, the duty-cycle generated by the PWM circuit is (d is the natural
number coded in the d[NPWM:0] port):

; ≤2 −1
= 2
1 ; ≥2

To generate the PWM output, the natural number contained in the counter
cnt[NPWM-1:0] is compared against the natural number coded in the input port
d[NPWM:0]. A combinational circuit could generate the PWM output to q=‘1’ when
cnt < d, and q=‘0’ when cnt ≥ d. However, since the output q is connected to a
motor driver, a combinational circuit may generate undesired glitches.

In the previous example (NPWN=4, NPS=10, fCLK=40 MHz), an increment in d[4:0]


increments the duty-cycle D=1/16, as depicted in the next Figure. When
d[4:0]=”0_0000” (d=0), D=0/16=0.0, and when d[4:0]=”0_1111” (d=15),
D=15/16=0.9375. The duty-cycle achieves the maximum D= D=16/16=1.0 when
d[4:0]=”1_0000” (d=16). For values d[4:0]=”1_XXXX” (d≥16), the duty-cycle is
saturated to the maximum D=1.0, since the maximum number code in the
counter cnt[NPWM-1:0]=”1111”=15 cannot reach the value coded in the port
d[NPWM:0]=”1_XXXX” (d≥16).

TPWM=16*NPS*TCLK d(4:0)

q D=0/16=0.0 0 0000

q D=1/16=0.0625 0 0001

q D=4/16=0.25 0 0100

q 0 1000
D=8/16=0.5

q
D=12/16=0.75 0 1100

q D=15/16=0.9375 0 1111

q D=16/16=1.0 1 XXXX

The output q must be registered (not a combinational output) to eliminate


glitches that may affect the motor driver. It can be implemented with a very
simple 1-bit FSM (Finite State Machine) which changes q_int (q equals to q_int)
as depicted in the next Figure. The state changes to q_int=‘1’ when q_int=’0’
and a new PWM cycle starts (cnt=0). Similarly, the state changes to q_int=’0’
when q_int=’1’ and the counter reaches the duty-cycle requested (cnt=d). Note
two comparators are required to test the conditions: cnt=0 and cnt=d. This in
done in the VHDL code with the signals cnt_0=’1’ and cnt_d=’1’ respectively. In
the special case when d=0, the q_int must stay to ’0’ therefore the condition to
change to ‘1’ is modified to cnt=0 and d≠0. In order to avoid a new comparator
(more logic) to test the condition d=0, this condition can also be written as cnt=0
and cnt≠d to use the two previously defined comparators. Therefore, the cnt=0
and d≠0 condition is substituted by cnt=0 (cnt_0=’1’) and cnt≠d (cnt_d=’0’).
cnt=0 & cnt=d

cnt=d 1 0 cnt=0 & cnt=d

cnt=d
The q_new output is a signal which is asserted (q_new=’1’) during a single
clock cycle (TCLK) just before starting a new PWM period. It will be used later to
synchronize the rest of the system with the PWM module. This signal is also
used by the testbench to change the d port (the requested duty-cycle)
TPWM

q_new
TCLK TCLK
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity pwm is
generic(
NPWM: integer:=8 );
port(
clk: in std_logic;
rst: in std_logic;
en: in std_logic;
d: in std_logic_vector(NPWM downto 0);
q: out std_logic;
q_new: out std_logic );
end entity;

architecture a1 of pwm is
signal cnt_0, cnt_d: std_logic;
signal q_int: std_logic;
signal cnt: unsigned(NPWM-1 downto 0);
begin
q<=q_int;

cnt_0<=... --comparator cnt_0=’1’ when cnt=0


cnt_d<=...; --comparator cnt_d=’1’ when cnt=d

counter: process begin


wait until rising_edge(clk);
...--cnt assignations
end process;

fsm: process begin


wait until rising_edge(clk);
...--q_int assignations
end process;

q_new<='1' when en='1' and cnt+1=0 else '0';

end architecture;

------------------------------------------------------------------------------
-- synthesis translate_off

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity tb_pwm is
end entity;

architecture tb1 of tb_pwm is


constant TCLK: time:=25 ns;
constant PWM_NBITS: integer:=4;
constant PRESCALER_COUNTS: integer:=10;
signal clk: std_logic:='0';
signal rst, pwm_en, pwm_q, pwm_q_new: std_logic;
signal pwm_d, duty_bin: std_logic_vector(PWM_NBITS downto 0);
begin
ps0: entity work.prescaler(a1d) generic map(PRESCALER_COUNTS)
port map(clk,rst, pwm_en);
dut: entity work.pwm(a1) generic map(PWM_NBITS)
port map(clk, rst, pwm_en, pwm_d, pwm_q, pwm_q_new);

clk<=not clk after TCLK/2;


rst<='0', '1' after 2*TCLK, '0' after 10*TCLK;
pwm_d<=duty_bin;
process
constant REPETITIONS: integer:=4;
variable rep: integer:=0;
variable duty: integer:=0;
begin
wait until rising_edge(clk);
if pwm_q_new='1' then
rep:=rep+1;
if rep=REPETITIONS then
rep:=0;
duty:=duty+1;
end if;
end if;
duty_bin<=std_logic_vector(conv_unsigned(duty,duty_bin'length));
end process;

end architecture;

-- synthesis translate_on

You might also like