Very High Speed Integrated Circuit Hardware Description Language/Travail pratique/TPs ATTiny861 avec Altera
Nous allons présenter dans ce chapitre une série de 6 TPs réalisés avec une carte Altera DE2-115. Il s'agit de réaliser un compteur de passages d’abord sans processeur puis avec un processeur sans périphérique particulier. On ajoutera ensuite petit à petit des périphériques.
Pour information, ces TPs ont été réalisés avec un format de 6 TPs de durée 1h30 et des étudiants de deuxième année de Génie Électrique (2015/2016). Avec ce format, la seule chose qui peut être réalisée est du TP dirigé ! Il faudrait deux ou trois heures par séance pour un fonctionnement plus interactif avec une plus grande autonomie des étudiants. Le sixième TP n'a pas été réalisé mais a été remplacé par le septième qui est une évaluation. L'objectif était de montrer à des étudiants la possibilité d’utiliser des périphériques sophistiqués pour simplifier la programmation. Il a donc été partiellement atteint avec le TP 5.
Pour éviter la création d'un autre chapitre, nous avons complété cette série par une autre série de 6 TPs qui seront réalisés dans les mêmes conditions que la première série. Ces TPs seront numérotés TP1b, TP2b, ... et TP6b et auront pour thème la réalisation partielle d'un réveil sur des afficheurs 7 segments.
Puis... nous avons encore créé une série de 6 TPs avec un thème similaire : le réveil. Sa sortie est par contre réalisée sur l'afficheur LCD de 2x16 caractères. Ils seront numérotés de TP1c à TP6c. Ils ont été réalisés en 2016/2017 par des étudiants de GEII 2ᵉ année.
Introduction
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7. La série d'exercices proposée ici peut certainement être réalisée avec d'autres cartes terasIC, comme la DE-2 et même probablement la DE-1. La seule chose qu’il vous faudra adapter sera le fichier de contraintes. Pour le reste, rien à changer !
Le processeur embarqué qui est utilisé dans ces séries de TPs a été conçu par Andreas Hilvarsson. Il a écrit ce cœur ATTinyx61 et l'a publié chez Opencores.org. Nous l'utiliserons avec une mémoire programme de 8ko, d'où l'appellation ATTiny861 dans ce cours. Il sera programmé en C.
Le compteur de passages est évoqué dans un autre livre : Compteur de passages revu et corrigé. Il sera le fil conducteur de la première série de TPs (TP1 à TP7).
Le réveil sera abordé dans la deuxième série de TPs (TP1b à TP6b) comme dans la troisième série (TP1c à TP6c). La différence entre les deux séries est l'affichage : soit sur des afficheurs sept segments soit sur un afficheur lcd.
Nous allons, dans cette première série de TPs, construire d’abord complètement un compteur de passages sans processeur. Une fois terminé, nous allons utiliser un processeur (embarqué) sans périphérique particulier. Pour terminer cette série, nous allons d’abord déporter le transcodage vers l'extérieur, puis l’ensemble des deux compteurs BCD et des deux transcodeurs. L'objectif de ce travail est donc de bien comprendre les rapports entre le matériel et le logiciel pour des exemples relativement simples.
TP 1 (1h30)
[modifier | modifier le wikicode]Exercice 1
[modifier | modifier le wikicode]On vous demande simplement de réaliser un transcodeur pour afficher tout nombre sur 4 bits (entrée) sur un afficheur 7 segments. Avec notre carte DE2-115, pour allumer un segment il faut mettre un '0' sur la broche correspondante. Une autre caractéristique est l'absence de multiplexage des afficheurs sept segments. Pour information il y en a 8 sur la carte en question et cela nécessite donc 8x7=56 broches de FPGA.
Indication 1
[modifier | modifier le wikicode]Vous devez réaliser l'architecture correspondante pour l'entité VHDL suivante :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- zero pour allumer les LEDs
entity transcod7segs is
port (
I_in4 : in std_logic_vector(3 downto 0);
-- Ordre : gfedcba
Q_7segs : out std_logic_vector(6 downto 0)
);
end transcod7segs;
Si le nom des entrées sorties ne vous convient pas, changez-les mais assumez alors ces changements lors des nombreux câblages de ce transcodeur. Tous nos schémas gardent cette convention de noms, à commencer par le fichier de contraintes que nous donnons maintenant.
Indication 2
[modifier | modifier le wikicode]Le fichier de contraintes sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved Q_7segs[6],Output,PIN_H22,6,B6_N0,2.5 V, Q_7segs[5],Output,PIN_J22,6,B6_N0,2.5 V, Q_7segs[4],Output,PIN_L25,6,B6_N1,2.5 V, Q_7segs[3],Output,PIN_L26,6,B6_N1,2.5 V, Q_7segs[2],Output,PIN_E17,7,B7_N2,2.5 V, Q_7segs[1],Output,PIN_F22,7,B7_N0,2.5 V, Q_7segs[0],Output,PIN_G18,7,B7_N2,2.5 V, I_in4[3],Input,PIN_AD27,5,B5_N2,2.5 V, I_in4[2],Input,PIN_AC27,5,B5_N2,2.5 V, I_in4[1],Input,PIN_AC28,5,B5_N2,2.5 V, I_in4[0],Input,PIN_AB28,5,B5_N1,2.5 V,
L'enseignant vous expliquera comment trouver ces contraintes avec la documentation de la carte.
Exercice 2
[modifier | modifier le wikicode]
Réaliser la même chose avec deux afficheurs. On aura donc maintenant 8 entrées et 14 sorties. On vous demande cette réalisation avec deux composants correspondant à l'exercice 1 (transcodeurs)
Le fichier de contraintes sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved Unit7segs[6],Output,PIN_H22,6,B6_N0,2.5 V, Unit7segs[5],Output,PIN_J22,6,B6_N0,2.5 V, Unit7segs[4],Output,PIN_L25,6,B6_N1,2.5 V, Unit7segs[3],Output,PIN_L26,6,B6_N1,2.5 V, Unit7segs[2],Output,PIN_E17,7,B7_N2,2.5 V, Unit7segs[1],Output,PIN_F22,7,B7_N0,2.5 V, Unit7segs[0],Output,PIN_G18,7,B7_N2,2.5 V, Diz7segs[6],Output,PIN_U24,5,B5_N0,2.5 V, Diz7segs[5],Output,PIN_U23,5,B5_N1,2.5 V, Diz7segs[4],Output,PIN_W25,5,B5_N1,2.5 V, Diz7segs[3],Output,PIN_W22,5,B5_N0,2.5 V, Diz7segs[2],Output,PIN_W21,5,B5_N1,2.5 V, Diz7segs[1],Output,PIN_Y22,5,B5_N0,2.5 V, Diz7segs[0],Output,PIN_M24,6,B6_N2,2.5 V, sw[7],Input,PIN_AB26,5,B5_N1,2.5 V, sw[6],Input,PIN_AD26,5,B5_N2,2.5 V, sw[5],Input,PIN_AC26,5,B5_N2,2.5 V, sw[4],Input,PIN_AB27,5,B5_N1,2.5 V, sw[3],Input,PIN_AD27,5,B5_N2,2.5 V, sw[2],Input,PIN_AC27,5,B5_N2,2.5 V, sw[1],Input,PIN_AC28,5,B5_N2,2.5 V, sw[0],Input,PIN_AB28,5,B5_N1,2.5 V,
Nous mettons ici un lien vers les corrigés qui se trouvent sur le WIKI de l'IUT de Troyes. Comme ces TPs sont effectivement réalisés par les étudiants, les solutions en question ne sont accessibles qu'en accès réservé sur une partie de l'année. Essayez le lien, il est librement accessible une quarantaine de semaines dans l'année :
TP 2 (1h30)
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.
Exercice 1
[modifier | modifier le wikicode]En vous aidant du compteur décompteur décimal présenté ici, on vous demande de compléter le code pour faire un compteur décimal cascadable.
Exercice 2
[modifier | modifier le wikicode]
Le compteur cascadable de l'exercice 1 doit être testé. Pour cela vous devez lui envoyer une horloge lente. Vous allez donc assembler :
- un compteur destiné à réaliser une horloge lente (division par 2**24)
- deux compteurs décompteurs décimaux cascadés
- deux transcodeurs
Le fichier de contraintes sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved clk,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, Init,Input,PIN_Y23,5,B5_N2,2.5 V, Unit7segs[6],Output,PIN_H22,6,B6_N0,2.5 V, Unit7segs[5],Output,PIN_J22,6,B6_N0,2.5 V, Unit7segs[4],Output,PIN_L25,6,B6_N1,2.5 V, Unit7segs[3],Output,PIN_L26,6,B6_N1,2.5 V, Unit7segs[2],Output,PIN_E17,7,B7_N2,2.5 V, Unit7segs[1],Output,PIN_F22,7,B7_N0,2.5 V, Unit7segs[0],Output,PIN_G18,7,B7_N2,2.5 V, Diz7segs[6],Output,PIN_U24,5,B5_N0,2.5 V, Diz7segs[5],Output,PIN_U23,5,B5_N1,2.5 V, Diz7segs[4],Output,PIN_W25,5,B5_N1,2.5 V, Diz7segs[3],Output,PIN_W22,5,B5_N0,2.5 V, Diz7segs[2],Output,PIN_W21,5,B5_N1,2.5 V, Diz7segs[1],Output,PIN_Y22,5,B5_N0,2.5 V, Diz7segs[0],Output,PIN_M24,6,B6_N2,2.5 V, DownUp,Input,PIN_AC28,5,B5_N2,2.5 V, en,Input,PIN_AB28,5,B5_N1,2.5 V,
Ce fichier de contraintes correspond à "en" interrupteur complètement à droite, "DownUp" juste à côté et "Init" complètement à gauche.
Nous mettons ici un lien vers les corrigés qui se trouvent sur le WIKI de l'IUT de Troyes. Comme ces TPs sont effectivement réalisés par les étudiants, les solutions en question ne sont accessibles qu'en accès réservé sur une partie de l'année. Essayez le lien, il est librement accessible une quarantaine de semaines dans l'année :
TP 3 (1h30)
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.
Le compteur de passages comporte deux entrées capteurs notés CapteurDroit et CapteurGauche qui sont supposés détecter un passage. Pour nous ce sera deux interrupteurs. L'objectif du montage est de compter le nombre de personnes dans une pièce (à une seule entrée) ou de voitures dans un parking (lui aussi à une entrée). On considère donc que lorsqu'on passe devant un capteur puis devant l'autre on entre dans la pièce (ou dans le parking) et lorsque les choses se passent dans l'autre sens on sort. Un séquenceur devra être couplé à un compteur décompteur pour réaliser ce cahier des charges.
Exercice 1
[modifier | modifier le wikicode]
Réaliser le séquenceur en complétant le code correspondant trouvé ICI dans un autre livre. Vous allez ensuite assembler :
- un compteur destiné à réaliser une horloge lente (division par 2**20) : ce sera l'horloge générale
- un séquenceur
- deux compteurs décompteurs décimaux cascadés
- deux transcodeurs
Le fichier de contrainte sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved clk_50MHz,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, Init,Input,PIN_Y23,5,B5_N2,2.5 V, Unit7segs[6],Output,PIN_H22,6,B6_N0,2.5 V, Unit7segs[5],Output,PIN_J22,6,B6_N0,2.5 V, Unit7segs[4],Output,PIN_L25,6,B6_N1,2.5 V, Unit7segs[3],Output,PIN_L26,6,B6_N1,2.5 V, Unit7segs[2],Output,PIN_E17,7,B7_N2,2.5 V, Unit7segs[1],Output,PIN_F22,7,B7_N0,2.5 V, Unit7segs[0],Output,PIN_G18,7,B7_N2,2.5 V, Diz7segs[6],Output,PIN_U24,5,B5_N0,2.5 V, Diz7segs[5],Output,PIN_U23,5,B5_N1,2.5 V, Diz7segs[4],Output,PIN_W25,5,B5_N1,2.5 V, Diz7segs[3],Output,PIN_W22,5,B5_N0,2.5 V, Diz7segs[2],Output,PIN_W21,5,B5_N1,2.5 V, Diz7segs[1],Output,PIN_Y22,5,B5_N0,2.5 V, Diz7segs[0],Output,PIN_M24,6,B6_N2,2.5 V, CapteurGauche,Input,PIN_AC28,5,B5_N2,2.5 V, CapteurDroit,Input,PIN_AB28,5,B5_N1,2.5 V,
CapteurDroit est l'interrupteur complètement à droite, CapteurGauche est juste à côté tandis que Init est complètement à gauche.

Voici en schéma ce que vous devez réaliser.
Exercice 2
[modifier | modifier le wikicode]Changer le séquenceur pour laisser la possibilité de réaliser un demi-tour.
Nous mettons ici un lien vers les corrigés qui se trouvent sur le WIKI de l'IUT de Troyes. Comme ces TPs sont effectivement réalisés par les étudiants, les solutions en question ne sont accessibles qu'en accès réservé sur une partie de l'année. Essayez le lien, il est librement accessible une quarantaine de semaines dans l'année :
TP 4 (1h30)
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.

Indication : Pour la mise à jour du contenu de la mémoire seulement, sans passer par un temps de recompilation complet de l’application (donc plus ou moins équivalent à data2mem), il faut valider l'option "use smart compilation". Pour y accéder : menu assignements => settings => compilation process settings => puis cocher l'option. Ne pas oublier de faire "apply" avant de quitter la fenêtre !
Vous allez maintenant réaliser vos premiers programmes en C destinés à un processeur dans le FPGA. Le code source complet du processeur vous est donné. Vous allez donc réaliser un projet pour compiler ce processeur. Cette compilation peut être assez longue (près de 10 min) mais la simple mise en RAM/ROM du programme ne prendra que 2 min une fois cette première compilation réalisée.
Voici sous forme schématique l’ensemble du processeur.
Ressources
[modifier | modifier le wikicode]Andreas Hilvarsson a écrit ce cœur ATTiny861 et l'a publié chez Opencores.org. Nous avons modifié sa façon d'y créer des périphériques et en particulier retiré tous les PORTs bidirectionnels qui ne servent à rien dans un FPGA. Nous en avons fait aussi une version directement utilisable pour les circuits Altera.
![]() |
L'hébergement de mon site perso se termine le 5 septembre 2023 ! À ce jour je n'ai pas encore décidé comment je vais gérer ce problème dont je ne suis pas à l'origine. Il en résulte que les liens de l'ensemble des corrections qui utilisent mon site perso seront indisponibles à partir de cette date pour tout ce livre. Cela concerne entre autre la remarque ci-dessous ! |
Mon site n'étant pas indéfiniment disponible, nous en publions ici une version utilisée dans ce TP. Les modifications citées n'ont été réalisées que dans le fichier "microcontroleur.vhd".
Fichier top
[modifier | modifier le wikicode]----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 08:57:48 08/26/2014
-- Design Name:
-- Module Name: microcontroleur - microcontroleur_architecture
-- Project Name:
-- Target Devices:
-- Tool versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Tiny861 is
Port ( clk : in STD_LOGIC;
Rst : in STD_LOGIC;
sw : in STD_LOGIC_VECTOR (7 downto 0);
In_PINB : in STD_LOGIC_VECTOR (7 downto 0);
Led : out STD_LOGIC_VECTOR (7 downto 0);
Diz7segs : out STD_LOGIC_VECTOR (7 downto 0);
Aff7segs : out STD_LOGIC_VECTOR (7 downto 0));
end Tiny861;
architecture microcontroleur_architecture of Tiny861 is
--Registres et PORTs de l'ATTiny861
constant OCR1A : std_logic_vector(5 downto 0) := "101101";
constant OCR1B : std_logic_vector(5 downto 0) := "101100";
constant PORTA : std_logic_vector(5 downto 0) := "011011";
constant DDRA : std_logic_vector(5 downto 0) := "011010";
constant PINA : std_logic_vector(5 downto 0) := "011001";
constant PORTB : std_logic_vector(5 downto 0) := "011000";
constant DDRB : std_logic_vector(5 downto 0) := "010111";
constant PINB : std_logic_vector(5 downto 0) := "010110";
constant ADCH : std_logic_vector(5 downto 0) := "000101";
constant ADCL : std_logic_vector(5 downto 0) := "000100";
--Registres non présents dans l'ATTiny861
constant UDR : std_logic_vector(5 downto 0) := "000011";
constant UCSRA : std_logic_vector(5 downto 0) := "000010";
constant UCSRB : std_logic_vector(5 downto 0) := "000001";
component mcu_core is
Port (
Clk : in std_logic;
Rst : in std_logic; -- Reset core when Rst='1'
En : in std_logic; -- CPU stops when En='0', could be used to slow down cpu to save power
-- PM
PM_A : out std_logic_vector(15 downto 0);
PM_Drd : in std_logic_vector(15 downto 0);
-- DM
DM_A : out std_logic_vector(15 downto 0); -- 0x00 - xxxx
DM_Areal : out std_logic_vector(15 downto 0); -- 0x60 - xxxx (same as above + io-adr offset)
DM_Drd : in std_logic_vector(7 downto 0);
DM_Dwr : out std_logic_vector(7 downto 0);
DM_rd : out std_logic;
DM_wr : out std_logic;
-- IO
IO_A : out std_logic_vector(5 downto 0); -- 0x00 - 0x3F
IO_Drd : in std_logic_vector(7 downto 0);
IO_Dwr : out std_logic_vector(7 downto 0);
IO_rd : out std_logic;
IO_wr : out std_logic;
-- OTHER
OT_FeatErr : out std_logic; -- Feature error! (Unhandled part of instruction)
OT_InstrErr : out std_logic -- Instruction error! (Unknown instruction)
);
end component mcu_core;
--PM
component pm is
Port (
Clk : in std_logic;
rst : in std_logic; -- Reset when Rst='1'
-- PM
PM_A : in std_logic_vector(15 downto 0);
PM_Drd : out std_logic_vector(15 downto 0)
);
end component pm;
component dm is
Port ( clk : in STD_LOGIC;
addr : in STD_LOGIC_VECTOR (15 downto 0);
dataread : out STD_LOGIC_VECTOR (7 downto 0);
datawrite : in STD_LOGIC_VECTOR (7 downto 0);
rd : in STD_LOGIC;
wr : in STD_LOGIC);
end component dm;
signal PM_A : std_logic_vector(15 downto 0);
signal PM_Drd : std_logic_vector(15 downto 0);
-- DM
signal DM_A : std_logic_vector(15 downto 0); -- 0x00 - xxxx
signal DM_Areal : std_logic_vector(15 downto 0); -- 0x60 - xxxx (same as above + io-adr offset)
signal DM_Drd : std_logic_vector(7 downto 0);
signal DM_Dwr : std_logic_vector(7 downto 0);
signal DM_rd : std_logic;
signal DM_wr : std_logic;
-- IO
signal IO_A : std_logic_vector(5 downto 0); -- 0x00 - 0x3F
signal IO_Drd : std_logic_vector(7 downto 0);
signal IO_Dwr : std_logic_vector(7 downto 0);
signal IO_rd : std_logic;
signal IO_wr : std_logic;
signal IO_DrdA : std_logic_vector(7 downto 0);
signal IO_DrdB : std_logic_vector(7 downto 0);
begin
core : mcu_core Port map (
Clk => clk,
Rst => Rst,
En => '1',
-- PM
PM_A => PM_A,
PM_Drd => PM_Drd,
-- DM
DM_A => DM_A,
DM_Areal => DM_Areal,
DM_Drd => DM_Drd,
DM_Dwr => DM_Dwr,
DM_rd => DM_rd,
DM_wr => DM_wr,
-- IO
IO_A => IO_A,
IO_Drd => IO_Drd,
IO_Dwr => IO_Dwr,
IO_rd => IO_rd,
IO_wr => IO_wr,
-- OTHER
OT_FeatErr => open,
OT_InstrErr => open
);
prgmem : pm port map (
Clk => clk,
Rst => '0',
-- PM
PM_A => PM_A,
PM_Drd => PM_Drd
);
datamem : dm port map (
clk => clk,
addr => DM_A,
dataread => DM_Drd,
datawrite => DM_Dwr,
rd => DM_rd,
wr => DM_wr
);
-- IO write process
--
iowr: process(CLK)
begin
if (rising_edge(CLK)) then
if (IO_wr = '1') then
case IO_A is
-- addresses for tiny861 device (use io.h).
--
when PORTA => -- PORTA=X"1B" (0X3B)
Led <= IO_Dwr;
when PORTB => -- PORTB=X"18" (0X38)
Aff7segs <= IO_Dwr;
when DDRB => -- PORTB=X"17" (0X37)
Diz7segs <= IO_Dwr;
when others =>
end case;
end if;
end if;
end process;
-- IO read process
--
iord: process(IO_rd,IO_A,In_PINB,sw)
begin
-- addresses for tinyX6 device (use iom8.h).
--
if IO_rd = '1' then
case IO_A is
when PINA => IO_Drd <= sw; -- PINA=X"19" (0X39)
when PINB => IO_Drd <= In_PINB; -- PINB=X"16" (0X36)
when others => IO_Drd <= X"AA";
end case;
end if;
end process;
end microcontroleur_architecture;
Fichier de réalisation du cœur
[modifier | modifier le wikicode]------------------------------------------------------------------------------
--
-- Written by: Andreas Hilvarsson, avmcu€opencores.org (www.syntera.se)
-- Project...: AVRtinyX61core
--
-- Purpose:
-- AVR tiny261/461/861 core
--
------------------------------------------------------------------------------
-- AVR tiny261/461/861 core
-- Copyright (C) 2008 Andreas Hilvarsson
--
-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with this library; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--
-- Andreas Hilvarsson reserves the right to distribute this core under
-- other licenses aswell.
------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity mcu_core is
Generic (
G_PM_A_bits : natural := 16;
G_DM_A_bits : natural := 16;
G_DM_Areal_bits : natural := 16
);
Port (
Clk : in std_logic;
Rst : in std_logic; -- Reset core when Rst='1'
En : in std_logic; -- CPU stops when En='0', could be used to slow down cpu to save power
-- PM
PM_A : out std_logic_vector(G_PM_A_bits-1 downto 0);
PM_Drd : in std_logic_vector(15 downto 0);
-- DM
DM_A : out std_logic_vector(G_DM_A_bits-1 downto 0); -- 0x00 - xxxx
DM_Areal : out std_logic_vector(G_DM_Areal_bits-1 downto 0); -- 0x60 - xxxx (same as above + io-adr offset)
DM_Drd : in std_logic_vector(7 downto 0);
DM_Dwr : out std_logic_vector(7 downto 0);
DM_rd : out std_logic;
DM_wr : out std_logic;
-- IO
IO_A : out std_logic_vector(5 downto 0); -- 0x00 - 0x3F
IO_Drd : in std_logic_vector(7 downto 0);
IO_Dwr : out std_logic_vector(7 downto 0);
IO_rd : out std_logic;
IO_wr : out std_logic;
-- OTHER
OT_FeatErr : out std_logic; -- Feature error! (Unhandled part of instruction)
OT_InstrErr : out std_logic -- Instruction error! (Unknown instruction)
);
end mcu_core;
-- Feature error is pulsed high if LD/LDD/LDS/ST/STD/STS instruction tries to access register map
architecture Beh of mcu_core is
------------------------------------------------------------------------------
--IO
--REG
signal reg_sreg : std_logic_vector(7 downto 0);
constant c_sreg_i : natural := 7;
constant c_sreg_t : natural := 6;
constant c_sreg_h : natural := 5;
constant c_sreg_s : natural := 4;
constant c_sreg_v : natural := 3;
constant c_sreg_n : natural := 2;
constant c_sreg_z : natural := 1;
constant c_sreg_c : natural := 0;
signal reg_sp : std_logic_vector(15 downto 0);
signal reg_pc : std_logic_vector(PM_A'range);
constant c_reg_X : std_logic_vector(4 downto 0) := "11010";
constant c_reg_Y : std_logic_vector(4 downto 0) := "11100";
constant c_reg_Z : std_logic_vector(4 downto 0) := "11110";
constant c_reg_R0 : std_logic_vector(4 downto 0) := "00000";
type half_reg_file_type is array(0 to 15) of std_logic_vector(7 downto 0);
signal reg_regs_h : half_reg_file_type; -- 1, 3 ... 31
signal reg_regs_l : half_reg_file_type; -- 0, 2 ... 30
signal reg_dData : std_logic_vector(7 downto 0); -- dest read data
signal reg_dData_d : std_logic_vector(7 downto 0); -- dest read data
signal reg_dRData : std_logic_vector(15 downto 0); -- dest read data
signal reg_dRData_d : std_logic_vector(15 downto 0); -- dest read data
signal reg_rData : std_logic_vector(7 downto 0); -- read read data
signal reg_rData_d : std_logic_vector(7 downto 0); -- read read data
signal reg_rRData : std_logic_vector(15 downto 0); -- read read data
signal reg_rRData_d : std_logic_vector(15 downto 0); -- read read data
signal reg_dAdr : std_logic_vector(4 downto 0); -- dest reg adr
signal reg_rAdr : std_logic_vector(4 downto 0); -- read reg adr
signal reg_dAdr_int : natural range 0 to 15; -- dest reg adr
signal reg_rAdr_int : natural range 0 to 15; -- read reg adr
signal reg_dWData : std_logic_vector(15 downto 0); -- dest write data
signal reg_dWE_b : std_logic; -- Write byte
signal reg_dWE_w : std_logic; -- Write word
--OTHER
signal ext_PM_A : std_logic_vector(15 downto 0);
signal ext_DM_A : std_logic_vector(15 downto 0);
signal ext_DM_Areal : std_logic_vector(15 downto 0);
signal loc_Rd5 : std_logic_vector(4 downto 0); -- xxxx xxxd dddd xxxx
signal loc_Rd5_locked : std_logic_vector(4 downto 0); -- xxxx xxxd dddd xxxx
signal loc_Rd4 : std_logic_vector(3 downto 0); -- xxxx xxxx dddd xxxx
signal loc_Rd2 : std_logic_vector(1 downto 0); -- xxxx xxxx xxdd xxxx
signal loc_Rr5 : std_logic_vector(4 downto 0); -- xxxx xxrx xxxx rrrr
signal loc_Rr4 : std_logic_vector(3 downto 0); -- xxxx xxxx xxxx rrrr
signal loc_K12 : std_logic_vector(11 downto 0); -- xxxx kkkk kkkk kkkk
signal loc_K8 : std_logic_vector(7 downto 0); -- xxxx kkkk xxxx kkkk
signal loc_K7 : std_logic_vector(6 downto 0); -- xxxx xxkk kkkk kxxx
signal loc_K6 : std_logic_vector(5 downto 0); -- xxxx xxxx kkxx kkkk
signal loc_s3 : std_logic_vector(2 downto 0); -- xxxx xxxx xsss xxxx
signal loc_b3 : std_logic_vector(2 downto 0); -- xxxx xxxx xxxx xbbb
signal loc_A6 : std_logic_vector(5 downto 0); -- xxxx xAAx xxxx AAAA
signal loc_A5 : std_logic_vector(4 downto 0); -- xxxx xxxx AAAA Axxx
signal loc_q6 : std_logic_vector(5 downto 0); -- xxqx qqxx xxxx xqqq
signal loc_add_h_flag : std_logic;
signal loc_sub_h_flag : std_logic;
signal loc_subi_h_flag : std_logic;
signal loc_add_s_flag : std_logic;
signal loc_sub_s_flag : std_logic;
signal loc_subi_s_flag : std_logic;
signal loc_add_v_flag : std_logic;
signal loc_sub_v_flag : std_logic;
signal loc_subi_v_flag : std_logic;
signal loc_std_n_flag : std_logic;
signal loc_std_z_flag : std_logic;
signal loc_add_c_flag : std_logic;
signal loc_sub_c_flag : std_logic;
signal loc_subi_c_flag : std_logic;
signal step : std_logic_vector(2 downto 0);
signal rst_step : std_logic_vector(4 downto 0);
type int_processing_state_type is ( Reset, NextInstr, WaitForInstr, HandleInstr, InstrError,
HandleLDS, HandleSTS, HandleLpmR0Z, HandleLpmZ, HandleLpmZPlus,
SkipInstr
--HandleFarJmp, HandleFarCall
);
signal proc_state : int_processing_state_type;
signal skip_next_instr : std_logic;
signal ext_Dwr : std_logic_vector(7 downto 0);
signal ext_Drd : std_logic_vector(7 downto 0);
signal ext_Drd_d : std_logic_vector(7 downto 0);
signal ext_rd : std_logic;
signal ext_wr : std_logic;
signal ext_DM_A_is_sreg : std_logic;
signal ext_DM_A_is_spl : std_logic;
signal ext_DM_A_is_sph : std_logic;
signal ext_DM_A_is_reg : std_logic;
signal ext_DM_A_is_intIO : std_logic;
signal ext_DM_A_is_io : std_logic;
signal ext_DM_A_is_dm : std_logic;
------------------------------------------------------------------------------
begin
------------------------------------------------------------------------------
loc_Rd5 <= PM_Drd(8 downto 4); -- xxxx xxxd dddd xxxx
loc_Rd4 <= PM_Drd(7 downto 4); -- xxxx xxxx dddd xxxx
loc_Rd2 <= PM_Drd(5 downto 4); -- xxxx xxxx xxdd xxxx
loc_Rr5 <= PM_Drd(9) & PM_Drd(3 downto 0); -- xxxx xxrx xxxx rrrr
loc_Rr4 <= PM_Drd(3 downto 0); -- xxxx xxxx xxxx rrrr
loc_K12 <= PM_Drd(11 downto 0); -- xxxx kkkk kkkk kkkk
loc_K8 <= PM_Drd(11 downto 8) & PM_Drd(3 downto 0); -- xxxx kkkk xxxx kkkk
loc_K7 <= PM_Drd(9 downto 3); -- xxxx xxkk kkkk kxxx
loc_K6 <= PM_Drd(7 downto 6) & PM_Drd(3 downto 0); -- xxxx xxxx kkxx kkkk
loc_s3 <= PM_Drd(6 downto 4); -- xxxx xxxx xsss xxxx
loc_b3 <= PM_Drd(2 downto 0); -- xxxx xxxx xxxx xbbb
loc_A6 <= PM_Drd(10 downto 9) & PM_Drd(3 downto 0); -- xxxx xAAx xxxx AAAA
loc_A5 <= PM_Drd(7 downto 3); -- xxxx xxxx AAAA Axxx
loc_q6 <= PM_Drd(13) & PM_Drd(11 downto 10) & PM_Drd(2 downto 0); -- xxqx qqxx xxxx xqqq
------------------------------------------------------------------------------
loc_add_h_flag <= (reg_dData_d(3) and reg_rData_d(3)) or
(reg_rData_d(3) and (not reg_dWData(3))) or
((not reg_dWData(3)) and reg_dData_d(3));
loc_add_c_flag <= (reg_dData_d(7) and reg_rData_d(7)) or
(reg_rData_d(7) and (not reg_dWData(7))) or
((not reg_dWData(7)) and reg_dData_d(7));
loc_add_v_flag <= (reg_dData_d(7) and reg_rData_d(7) and (not reg_dWData(7))) or
((not reg_dData_d(7)) and (not reg_rData_d(7)) and reg_dWData(7));
loc_sub_h_flag <= ((not reg_dData_d(3)) and reg_rData_d(3)) or
(reg_rData_d(3) and reg_dWData(3)) or
(reg_dWData(3) and (not reg_dData_d(3)));
loc_sub_c_flag <= ((not reg_dData_d(7)) and reg_rData_d(7)) or
(reg_rData_d(7) and reg_dWData(7)) or
(reg_dWData(7) and (not reg_dData_d(7)));
loc_sub_v_flag <= (reg_dData_d(7) and (not reg_rData_d(7)) and (not reg_dWData(7))) or
((not reg_dData_d(7)) and reg_rData_d(7) and reg_dWData(7));
loc_subi_h_flag <= ((not reg_dData_d(3)) and loc_K8(3)) or
(loc_K8(3) and reg_dWData(3)) or
(reg_dWData(3) and (not reg_dData_d(3)));
loc_subi_c_flag <= ((not reg_dData_d(7)) and loc_K8(7)) or
(loc_K8(7) and reg_dWData(7)) or
(reg_dWData(7) and (not reg_dData_d(7)));
loc_subi_v_flag <= (reg_dData_d(7) and (not loc_K8(7)) and (not reg_dWData(7))) or
((not reg_dData_d(7)) and loc_K8(7) and reg_dWData(7));
loc_add_s_flag <= loc_std_n_flag xor loc_add_v_flag; -- for signed tests
loc_sub_s_flag <= loc_std_n_flag xor loc_sub_v_flag; -- for signed tests
loc_subi_s_flag <= loc_std_n_flag xor loc_subi_v_flag; -- for signed tests
loc_std_n_flag <= reg_dWData(7);
loc_std_z_flag <= '1' when (reg_dWData(7 downto 0) = "00000000") else '0';
------------------------------------------------------------------------------
reg_dAdr_int <= CONV_INTEGER('0'®_dAdr(4 downto 1));
reg_rAdr_int <= CONV_INTEGER('0'®_rAdr(4 downto 1));
reg_proc : process(clk)
begin
if (clk'event and clk='1') then
if (rst='1') then
-- registers are cleared in processin_proc process
elsif (En='1') then
if (reg_dWE_w = '1') then -- word write
reg_regs_h(reg_dAdr_int) <= reg_dWData(15 downto 8);
reg_regs_l(reg_dAdr_int) <= reg_dWData(7 downto 0);
elsif (reg_dWE_b = '1') then -- byte write
if (reg_dAdr(0)= '0') then
reg_regs_l(reg_dAdr_int) <= reg_dWData(7 downto 0);
else
reg_regs_h(reg_dAdr_int) <= reg_dWData(7 downto 0); -- Only use lowe byte
end if; -- dAdr(0)
end if; --WE
end if; -- Rst/En
end if; -- Clk
end process reg_proc;
reg_dData <= reg_regs_l(reg_dAdr_int) when (reg_dAdr(0) = '0') else reg_regs_h(reg_dAdr_int);
reg_dRData <= reg_regs_h(reg_dAdr_int) & reg_regs_l(reg_dAdr_int);
reg_rData <= reg_regs_l(reg_rAdr_int) when (reg_rAdr(0) = '0') else reg_regs_h(reg_rAdr_int);
reg_rRData <= reg_regs_h(reg_rAdr_int) & reg_regs_l(reg_rAdr_int);
regd_d_proc : process(clk)
begin
if (clk'event and clk='1') then
if (rst='1') then
reg_dData_d <= (OTHERS => '0');
reg_rData_d <= (OTHERS => '0');
reg_dRData_d <= (OTHERS => '0');
reg_rRData_d <= (OTHERS => '0');
elsif (En='1') then
-- store for next clockpulse
reg_dData_d <= reg_dData;
reg_rData_d <= reg_rData;
reg_dRData_d <= reg_dRData;
reg_rRData_d <= reg_rRData;
end if; -- Rst/En
end if; -- Clk
end process regd_d_proc;
------------------------------------------------------------------------------
PM_A <= ext_PM_A(PM_A'range);
pma_proc : process (reg_pc, proc_state, reg_rRData)
begin
case proc_state is
when HandleLpmR0Z => ext_PM_A <= '0' & reg_rRData(15 downto 1);
when HandleLpmZ => ext_PM_A <= '0' & reg_rRData(15 downto 1);
when HandleLpmZPlus => ext_PM_A <= '0' & reg_rRData(15 downto 1);
when others =>
ext_PM_A <= (OTHERS=>'0');
ext_PM_A(reg_pc'range) <= reg_pc;
end case;
end process pma_proc;
------------------------------------------------------------------------------
------------------------------------------------------------------------------
------------------------------------------------------------------------------
ext_DM_A_is_sreg <= '1' when (ext_DM_Areal = X"005F") else '0';
ext_DM_A_is_sph <= '1' when (ext_DM_Areal = X"005E") else '0';
ext_DM_A_is_spl <= '1' when (ext_DM_Areal = X"005D") else '0';
ext_DM_A_is_intIO <= '1' when ((ext_DM_A_is_sreg = '1') or (ext_DM_A_is_sph = '1') or (ext_DM_A_is_spl = '1')) else '0';
ext_DM_A_is_reg <= '1' when (ext_DM_Areal(15 downto 5) = X"00"&"000") else '0';
ext_DM_A_is_io <= '1' when ( (ext_DM_A_is_intIO = '0') and
((ext_DM_Areal(15 downto 5) = X"00"&"001") or (ext_DM_Areal(15 downto 5) = X"00"&"010"))
) else '0';
ext_DM_A_is_dm <= '1' when ((ext_DM_A_is_io = '0') and (ext_DM_A_is_reg = '0') and (ext_DM_A_is_intIO = '0')) else '0';
DM_A <= ext_DM_A(DM_A'range);
ext_DM_A <= ext_DM_Areal - X"0060";
DM_Areal <= ext_DM_Areal(DM_Areal'range);
DM_Dwr <= ext_Dwr;
IO_A <= ext_DM_Areal(IO_A'range) - ("10" & X"0"); -- -0x20
IO_Dwr <= ext_Dwr;
erwp : process ( ext_DM_Areal, ext_Dwr, ext_wr, ext_rd, reg_sreg, reg_sp, io_Drd, DM_Drd,
ext_DM_A_is_sreg, ext_DM_A_is_sph, ext_DM_A_is_spl, ext_DM_A_is_reg, ext_DM_A_is_io, ext_DM_A_is_intIO, ext_DM_A_is_dm)
begin
-- defaults
DM_rd <= '0';
DM_wr <= '0';
io_rd <= '0';
io_wr <= '0';
ext_Drd <= (OTHERS => '0');
-- check whats active
if (ext_DM_A_is_sreg = '1') then
ext_Drd <= reg_sreg;
end if;
if (ext_DM_A_is_sph = '1') then
ext_Drd <= reg_sp(15 downto 8);
end if;
if (ext_DM_A_is_spl = '1') then
ext_Drd <= reg_sp(7 downto 0);
end if;
if (ext_DM_A_is_reg = '1') then
NULL; -- Do nothing, must be handled else where
end if;
if (ext_DM_A_is_io = '1') then
IO_rd <= ext_rd;
IO_wr <= ext_wr;
ext_Drd <= IO_Drd;
end if;
if (ext_DM_A_is_dm = '1') then
DM_rd <= ext_rd;
DM_wr <= ext_wr;
ext_Drd <= DM_Drd;
end if;
end process;
extd_d_proc : process(clk)
begin
if (clk'event and clk='1') then
if (rst='1') then
ext_Drd_d <= (OTHERS => '0');
elsif (En='1') then
-- store for next clockpulse
ext_Drd_d <= ext_Drd;
end if; -- Rst/En
end if; -- Clk
end process extd_d_proc;
------------------------------------------------------------------------------
processin_proc : process(clk)
variable tmp_calc9 : std_logic_vector(8 downto 0); -- used when 9 bit result is returned from calc.
variable tmp_calc16 : std_logic_vector(15 downto 0); -- used when 16 bit result is returned from calc.
variable reg_pc_plus_one : std_logic_vector(15 downto 0); -- points to next pc address
variable next_pc : std_logic_vector(15 downto 0); -- points to next pc address
begin
if (clk'event and clk='1') then
if (rst='1') then
proc_state <= Reset;
step <= (OTHERS => '0');
rst_step <= (OTHERS => '0');
OT_FeatErr <= '0';
OT_InstrErr <= '0';
skip_next_instr <= '0';
loc_Rd5_locked <= (others => '0');
-- Regs
reg_sreg <= (OTHERS => '0');
reg_sp <= (OTHERS => '0');
reg_pc <= (OTHERS => '0');
reg_dWData <= (OTHERS => '0');
reg_dAdr <= (OTHERS => '0');
reg_rAdr <= (OTHERS => '0');
reg_dWE_b <= '0';
reg_dWE_w <= '0';
-- Ext
ext_DM_Areal <= (OTHERS => '0');
ext_Dwr <= (OTHERS => '0');
ext_rd <= '0';
ext_wr <= '0';
elsif (En='1') then
-- Defaults
step <= (OTHERS => '0');
rst_step <= rst_step + 1;
OT_InstrErr <= '0';
OT_FeatErr <= '0';
reg_pc_plus_one := reg_pc + 1;
-- Regs
reg_dWData <= (OTHERS => '0');
reg_dAdr <= loc_Rd5;
reg_rAdr <= loc_Rr5;
reg_dWE_b <= '0';
reg_dWE_w <= '0';
-- Ext
ext_DM_Areal <= (OTHERS => '0');
ext_Dwr <= (OTHERS => '0');
ext_rd <= '0';
ext_wr <= '0';
-- Handle signaling of error if internal registers are access as external addresses
if ((ext_rd = '1') or (ext_wr = '1')) then
if (ext_DM_A_is_reg = '1') then
OT_FeatErr <= '1'; -- Signal error if reg is accessed
end if;
end if;
-- Handle writing of internal registers.
if (ext_wr = '1') then
if (ext_DM_A_is_sreg = '1') then
reg_sreg <= ext_Dwr;
end if;
if (ext_DM_A_is_sph = '1') then
reg_sp(15 downto 8) <= ext_Dwr;
end if;
if (ext_DM_A_is_spl = '1') then
reg_sp(7 downto 0) <= ext_Dwr;
end if;
if (ext_DM_A_is_reg = '1') then
-- synthesis off
REPORT "Not implemented"
SEVERITY ERROR;
-- synthesis on
-- TODO implement
end if;
end if;
-- Handle proc
case proc_state is
-- Processor is reset
when Reset =>
if (rst_step(4) = '0') then
-- Clear reg_regs
reg_dAdr <= rst_step(3 downto 0) & '0';
reg_dWE_w <= '1';
else
proc_state <= HandleInstr;
end if; --rst_step
-- Go to next instruction
when NextInstr =>
next_pc := reg_pc_plus_one;
reg_pc <= next_pc(reg_pc'range);
proc_state <= WaitForInstr;
-- Wait one wait state for PM
when WaitForInstr =>
skip_next_instr <= '0';
if (skip_next_instr = '0') then
proc_state <= HandleInstr;
else
proc_state <= SkipInstr;
end if;
-- Skip instruction
when SkipInstr =>
proc_state <= NextInstr;
-- If 2 word instruction move pc one extra step
if ((PM_Drd(15 downto 10) = "100100") and (PM_Drd(3 downto 0) = "0000")) then -- lds / sts
next_pc := reg_pc_plus_one;
reg_pc <= next_pc(reg_pc'range);
end if;
-- Handle instruction
when HandleInstr =>
-- Default
step <= step + 1;
skip_next_instr <= '0';
loc_Rd5_locked <= loc_Rd5; -- Safe for other separate HandleXXXX states
case PM_Drd(15 downto 12) is -- (DONE)
when "0000" => -- nop, movw, cpc, sbc, add (DONE)
case PM_Drd(11 downto 8) is
when "0000" => -- nop?
proc_state <= NextInstr;
if (PM_Drd(7 downto 0) = "00000000") then
NULL; -- do nothing
else
proc_state <= InstrError;
end if;
when "0001" => -- movw
reg_dWData <= reg_rRData;
reg_dAdr <= loc_Rd4 & '0';
reg_rAdr <= loc_Rr4 & '0';
if (step(0) = '0') then
proc_state <= HandleInstr; -- Stay here
else
reg_dWE_w <= '1';
proc_state <= NextInstr;
end if;
when "0100"|"0101"|"0110"|"0111" | "1000"|"1001"|"1010"|"1011" => -- cpc | sbc
reg_dAdr <= loc_Rd5;
reg_rAdr <= loc_Rr5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData - reg_rData - reg_sreg(c_sreg_c);
reg_dWE_b <= PM_Drd(11); -- Only write if sbc, not if cpc
when others => -- set flags and done
reg_sreg(c_sreg_h) <= loc_sub_h_flag;
reg_sreg(c_sreg_s) <= loc_sub_s_flag;
reg_sreg(c_sreg_v) <= loc_sub_v_flag;
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag and reg_sreg(c_sreg_z);
reg_sreg(c_sreg_c) <= loc_sub_c_flag;
proc_state <= NextInstr;
end case; --step
when "1100"|"1101"|"1110"|"1111" => -- add
reg_dAdr <= loc_Rd5;
reg_rAdr <= loc_Rr5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData + reg_rData;
reg_dWE_b <= '1';
when others => -- set flags and done
reg_sreg(c_sreg_h) <= loc_add_h_flag;
reg_sreg(c_sreg_s) <= loc_add_s_flag;
reg_sreg(c_sreg_v) <= loc_add_v_flag;
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
reg_sreg(c_sreg_c) <= loc_add_c_flag;
proc_state <= NextInstr;
end case; --step
when others =>
proc_state <= InstrError;
end case; --11 downto 8
when "0001" => -- cpse, cp, sub, adc (DONE)
case PM_Drd(11 downto 10) is
when "00" => -- cpse (DONE)
reg_dAdr <= loc_Rd5;
reg_rAdr <= loc_Rr5;
if (step(0) = '0') then
NULL; -- fetch data
else -- compare data
proc_state <= NextInstr;
if (reg_dData = reg_rData) then
skip_next_instr <= '1';
end if; -- eq?
end if; --step
when "01" | "10" => -- cp, sub (DONE)
reg_dAdr <= loc_Rd5;
reg_rAdr <= loc_Rr5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData - reg_rData;
reg_dWE_b <= PM_Drd(11); -- Only write if sub, not if cp
when others => -- set flags and done
reg_sreg(c_sreg_h) <= loc_sub_h_flag;
reg_sreg(c_sreg_s) <= loc_sub_s_flag;
reg_sreg(c_sreg_v) <= loc_sub_v_flag;
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
reg_sreg(c_sreg_c) <= loc_sub_c_flag;
proc_state <= NextInstr;
end case; --step
when "11" => -- adc (DONE)
reg_dAdr <= loc_Rd5;
reg_rAdr <= loc_Rr5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData + reg_rData + reg_sreg(c_sreg_c);
reg_dWE_b <= '1';
when others => -- set flags and done
reg_sreg(c_sreg_h) <= loc_add_h_flag;
reg_sreg(c_sreg_s) <= loc_add_s_flag;
reg_sreg(c_sreg_v) <= loc_add_v_flag;
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
reg_sreg(c_sreg_c) <= loc_add_c_flag;
proc_state <= NextInstr;
end case; --step
when others => NULL; --for simulation
end case; --11 downto 10
when "0010" => -- and, eor, or, mov (DONE)
case PM_Drd(11 downto 10) is
when "00" => -- and
reg_dAdr <= loc_Rd5;
reg_rAdr <= loc_Rr5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData and reg_rData;
reg_dWE_b <= '1';
when others => -- set flags and done
reg_sreg(c_sreg_s) <= loc_std_n_flag;
reg_sreg(c_sreg_v) <= '0';
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
proc_state <= NextInstr;
end case; --step
when "01" => -- eor
reg_dAdr <= loc_Rd5;
reg_rAdr <= loc_Rr5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData xor reg_rData;
reg_dWE_b <= '1';
when others => -- set flags and done
reg_sreg(c_sreg_s) <= loc_std_n_flag;
reg_sreg(c_sreg_v) <= '0';
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
proc_state <= NextInstr;
end case; --step
when "10" => -- or
reg_dAdr <= loc_Rd5;
reg_rAdr <= loc_Rr5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData or reg_rData;
reg_dWE_b <= '1';
when others => -- set flags and done
reg_sreg(c_sreg_s) <= loc_std_n_flag;
reg_sreg(c_sreg_v) <= '0';
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
proc_state <= NextInstr;
end case; --step
when "11" => -- mov
reg_dAdr <= loc_Rd5;
reg_rAdr <= loc_Rr5;
reg_dWData(7 downto 0) <= reg_rData;
if (step(0) = '0') then -- fetch data
NULL;
else -- move and done
reg_dWE_b <= '1';
proc_state <= NextInstr;
end if; -- step
when others => NULL; --for simulation
end case; --11 downto 10
when "0011" | "0101" => -- cpi, subi (DONE)
reg_dAdr <= '1' & loc_Rd4; -- only reg 16-31
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData - loc_k8;
reg_dWE_b <= PM_Drd(14); -- Only write if subi, not if cpi
when others => -- set flags and done
reg_sreg(c_sreg_h) <= loc_subi_h_flag;
reg_sreg(c_sreg_s) <= loc_subi_s_flag;
reg_sreg(c_sreg_v) <= loc_subi_v_flag;
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
reg_sreg(c_sreg_c) <= loc_subi_c_flag;
proc_state <= NextInstr;
end case; --step
when "0100" => -- sbci (DONE)
reg_dAdr <= '1' & loc_Rd4; -- only reg 16-31
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData - loc_k8 - reg_sreg(c_sreg_c);
reg_dWE_b <= '1';
when others => -- set flags and done
reg_sreg(c_sreg_h) <= loc_subi_h_flag;
reg_sreg(c_sreg_s) <= loc_subi_s_flag;
reg_sreg(c_sreg_v) <= loc_subi_v_flag;
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag and reg_sreg(c_sreg_z);
reg_sreg(c_sreg_c) <= loc_subi_c_flag;
proc_state <= NextInstr;
end case; --step
when "0110" => -- ori (DONE)
reg_dAdr <= '1' & loc_Rd4; -- only reg 16-31
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData or loc_k8;
reg_dWE_b <= '1';
when others => -- set flags and done
reg_sreg(c_sreg_s) <= loc_std_n_flag;
reg_sreg(c_sreg_v) <= '0';
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
proc_state <= NextInstr;
end case; --step
when "0111" => -- andi (DONE)
reg_dAdr <= '1' & loc_Rd4; -- only reg 16-31
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData and loc_k8;
reg_dWE_b <= '1';
when others => -- set flags and done
reg_sreg(c_sreg_s) <= loc_std_n_flag;
reg_sreg(c_sreg_v) <= '0';
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
proc_state <= NextInstr;
end case; --step
when "1000" | "1010" => -- ldd+q, std+q (DONE)
reg_dAdr <= loc_Rd5;
if (PM_Drd(3) = '0') then -- Z
reg_rAdr <= c_reg_Z;
else -- Y
reg_rAdr <= c_reg_Y;
end if;
ext_DM_Areal <= reg_rRData + loc_q6;
if (PM_Drd(9) = '0') then -- ldd+q (DONE)
reg_dWData(7 downto 0) <= ext_Drd;
case step(1 downto 0) is
when "00" => -- do reg read
when "01" => -- do sram read
ext_rd <= '1';
when "10" => -- wait on sram read
when others => -- set reg with loaded data and done
reg_dWE_b <= '1';
proc_state <= NextInstr;
end case; --step
else -- std+q (DONE)
ext_Dwr <= reg_dData;
case step(0) is
when '0' => -- do reg read
when others => -- store reg in sram and done
ext_wr <= '1';
proc_state <= NextInstr;
end case; --step
end if;
when "1001" => -- *many* (DONE)
case PM_Drd(11 downto 9) is
when "000" => -- ld, lds, lpm, pop (DONE)
case PM_Drd(3 downto 0) is
when "0000" => -- lds (DONE)
next_pc := reg_pc_plus_one;
reg_pc <= next_pc(reg_pc'range);
proc_state <= HandleLDS;
step <= (others => '0');
when "0001" | "1001" | "1101" => -- ld Z+ Y+ X+ (DONE)
reg_dAdr <= loc_Rd5;
case PM_Drd(3 downto 2) is
when "00" => reg_rAdr <= c_reg_Z;
when "10" => reg_rAdr <= c_reg_Y;
when "11" => reg_rAdr <= c_reg_X;
when others => NULL; -- for simulation
end case;
ext_DM_Areal <= reg_rRData;
reg_dWData(7 downto 0) <= ext_Drd;
case step(2 downto 0) is
when "000" => -- wait on data
when "001" => -- wait on data
ext_rd <= '1';
when "010" => -- wait on data
when "011" => -- set reg with loaded data
reg_dWE_b <= '1';
when others => -- update XYZ with +1
reg_dWE_w <= '1';
case PM_Drd(3 downto 2) is
when "00" => reg_dAdr <= c_reg_Z;
when "10" => reg_dAdr <= c_reg_Y;
when "11" => reg_dAdr <= c_reg_X;
when others => NULL; -- for simulation
end case;
reg_dWData <= reg_rRData + 1;
proc_state <= NextInstr;
end case; --step
when "0010" | "1010" | "1110" => -- ld -Z -Y -X (DONE)
reg_dAdr <= loc_Rd5;
case PM_Drd(3 downto 2) is
when "00" => reg_rAdr <= c_reg_Z;
when "10" => reg_rAdr <= c_reg_Y;
when "11" => reg_rAdr <= c_reg_X;
when others => NULL; -- for simulation
end case;
ext_DM_Areal <= reg_rRData - 1;
reg_dWData(7 downto 0) <= ext_Drd;
case step(2 downto 0) is
when "000" => -- wait on data
when "001" => -- wait on data
ext_rd <= '1';
when "010" => -- wait on data
when "011" => -- set reg with loaded data
reg_dWE_b <= '1';
when others => -- update XYZ with -1
reg_dWE_w <= '1';
case PM_Drd(3 downto 2) is
when "00" => reg_dAdr <= c_reg_Z;
when "10" => reg_dAdr <= c_reg_Y;
when "11" => reg_dAdr <= c_reg_X;
when others => NULL; -- for simulation
end case;
reg_dWData <= reg_rRData - 1;
proc_state <= NextInstr;
end case; --step
when "1100" => -- ld X (DONE)
reg_dAdr <= loc_Rd5;
reg_rAdr <= c_reg_X;
ext_DM_Areal <= reg_rRData;
reg_dWData(7 downto 0) <= ext_Drd;
case step(1 downto 0) is
when "00" => -- wait on reg data
when "01" => -- start sram read
ext_rd <= '1';
when "10" => -- wait on sram data
when others => -- set pc and done
reg_dWE_b <= '1';
proc_state <= NextInstr;
end case; --step
when "0100" => -- lpm Z (DONE)
reg_rAdr <= c_reg_Z; -- Z
proc_state <= HandleLpmZ;
step <= (others => '0');
when "0101" => -- lpm Z+ (DONE)
reg_rAdr <= c_reg_Z; -- Z
proc_state <= HandleLpmZPlus;
step <= (others => '0');
when "1111" => -- pop (DONE)
reg_dAdr <= loc_Rd5;
ext_DM_Areal <= reg_sp + 1;
reg_dWData(7 downto 0) <= ext_Drd;
case step(1 downto 0) is
when "00" => -- wait on adr
when "01" => -- do read
ext_rd <= '1';
when "10" => -- wait on data
when others => -- set reg, update sp and done
reg_dWE_b <= '1';
reg_sp <= reg_sp + 1;
proc_state <= NextInstr;
end case; --step
when others =>
proc_state <= InstrError;
end case; -- 3 downto 0
when "001" => -- st, sts, push (DONE)
case PM_Drd(3 downto 0) is
when "0000" => -- sts (DONE)
next_pc := reg_pc_plus_one;
reg_pc <= next_pc(reg_pc'range);
proc_state <= HandleSTS;
step <= (others => '0');
when "0001" | "1001" | "1101" => -- st Z+ Y+ X+ (DONE)
reg_rAdr <= loc_Rd5;
case PM_Drd(3 downto 2) is
when "00" => reg_dAdr <= c_reg_Z;
when "10" => reg_dAdr <= c_reg_Y;
when "11" => reg_dAdr <= c_reg_X;
when others => NULL; -- for simulation
end case;
reg_dWData <= reg_dRData + 1;
ext_DM_Areal <= reg_dRData;
ext_Dwr <= reg_rData;
case step(1 downto 0) is
when "00" => -- wait on data
when "01" => -- wait on data
when others => -- store reg in sram and update XYZ with +1
ext_wr <= '1';
reg_dWE_w <= '1';
proc_state <= NextInstr;
end case; --step
when "0010" | "1010" | "1110" => -- st -Z -Y -X (DONE)
reg_rAdr <= loc_Rd5;
case PM_Drd(3 downto 2) is
when "00" => reg_dAdr <= c_reg_Z;
when "10" => reg_dAdr <= c_reg_Y;
when "11" => reg_dAdr <= c_reg_X;
when others => NULL; -- for simulation
end case;
reg_dWData <= reg_dRData - 1;
ext_DM_Areal <= reg_dRData - 1;
ext_Dwr <= reg_rData;
case step(1 downto 0) is
when "00" => -- wait on data
when "01" => -- wait on data
when others => -- store reg in sram and update XYZ with -1
ext_wr <= '1';
reg_dWE_w <= '1';
proc_state <= NextInstr;
end case; --step
when "1100" => -- st X (DONE)
reg_dAdr <= loc_Rd5;
reg_rAdr <= c_reg_X;
ext_DM_Areal <= reg_rRData;
ext_Dwr <= reg_dData;
reg_dWData(7 downto 0) <= ext_Drd;
case step(1 downto 0) is
when "00" => -- wait on data
when "01" => -- wait on data
when others => -- set pc and done
ext_wr <= '1';
proc_state <= NextInstr;
end case; --step
when "1111" => -- push (DONE)
reg_dAdr <= loc_Rd5;
ext_DM_Areal <= reg_sp;
case step(0) is
when '0' => NULL; -- fetch data
when others => -- store reg, update sp and done
ext_Dwr <= reg_dData;
ext_wr <= '1';
reg_sp <= reg_sp - 1;
proc_state <= NextInstr;
end case; --step
when others =>
proc_state <= InstrError;
end case; -- 3 downto 0
when "010" => -- *many* (DONE)
case PM_Drd(3 downto 0) is
when "0000" => -- com (DONE)
reg_dAdr <= loc_Rd5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= X"FF" - reg_dData;
reg_dWE_b <= '1';
when others => -- set flags and done
reg_sreg(c_sreg_s) <= loc_std_n_flag;
reg_sreg(c_sreg_v) <= '0';
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
reg_sreg(c_sreg_c) <= '1';
proc_state <= NextInstr;
end case; --step
when "0001" => -- neg (DONE)
reg_dAdr <= loc_Rd5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= X"00" - reg_dData;
reg_dWE_b <= '1';
when others => -- set flags and done
reg_sreg(c_sreg_h) <= reg_dWData(3) or reg_dData_d(3);
if (reg_dWdata(7 downto 0) = "10000000") then
reg_sreg(c_sreg_v) <= '1';
reg_sreg(c_sreg_s) <= not loc_std_n_flag;
else
reg_sreg(c_sreg_v) <= '0';
reg_sreg(c_sreg_s) <= loc_std_n_flag;
end if;
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
if (reg_dWdata(7 downto 0) = "00000000") then
reg_sreg(c_sreg_c) <= '0';
else
reg_sreg(c_sreg_c) <= '1';
end if;
proc_state <= NextInstr;
end case; --step
when "0010" => -- swap (DONE)
reg_dAdr <= loc_Rd5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData(3 downto 0) & reg_dData(7 downto 4);
reg_dWE_b <= '1';
when others => -- set flags and done
proc_state <= NextInstr;
end case; --step
when "0011" => -- inc (DONE)
reg_dAdr <= loc_Rd5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData + 1;
reg_dWE_b <= '1';
when others => -- set flags and done
if (reg_dWdata(7 downto 0) = "10000000") then
reg_sreg(c_sreg_v) <= '1';
reg_sreg(c_sreg_s) <= not loc_std_n_flag;
else
reg_sreg(c_sreg_v) <= '0';
reg_sreg(c_sreg_s) <= loc_std_n_flag;
end if;
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
proc_state <= NextInstr;
end case; --step
when "0101" => -- asr (DONE)
reg_dAdr <= loc_Rd5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData(7)&(reg_dData(7 downto 1));
reg_dWE_b <= '1';
reg_sreg(c_sreg_c) <= reg_dData(0);
when others => -- set flags and done
reg_sreg(c_sreg_v) <= loc_std_n_flag xor reg_sreg(c_sreg_c);
reg_sreg(c_sreg_s) <= loc_std_n_flag xor loc_std_n_flag xor reg_sreg(c_sreg_c);
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
proc_state <= NextInstr;
end case; --step
when "0110" => -- lsr (DONE)
reg_dAdr <= loc_Rd5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= '0'&(reg_dData(7 downto 1));
reg_dWE_b <= '1';
reg_sreg(c_sreg_c) <= reg_dData(0);
when others => -- set flags and done
reg_sreg(c_sreg_v) <= loc_std_n_flag xor reg_sreg(c_sreg_c);
reg_sreg(c_sreg_s) <= loc_std_n_flag xor loc_std_n_flag xor reg_sreg(c_sreg_c);
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
proc_state <= NextInstr;
end case; --step
when "0111" => -- ror (DONE)
reg_dAdr <= loc_Rd5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_sreg(c_sreg_c)&(reg_dData(7 downto 1));
reg_dWE_b <= '1';
reg_sreg(c_sreg_c) <= reg_dData(0);
when others => -- set flags and done
reg_sreg(c_sreg_v) <= loc_std_n_flag xor reg_sreg(c_sreg_c);
reg_sreg(c_sreg_s) <= loc_std_n_flag xor loc_std_n_flag xor reg_sreg(c_sreg_c);
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
proc_state <= NextInstr;
end case; --step
when "1000" => -- *many* ("DONE") bset, bclr, ret, reti, sleep, wdr, lpm, spm
case PM_Drd(8 downto 7) is
when "00" => -- bset (DONE)
proc_state <= NextInstr;
reg_sreg(CONV_INTEGER(loc_s3)) <= '1';
when "01" => -- bclr (DONE)
proc_state <= NextInstr;
reg_sreg(CONV_INTEGER(loc_s3)) <= '0';
when "10" => -- ret, reti (DONE)
if (PM_Drd(6 downto 5) = "00") then
ext_DM_Areal <= reg_sp + 1;
case step(1 downto 0) is
when "00" => -- wait and set adr for pc(high)
reg_sp <= reg_sp + 1;
ext_rd <= '1';
when "01" => -- wait and set adr for pc(low)
reg_sp <= reg_sp + 1;
ext_rd <= '1';
when "10" => -- wait, fetch data pc(high)
when others => -- set pc and done
next_pc := ext_Drd_d & ext_Drd; -- ext_Drd_d now has pc(high) from stack and ext_Drd has pc(low)
reg_pc <= next_pc(reg_pc'range);
if (PM_Drd(4) = '1') then -- reti
reg_sreg(c_sreg_i) <= '1';
end if;
proc_state <= WaitForInstr;
end case; --step
else
proc_state <= InstrError;
end if;
when "11" => -- sleep, wdr, lpm, spm ("DONE")
case PM_Drd(6 downto 4) is
when "000" => -- sleep ("DONE")
proc_state <= InstrError;
when "010" => -- wdr ("DONE")
proc_state <= InstrError;
when "100" => -- lpm R0<-Z (DONE)
reg_rAdr <= c_reg_Z; -- Z
--PM_Adr <= reg_dRData;
proc_state <= HandleLpmR0Z;
step <= (others => '0');
when "110" => -- spm ("DONE")
proc_state <= InstrError;
when others =>
proc_state <= InstrError;
end case; -- 6 downto 4
when others => NULL; -- for simulation
end case; -- 8 downto 7
when "1001" => -- ijmp, icall (DONE)
if (PM_Drd(7 downto 4) = "0000") then
reg_dAdr <= c_reg_Z; -- Z
if (PM_Drd(8) = '0') then -- ijmp (DONE)
case step(0) is
when '0' => NULL; -- fetch data
when others => -- set pc and done
next_pc := reg_dRdata;
reg_pc <= next_pc(reg_pc'range);
proc_state <= WaitForInstr;
end case; --step
else -- icall (DONE)
tmp_calc16 := reg_pc_plus_one; -- next instruction
ext_DM_Areal <= reg_sp;
case step(1 downto 0) is
when "00" => -- fetch data and store pc(low)
ext_Dwr <= tmp_calc16(7 downto 0);
ext_wr <= '1';
reg_sp <= reg_sp - 1;
when "01" => -- fetch data and store pc(high)
ext_Dwr <= tmp_calc16(15 downto 8);
ext_wr <= '1';
reg_sp <= reg_sp - 1;
when others => -- set pc, update sp and done
next_pc := reg_dRdata;
reg_pc <= next_pc(reg_pc'range);
proc_state <= WaitForInstr;
end case; --step
end if;
else
proc_state <= InstrError;
end if;
when "1010" => -- dec (DONE)
reg_dAdr <= loc_Rd5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
reg_dWData(7 downto 0) <= reg_dData - 1;
reg_dWE_b <= '1';
when others => -- set flags and done
if (reg_dWdata(7 downto 0) = "01111111") then
reg_sreg(c_sreg_v) <= '1';
reg_sreg(c_sreg_s) <= not loc_std_n_flag;
else
reg_sreg(c_sreg_v) <= '0';
reg_sreg(c_sreg_s) <= loc_std_n_flag;
end if;
reg_sreg(c_sreg_n) <= loc_std_n_flag;
reg_sreg(c_sreg_z) <= loc_std_z_flag;
proc_state <= NextInstr;
end case; --step
--when "1100"|"1101" => -- jmp (DONE)
-- -- this instruction isn't available in tiny261/461/861
-- proc_state <= HandleFarJmp;
-- step <= (others => '0');
-- reg_pc <= reg_pc_plus_one;
--when "1110"|"1111" => -- call (DONE)
-- -- this instruction isn't available in tiny261/461/861
-- proc_state <= HandleFarCall;
-- step <= (others => '0');
-- reg_pc <= reg_pc_plus_one;
when others =>
proc_state <= InstrError;
end case; -- 3 downto 0
when "011" => -- adiw, sbiw (DONE)
if (PM_Drd(8) = '0') then -- adiw (DONE)
reg_dAdr <= "11" & loc_Rd2 & '0';
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
tmp_calc16 := (others => '0');
tmp_calc16(loc_k6'range) := loc_k6;
reg_dWData <= reg_dRData + tmp_calc16;
reg_dWE_w <= '1';
when others => -- set flags and done
if (reg_dWdata = X"0000") then
reg_sreg(c_sreg_z) <= '1';
else
reg_sreg(c_sreg_z) <= '0';
end if;
reg_sreg(c_sreg_n) <= reg_dWData(15);
reg_sreg(c_sreg_v) <= (not reg_dRData_d(15)) and reg_dWData(15);
reg_sreg(c_sreg_s) <= reg_dWData(15) xor ((not reg_dRData_d(15)) and reg_dWData(15));
reg_sreg(c_sreg_c) <= (not reg_dWData(15)) and reg_dRData_d(15);
proc_state <= NextInstr;
end case; --step
else -- sbiw (DONE)
reg_dAdr <= "11" & loc_Rd2 & '0';
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Do calc
tmp_calc16 := (others => '0');
tmp_calc16(loc_k6'range) := loc_k6;
reg_dWData <= reg_dRData - tmp_calc16;
reg_dWE_w <= '1';
when others => -- set flags and done
if (reg_dWdata = X"0000") then
reg_sreg(c_sreg_z) <= '1';
else
reg_sreg(c_sreg_z) <= '0';
end if;
reg_sreg(c_sreg_n) <= reg_dWData(15);
reg_sreg(c_sreg_v) <= reg_dRData_d(15) and (not reg_dWData(15));
reg_sreg(c_sreg_s) <= reg_dWData(15) xor (reg_dRData_d(15) and (not reg_dWData(15)));
reg_sreg(c_sreg_c) <= reg_dWData(15) and (not reg_dRData_d(15));
proc_state <= NextInstr;
end case; --step
end if;
when "100" => -- cbi, sbic (DONE)
if (PM_Drd(8) = '0') then -- cbi (DONE)
ext_DM_Areal <= X"0020" + loc_A5;
ext_Dwr <= ext_Drd;
ext_Dwr(CONV_INTEGER(loc_b3)) <= '0';
case step(1 downto 0) is
when "00" => NULL; -- fetch data
ext_rd <= '1';
when "01" => -- Wait for data
when others => -- Do store
ext_wr <= '1';
proc_state <= NextInstr;
end case; --step
else -- sbic (DONE)
ext_DM_Areal <= X"0020" + loc_A5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
ext_rd <= '1';
when "01" => -- Wait for data
when others => -- Do store
if (ext_Drd(CONV_INTEGER(loc_b3)) = '0') then
skip_next_instr <= '1';
end if;
proc_state <= NextInstr;
end case; --step
end if;
when "101" => -- sbi, sbis (DONE)
if (PM_Drd(8) = '0') then -- sbi (DONE)
ext_DM_Areal <= X"0020" + loc_A5;
ext_Dwr <= ext_Drd;
ext_Dwr(CONV_INTEGER(loc_b3)) <= '1';
case step(1 downto 0) is
when "00" => NULL; -- fetch data
ext_rd <= '1';
when "01" => -- Wait for data
when others => -- Do store
ext_wr <= '1';
proc_state <= NextInstr;
end case; --step
else -- sbis (DONE)
ext_DM_Areal <= X"0020" + loc_A5;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
ext_rd <= '1';
when "01" => -- Wait for data
when others => -- Do store
if (ext_Drd(CONV_INTEGER(loc_b3)) = '1') then
skip_next_instr <= '1';
end if;
proc_state <= NextInstr;
end case; --step
end if;
when others =>
proc_state <= InstrError;
end case; -- 11 downto 9
when "1011" => -- in, out (DONE)
if (PM_Drd(11) = '0') then -- in (DONE)
reg_dAdr <= loc_Rd5;
ext_DM_Areal <= X"0020" + loc_A6;
reg_dWData(7 downto 0) <= ext_Drd;
case step(1 downto 0) is
when "00" => NULL; -- wait for io data
ext_rd <= '1';
when "01" => -- Wait for data
when others => -- Do store
reg_dWE_b <= '1';
proc_state <= NextInstr;
end case; --step
else -- out (DONE)
reg_dAdr <= loc_Rd5;
ext_DM_Areal <= X"0020" + loc_A6;
ext_Dwr <= reg_dData;
case step(1 downto 0) is
when "00" => NULL; -- fetch data
when "01" => -- Wait for data
when others => -- Do store
ext_wr <= '1';
proc_state <= NextInstr;
end case; --step
end if;
when "1100" => -- rjmp (DONE)
tmp_calc16 := loc_k12(11)&loc_k12(11)&loc_k12(11)&loc_k12(11) & loc_k12; -- sign extend
next_pc := reg_pc_plus_one + tmp_calc16;
reg_pc <= next_pc(reg_pc'range);
proc_state <= WaitForInstr;
when "1101" => -- rcall (DONE)
tmp_calc16 := reg_pc_plus_one; -- next instruction
ext_DM_Areal <= reg_sp;
case step(1 downto 0) is
when "00" => -- store pc(low)
ext_Dwr <= tmp_calc16(7 downto 0);
ext_wr <= '1';
reg_sp <= reg_sp - 1;
when "01" => -- store pc(high)
ext_Dwr <= tmp_calc16(15 downto 8);
ext_wr <= '1';
reg_sp <= reg_sp - 1;
when others => -- set pc, update sp and done
tmp_calc16 := loc_k12(11)&loc_k12(11)&loc_k12(11)&loc_k12(11) & loc_k12; -- sign extend
next_pc := reg_pc_plus_one + tmp_calc16;
reg_pc <= next_pc(reg_pc'range);
proc_state <= WaitForInstr;
end case; --step
when "1110" => -- ldi (DONE)
reg_dWData(7 downto 0) <= loc_k8;
reg_dAdr <= '1' & loc_Rd4; -- only reg 16-31 could be written
reg_dWE_b <= '1';
proc_state <= NextInstr;
when "1111" => -- brbs, brbc, bld, bst, sbrc, sbrs (DONE)
if (PM_Drd(11) = '0') then -- brbs, brbc (DONE)
proc_state <= NextInstr;
if (PM_Drd(10) = '0') then -- brbs (DONE)
if (reg_sreg(CONV_INTEGER(loc_b3)) = '1') then
for i in tmp_calc16'high downto (loc_k7'high+1) loop
tmp_calc16(i) := loc_k7(loc_k7'high); -- sign extend
end loop;
tmp_calc16(loc_k7'range) := loc_k7;
next_pc := reg_pc_plus_one + tmp_calc16;
reg_pc <= next_pc(reg_pc'range);
proc_state <= WaitForInstr;
end if;
else -- brbc (DONE)
if (reg_sreg(CONV_INTEGER(loc_b3)) = '0') then
for i in tmp_calc16'high downto (loc_k7'high+1) loop
tmp_calc16(i) := loc_k7(loc_k7'high); -- sign extend
end loop;
tmp_calc16(loc_k7'range) := loc_k7;
next_pc := reg_pc_plus_one + tmp_calc16;
reg_pc <= next_pc(reg_pc'range);
proc_state <= WaitForInstr;
end if;
end if;
elsif (PM_Drd(3) = '0') then -- bld, bst, sbrc, sbrs (DONE)
case PM_Drd(10 downto 9) is
when "00" => -- bld
reg_dAdr <= loc_Rd5;
case step(0) is
when '0' => NULL; -- fetch data
when others => -- set bit and done
reg_dWData(7 downto 0) <= reg_dData;
reg_dWData(CONV_INTEGER(loc_b3)) <= reg_sreg(c_sreg_t);
reg_dWE_b <= '1';
proc_state <= NextInstr;
end case; --step
when "01" => -- bst
reg_dAdr <= loc_Rd5;
case step(0) is
when '0' => NULL; -- fetch data
when others => -- set bit and done
reg_sreg(c_sreg_t) <= reg_dData(CONV_INTEGER(loc_b3));
proc_state <= NextInstr;
end case; --step
when "10" => -- sbrc
reg_dAdr <= loc_Rd5;
if (step(0) = '0') then
NULL; -- fetch data
else -- compare data
proc_state <= NextInstr;
if (reg_dData(CONV_INTEGER(loc_b3)) = '0') then
skip_next_instr <= '1';
end if; -- eq?
end if; --step
when "11" => -- sbrs
reg_dAdr <= loc_Rd5;
if (step(0) = '0') then
NULL; -- fetch data
else -- compare data
proc_state <= NextInstr;
if (reg_dData(CONV_INTEGER(loc_b3)) = '1') then
skip_next_instr <= '1';
end if; -- eq?
end if; --step
when others => NULL; -- for simulation
end case; -- 10 downto 9
else
proc_state <= InstrError;
end if;
when others => NULL; -- for simulation
end case; --15 downto 12
-- Handle LDS instruction
when HandleLDS => -- (DONE)
step <= step + 1;
reg_dAdr <= loc_Rd5_locked;
ext_DM_Areal <= PM_Drd;
case step(1 downto 0) is
when "00" => NULL; -- wait on reg data
when "01" => -- fetch data
ext_rd <= '1';
when "10" => -- wait for data
when others => -- set reg and done
reg_dWData(7 downto 0) <= ext_Drd;
reg_dWE_b <= '1';
proc_state <= NextInstr;
end case; --step
-- Handle STS instruction
when HandleSTS => -- (DONE)
step <= step + 1;
reg_dAdr <= loc_Rd5_locked;
case step(0) is
when '0' => -- fetch data
when others => -- store data and done
ext_DM_Areal <= PM_Drd;
ext_Dwr <= reg_dData;
ext_wr <= '1';
proc_state <= NextInstr;
end case; --step
-- Handle LPM R0,Z
when HandleLpmR0Z => -- (DONE)
step <= step + 1;
reg_rAdr <= c_reg_Z;
reg_dAdr <= c_reg_R0;
case step(0) is
when '0' => NULL; -- wait for PM_Drd
when others => -- store data and done
reg_dWE_b <= '1';
if (reg_rRData(0) = '0') then
reg_dWData(7 downto 0) <= PM_Drd(7 downto 0);
else
reg_dWData(7 downto 0) <= PM_Drd(15 downto 8);
end if;
proc_state <= NextInstr;
end case; --step
-- Handle LPM Z
when HandleLpmZ => -- (DONE)
step <= step + 1;
reg_rAdr <= c_reg_Z;
reg_dAdr <= loc_Rd5_locked;
case step(0) is
when '0' => NULL; -- wait for PM_Drd
when others => -- store data and done
reg_dWE_b <= '1';
if (reg_rRData(0) = '0') then
reg_dWData(7 downto 0) <= PM_Drd(7 downto 0);
else
reg_dWData(7 downto 0) <= PM_Drd(15 downto 8);
end if;
proc_state <= NextInstr;
end case; --step
-- Handle LPM Z+
when HandleLpmZPlus => -- (DONE)
step <= step + 1;
reg_rAdr <= c_reg_Z;
reg_dAdr <= loc_Rd5_locked;
if (reg_rRData(0) = '0') then
reg_dWData(7 downto 0) <= PM_Drd(7 downto 0);
else
reg_dWData(7 downto 0) <= PM_Drd(15 downto 8);
end if;
case step(1 downto 0) is
when "00" => NULL; -- wait for PM_Drd
when "01" => -- store data
reg_dWE_b <= '1';
when others => -- update Z and done
reg_dAdr <= c_reg_Z;
reg_dWData <= reg_rRData + 1;
reg_dWE_w <= '1';
proc_state <= NextInstr;
end case; --step
-- Handle far jmp (DONE)
--when HandleFarJmp =>
-- -- this instruction isn't available in tiny261/461/861
-- reg_dAdr <= c_reg_Z; -- Z
-- step <= step + 1;
-- case step(0) is
-- when '0' => NULL; -- fetch data
-- when others => -- set pc and done
-- reg_pc <= PM_Drd;
-- proc_state <= WaitForInstr;
-- end case; --step
-- Handle far call (DONE)
--when HandleFarCall =>
-- -- -- this instruction isn't available in tiny261/461/861
-- tmp_calc16 := reg_pc_plus_one; -- next instruction
-- ext_DM_Areal <= reg_sp;
-- step <= step + 1;
-- case step(1 downto 0) is
-- when "00" => -- fetch data and store pc(low)
-- ext_Dwr <= tmp_calc16(7 downto 0);
-- ext_wr <= '1';
-- reg_sp <= reg_sp - 1;
-- when "01" => -- fetch data and store pc(high)
-- ext_Dwr <= tmp_calc16(15 downto 8);
-- ext_wr <= '1';
-- reg_sp <= reg_sp - 1;
-- when others => -- set pc, update sp and done
-- reg_pc <= PM_Drd;
-- proc_state <= WaitForInstr;
-- end case; --step
-- Handle error
when InstrError =>
-- Just hang here for now.
OT_InstrErr <= '1';
-- TODO
when others =>
NULL; -- Just hang here for now.
OT_InstrErr <= '1';
-- TODO
--proc_state <= Reset;
end case; --proc_state
end if; -- Rst / En
end if; -- clk
end process processin_proc;
------------------------------------------------------------------------------
end architecture Beh;
Fichier mémoire RAM
[modifier | modifier le wikicode]----------------------------------------------------------------------------------
-- Company:
-- Engineer:
--
-- Create Date: 11:15:55 08/26/2014
-- Design Name:
-- Module Name: dm - dm_architecture
-- Project Name:
-- Target Devices:
-- Tool versions:
-- Description:
--
-- Dependencies:
--
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity dm is
Port ( clk : in STD_LOGIC;
addr : in STD_LOGIC_VECTOR (15 downto 0);
dataread : out STD_LOGIC_VECTOR (7 downto 0);
datawrite : in STD_LOGIC_VECTOR (7 downto 0);
rd : in STD_LOGIC;
wr : in STD_LOGIC);
end dm;
architecture dm_architecture of dm is
type mem_type is array(0 to 511) of std_logic_vector(7 downto 0);
signal mem : mem_type;
begin
dmproc : process (clk)
variable a_int : natural;
variable rdwr : std_logic_vector(1 downto 0);
begin
if (clk'event and clk='1') then
a_int := CONV_INTEGER(addr);
rdwr := rd & wr;
dataread <= (others => '0');
case rdwr is
when "10" => -- rd
dataread <= mem(a_int);
when "01" => -- wr
mem(a_int) <= datawrite;
when others => NULL;
end case;
end if;
end process dmproc;
end dm_architecture;
Fichier mémoire ROM
[modifier | modifier le wikicode]Ce fichier est donné et expliqué dans l'annexe I plus loin.
Fichier de contraintes
[modifier | modifier le wikicode]Nous donnons le fichier de contraintes sous la forme csv. Il vous faudra importer ce fichier pour qu’il soit pris en compte.
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved clk,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, Rst,Input,PIN_Y23,5,B5_N2,2.5 V, Aff7segs[6],Output,PIN_H22,6,B6_N0,2.5 V, Aff7segs[5],Output,PIN_J22,6,B6_N0,2.5 V, Aff7segs[4],Output,PIN_L25,6,B6_N1,2.5 V, Aff7segs[3],Output,PIN_L26,6,B6_N1,2.5 V, Aff7segs[2],Output,PIN_E17,7,B7_N2,2.5 V, Aff7segs[1],Output,PIN_F22,7,B7_N0,2.5 V, Aff7segs[0],Output,PIN_G18,7,B7_N2,2.5 V, Diz7segs[6],Output,PIN_U24,5,B5_N0,2.5 V, Diz7segs[5],Output,PIN_U23,5,B5_N1,2.5 V, Diz7segs[4],Output,PIN_W25,5,B5_N1,2.5 V, Diz7segs[3],Output,PIN_W22,5,B5_N0,2.5 V, Diz7segs[2],Output,PIN_W21,5,B5_N1,2.5 V, Diz7segs[1],Output,PIN_Y22,5,B5_N0,2.5 V, Diz7segs[0],Output,PIN_M24,6,B6_N2,2.5 V, Led[7],Output,PIN_H19,7,B7_N2,2.5 V, Led[6],Output,PIN_J19,7,B7_N2,2.5 V, Led[5],Output,PIN_E18,7,B7_N1,2.5 V, Led[4],Output,PIN_F18,7,B7_N1,2.5 V, Led[3],Output,PIN_F21,7,B7_N0,2.5 V, Led[2],Output,PIN_E19,7,B7_N0,2.5 V, Led[1],Output,PIN_F19,7,B7_N0,2.5 V, Led[0],Output,PIN_G19,7,B7_N2,2.5 V, sw[7],Input,PIN_AB26,5,B5_N1,2.5 V, sw[6],Input,PIN_AD26,5,B5_N2,2.5 V, sw[5],Input,PIN_AC26,5,B5_N2,2.5 V, sw[4],Input,PIN_AB27,5,B5_N1,2.5 V, sw[3],Input,PIN_AD27,5,B5_N2,2.5 V, sw[2],Input,PIN_AC27,5,B5_N2,2.5 V, sw[1],Input,PIN_AC28,5,B5_N2,2.5 V, sw[0],Input,PIN_AB28,5,B5_N1,2.5 V, In_PINB[7],Input,PIN_AA22,5,B5_N2,2.5 V, In_PINB[6],Input,PIN_AA23,5,B5_N2,2.5 V, In_PINB[5],Input,PIN_AA24,5,B5_N2,2.5 V, In_PINB[4],Input,PIN_AB23,5,B5_N2,2.5 V, In_PINB[3],Input,PIN_AB24,5,B5_N2,2.5 V, In_PINB[2],Input,PIN_AC24,5,B5_N2,2.5 V, In_PINB[1],Input,PIN_AB25,5,B5_N1,2.5 V, In_PINB[0],Input,PIN_AC25,5,B5_N2,2.5 V,
Ce fichier pourra être utilisé pour toute la partie processeur du TP.
Pour information
[modifier | modifier le wikicode]Pour information la réalisation des périphériques en sortie a été réalisée comme dans la figure ci-contre. Rappelez-vous que la réalisation de périphérique de sortie se fait dans le fichier microcontroleur.vhd et dans le process iowr... et que c’est ce qui est montré dans cette figure.

Exercices
[modifier | modifier le wikicode]Voici un exemple de programme C :
#include "avr/io.h"
#undef F_CPU
#define F_CPU 15000000UL
#include "util/delay.h"
//#define UCSRB _SFR_IO8(0x01)
//#define UCSRA _SFR_IO8(0x02)
//#define UDR _SFR_IO8(0x03)
// UCSRA
//#define RXC 7
//#define TXC 6
//#define UDRE 5
//UCSRB
//#define RXEN 4
//#define TXEN 3
//***********************************************************************
// main
//***********************************************************************
int main (void) {
unsigned char ch=128;
while(1) {
// echo simple
PORTA = ch;
ch >>= 1;
if (ch == 0) ch = 128;
_delay_ms(300); // on défiler les valeurs
}
return 0;
}
Repérez dans ce code comment ne pas aller trop vite, comment décaler une valeur, comment sortir sur les leds....
Exercice 1
[modifier | modifier le wikicode]Vous allez réaliser divers chenillards dans un premier temps :
- chenillard gauche droite (très facile non ?)
- chenillard droite gauche
- chenillard droite et gauche simultanés
- chenillard à accumulation
Exercice 2
[modifier | modifier le wikicode]On vous donne deux sous-programmes facilitant cet exercice :
void incrementBCD(unsigned char *cnt) {
(*cnt)++;
if ((*cnt & 0x0F) > 0x09) *cnt += 6;
if ((*cnt & 0xF0) > 0x90) *cnt = 0;
}
void decrementBCD(unsigned char *cnt) {
(*cnt)--;
if ((*cnt & 0x0F) == 0x0F) *cnt -= 6;
if ((*cnt & 0xF0) == 0xF0) *cnt = 0x99;
}
ainsi qu'un tableau de valeurs précalculées
const unsigned char digit7segs[16]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
Vous allez réaliser divers compteurs et utiliser le ou les afficheurs sept segments.
- compteur binaire assez lent pour que l’affichage se fasse correctement en hexadécimal sur deux digits
- compteur décimal sur deux digits
Notez que c’est à vous, programmeur, de réaliser le transcodage. Un moyen simple de faire cela est d’utiliser un tableau. Ce n'est peut être pas un hasard si l’on vous a donné un tableau.
Exercice 3
[modifier | modifier le wikicode]Réaliser le compteur de passages. On vous demande de repérer dans le code ci-dessous la réalisation du séquenceur.
#include "avr/io.h"
#undef F_CPU
#define F_CPU 15000000UL
#include "util/delay.h"
//#define UCSRB _SFR_IO8(0x01)
//#define UCSRA _SFR_IO8(0x02)
//#define UDR _SFR_IO8(0x03)
// UCSRA
//#define RXC 7
//#define TXC 6
//#define UDRE 5
//UCSRB
//#define RXEN 4
//#define TXEN 3
//const unsigned char digit7segs[16]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
void incrementBCD(unsigned char *cnt);
void decrementBCD(unsigned char *cnt);
//***********************************************************************
// main
//***********************************************************************
int main (void) {
//unsigned char transcod7segs[]={0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0xFF,0x00};
unsigned char transcod7segs[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
unsigned char cmpt=0,ch=128,swPresent=0,swPasse=0,etat=1;
while(1) {
// compteur simple
PORTB = transcod7segs[cmpt&0x0F];
DDRB = transcod7segs[(cmpt&0xF0)>>4];
swPresent = PINA;
switch (etat) {
case 0x01 : if (((swPresent & 0x03)==0x01) && ((swPasse & 0x01)==0x00)) {etat = 0x02;break;}
if (((swPresent & 0x03)==0x02) && ((swPasse & 0x02)==0x00)) {etat = 0x08;break;}
case 0x02 : if (((swPresent & 0x03)==0x02) && ((swPasse & 0x02)==0x00)) etat = 0x04;break;
case 0x04 : etat = 0x01; break;
case 0x08 : if (((swPresent & 0x03)==0x01) && ((swPasse & 0x01)==0x00)) etat = 0x10;break;
case 0x10 : etat = 0x01; break;
default : etat = 0x01;
}
if (etat==0x04) incrementBCD(&cmpt);
if (etat==0x10) decrementBCD(&cmpt);
swPasse = swPresent;
PORTA = ch;
ch >>= 1;
if (ch == 0) ch = 128;
_delay_ms(300); // on verra passer les caractères
}
return 0;
}
void incrementBCD(unsigned char *cnt) {
(*cnt)++;
if ((*cnt & 0x0F) > 0x09) *cnt += 6;
if ((*cnt & 0xF0) > 0x90) *cnt = 0;
}
void decrementBCD(unsigned char *cnt) {
(*cnt)--;
if ((*cnt & 0x0F) == 0x0F) *cnt -= 6;
if ((*cnt & 0xF0) == 0xF0) *cnt = 0x99;
}
Modifier ce code pour être plus conforme au séquenceur du TP3 (7 états contre 5 ici).
Nous mettons ici un lien vers les corrigés qui se trouvent sur le WIKI de l'IUT de Troyes. Comme ces TPs sont effectivement réalisés par les étudiants, les solutions en question ne sont accessibles qu'en accès réservé sur une partie de l'année. Essayez le lien, il est librement accessible une quarantaine de semaines dans l'année :
TP 5 (1h30)
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7. Nous mettons la ressource complète sur notre site : M4209TinyStart.zip.
Nous allons déporter le transcodage du langage C vers le matériel, comme le montre la figure ci-contre.

Vous devez remarquer sur cette figure que maintenant le PORTB (8 bits) permet de commander deux afficheurs sept segments. Votre seul problème maintenant est de réaliser un compteur BCD sur 8 bits et de sortir sa valeur dans PORTB.
Exercice 1
[modifier | modifier le wikicode]Réaliser la partie matérielle dans microcontroleur.vhd en ajoutant les deux transcodeurs.
Exercice 2
[modifier | modifier le wikicode]Réaliser C un compteur BCD qui compte ou décompte en fonction d'une entrée sur un interrupteur. Évidemment ce que l’on vous demande de faire ici, c’est un programme C qui sera compilé et chargé dans le processeur.
Exercice 3
[modifier | modifier le wikicode]Refaire le compteur de passages correctement. Inspirez-vous de ce que vous venez de faire dans l'exercice précédent et du compteur de passages du TP précédent.
Nous mettons ici un lien vers les corrigés qui se trouvent sur le WIKI de l'IUT de Troyes. Comme ces TPs sont effectivement réalisés par les étudiants, les solutions en question ne sont accessibles qu'en accès réservé sur une partie de l'année. Essayez le lien, il est librement accessible une quarantaine de semaines dans l'année :
TP 6 (1h30)
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7. Nous mettons la ressource complète sur notre site : M4209TinyStart.zip.
Nous arrivons maintenant au terme de notre série de TPs. Notre objectif maintenant est d'ajouter les deux compteurs BCD comme périphériques. Ils attaqueront les deux transcodeurs qui seront chargés de l'affichage.

À partir de maintenant, fini les calculs d'additions et de soustractions BCD en C. C'est le matériel qui fera tout. Bien, le matériel fait tout mais il faut lui en donner l'ordre. Et cela, c’est de la responsabilité du processeur.
Il y a plusieurs possibilités pour réaliser cela. Nous avons choisi de réaliser cela de la manière suivante :
- le processeur est incapable de mettre une valeur particulière dans les deux compteurs BCD
- pour incrémenter le processeur écrit '1' dans le PORTB
- pour décrémenter, le processeur écrit '0' dans le PORTB
Exercice 1
[modifier | modifier le wikicode]- Réaliser la partie matérielle correspondant à la figure.
- Modifier et tester le programme du compteur de passages
Exercice 2
[modifier | modifier le wikicode]Modifier, pour terminer, la partie matérielle pour que la lecture par le processeur des deux compteurs BCD soit possible. Réaliser maintenant un programme pour tester. Une idée possible est d'afficher la valeur lue sur les LEDs du PORTA qui ne sont pas utilisées.
Nous mettons ici un lien vers les corrigés qui se trouvent sur le WIKI de l'IUT de Troyes. Comme ces TPs sont effectivement réalisés par les étudiants, les solutions en question ne sont accessibles qu'en accès réservé sur une partie de l'année. Essayez le lien, il est librement accessible une quarantaine de semaines dans l'année :
TP 7 Évaluation (1h30)
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7. Nous mettons la ressource complète sur notre site : M4209TinyStart.zip.
1°) À partir de la Correction du TP6, on vous demande de montrer que vous êtes capables de :
- faire un projet à partir de la ressource fournie
- mettre au bon endroit le programme C donné dans la correction
- le transformer en fichier .mif
- compiler la partie matérielle en remplaçant le microcontroleur.vhd de la ressource par celui de la correction
- faire le test qui montre que l’on a bien un compteur de passages
2°) Vous allez maintenant ajouter 8 leds en supplément dans votre microcontrôleur pour réaliser un chenillard sur 16 leds. Les 8 premières leds sont présentent depuis le TP4 et sont commandées par PORTA. Les 8 nouvelles seront reliées à DDRB qui n'est plus utilisé. Montrer que vous êtes capable de rajouter une sortie sur 8 bits comme indiqué dans le schéma ci-dessous :

Comme d'habitude, ce que vous avez à réaliser est indiqué en rouge. Tout ce qui est en noir est déjà réalisé dans la correction qui est accessible.
Les leds ajoutées nécessitent l'ajout des contraintes :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved LED2[7],Output,PIN_G15,7,B7_N2,2.5 V, LED2[6],Output,PIN_F15,7,B7_N2,2.5 V, LED2[5],Output,PIN_H17,7,B7_N2,2.5 V, LED2[4],Output,PIN_J16,7,B7_N2,2.5 V, LED2[3],Output,PIN_H16,7,B7_N2,2.5 V, LED2[2],Output,PIN_J15,7,B7_N2,2.5 V, LED2[1],Output,PIN_G17,7,B7_N1,2.5 V, LED2[0],Output,PIN_J17,7,B7_N2,2.5 V,
Réaliser ensuite un chenillard simple montrant le bon fonctionnement. Il devra obligatoirement être sur 16 LEDs.
![]() |
À partir de maintenant, c'est une autre série de TP qui commence. |
L'objectif de ces Travaux pratiques est de réaliser partiellement un réveil. Les trois premiers TP réalisent un comptage pour réveil en logique traditionnelle avec des composants combinatoires, les transcodeurs sept segments, et des composants séquentiels, des compteurs décimaux et modulo 6. Puis nous introduirons un processeur pour faciliter la gestion du réveil.
TP1b : Réalisation d'un transcodeur sept segments
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.
Pour commencer simplement avec l'environnement Quartus, nous allons réaliser le célèbre transcodeur 7 segments.
Exercice 1
[modifier | modifier le wikicode]Vous partirez du code suivant :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
ENTITY transcod7segs IS PORT(
e : in std_logic_vector(3 downto 0);
s7segs : out std_logic_vector(6 downto 0));
END ENTITY transcod7segs;
ARCHITECTURE arch of transcod7segs IS
BEGIN
with e select
--abcdefg
s7segs <= "0000001" when "0000",
"1001111" when "0001",
"0010010" when "0010",
"0000110" when "0011",
"1001100" when "0100",
"0100100" when "0101",
"0100000" when "0110",
"0001111" when "0111",
"0000000" when "1000",
"0000100" when "1001",
"0001000" when "1010",
"1100000" when "1011",
"0110001" when "1100",
"1000010" when "1101",
"0110000" when "1110",
"0111000" when others;
END;
Le fichier de contraintes sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved s7segs[0],Output,PIN_H22,6,B6_N0,2.5 V, s7segs[1],Output,PIN_J22,6,B6_N0,2.5 V, s7segs[2],Output,PIN_L25,6,B6_N1,2.5 V, s7segs[3],Output,PIN_L26,6,B6_N1,2.5 V, s7segs[4],Output,PIN_E17,7,B7_N2,2.5 V, s7segs[5],Output,PIN_F22,7,B7_N0,2.5 V, s7segs[6],Output,PIN_G18,7,B7_N2,2.5 V, e[3],Input,PIN_AD27,5,B5_N2,2.5 V, e[2],Input,PIN_AC27,5,B5_N2,2.5 V, e[1],Input,PIN_AC28,5,B5_N2,2.5 V, e[0],Input,PIN_AB28,5,B5_N1,2.5 V,
Exercice 2 : Utilisation de 4 digits
[modifier | modifier le wikicode]On désire étendre le travail réalisé en exercice 2 sur 4 digits.
Le fichier de contraintes sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved DIGIT0[0],Output,PIN_H22,6,B6_N0,2.5 V, DIGIT0[1],Output,PIN_J22,6,B6_N0,2.5 V, DIGIT0[2],Output,PIN_L25,6,B6_N1,2.5 V, DIGIT0[3],Output,PIN_L26,6,B6_N1,2.5 V, DIGIT0[4],Output,PIN_E17,7,B7_N2,2.5 V, DIGIT0[5],Output,PIN_F22,7,B7_N0,2.5 V, DIGIT0[6],Output,PIN_G18,7,B7_N2,2.5 V, DIGIT1[0],Output,PIN_U24,5,B5_N0,2.5 V, DIGIT1[1],Output,PIN_U23,5,B5_N1,2.5 V, DIGIT1[2],Output,PIN_W25,5,B5_N1,2.5 V, DIGIT1[3],Output,PIN_W22,5,B5_N0,2.5 V, DIGIT1[4],Output,PIN_W21,5,B5_N1,2.5 V, DIGIT1[5],Output,PIN_Y22,5,B5_N0,2.5 V, DIGIT1[6],Output,PIN_M24,6,B6_N2,2.5 V, DIGIT2[0],Output,PIN_W28,5,B5_N1,2.5 V, DIGIT2[1],Output,PIN_W27,5,B5_N1,2.5 V, DIGIT2[2],Output,PIN_Y26,5,B5_N1,2.5 V, DIGIT2[3],Output,PIN_W26,5,B5_N1,2.5 V, DIGIT2[4],Output,PIN_Y25,5,B5_N1,2.5 V, DIGIT2[5],Output,PIN_AA26,5,B5_N1,2.5 V, DIGIT2[6],Output,PIN_AA25,5,B5_N1,2.5 V, DIGIT3[0],Output,PIN_Y19,4,B4_N0,3.3-V LVTTL, DIGIT3[1],Output,PIN_AF23,4,B4_N0,3.3-V LVTTL, DIGIT3[2],Output,PIN_AD24,4,B4_N0,3.3-V LVTTL, DIGIT3[3],Output,PIN_AA21,4,B4_N0,3.3-V LVTTL, DIGIT3[4],Output,PIN_AB20,4,B4_N0,3.3-V LVTTL, DIGIT3[5],Output,PIN_U21,5,B5_N0,2.5 V, DIGIT3[6],Output,PIN_V21,5,B5_N1,2.5 V, SW[15],Input,PIN_AA22,5,B5_N2,2.5 V, SW[14],Input,PIN_AA23,5,B5_N2,2.5 V, SW[13],Input,PIN_AA24,5,B5_N2,2.5 V, SW[12],Input,PIN_AB23,5,B5_N2,2.5 V, SW[11],Input,PIN_AB24,5,B5_N2,2.5 V, SW[10],Input,PIN_AC24,5,B5_N2,2.5 V, SW[9],Input,PIN_AB25,5,B5_N1,2.5 V, SW[8],Input,PIN_AC25,5,B5_N2,2.5 V, SW[7],Input,PIN_AB26,5,B5_N1,2.5 V, SW[6],Input,PIN_AD26,5,B5_N2,2.5 V, SW[5],Input,PIN_AC26,5,B5_N2,2.5 V, SW[4],Input,PIN_AB27,5,B5_N1,2.5 V, SW[3],Input,PIN_AD27,5,B5_N2,2.5 V, SW[2],Input,PIN_AC27,5,B5_N2,2.5 V, SW[1],Input,PIN_AC28,5,B5_N2,2.5 V, SW[0],Input,PIN_AB28,5,B5_N1,2.5 V,
et vous donne le nom de vos entrées et des sorties.
TP2b : Réalisation d'un réveil HH:MN à Trois hertz
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.
Nous disposons d'un compteur BCD sur 4 digits et nous allons réaliser un réveil en modifiant un peu l'ensemble.
Exercice 1 : Faire fonctionner l'ensemble donné
[modifier | modifier le wikicode]
Nous allons réaliser ce qui est présenté dans la figure ci-contre mais sans le multiplexeur. Le retrait du multiplexeur est lié à la gestion des afficheurs sur la carte qui va nous servir pour les TPs. Sur cette carte il n'y a aucun multiplexage, c'est-à-dire qu'il est possible d'envoyer des valeurs différentes sur chacun des afficheurs sept segments.
Étant donné que chacun des compteurs a une sortie sur 4 bits et que celle-ci doit commander un affichage 7 segments, il faut donc remplacer le multiplexeur par 4 transcodeurs.
Pour vous entraîner à la lecture des "PORT MAP", un exercice intéressant est de partir du code donné ci-dessous et de refaire le schéma correspondant. Une autre manière de dire les choses est que le schéma ci-contre donne l'idée générale (cascader des compteurs BCD) mais ne représente pas complètement le code ci-dessous. Nous garderons cet étrangeté pour vous obliger à faire un dessin.
On vous donne donc le code suivant :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity Compteur4Digits is
port (
clk : in std_logic;
DIGIT3, DIGIT2, DIGIT1, DIGIT0 : out std_logic_vector(6 downto 0)
);
end Compteur4Digits;
architecture arch_Compteur4Digits of Compteur4Digits is
COMPONENT cmpt24bits IS
PORT(clk : IN STD_LOGIC;
eno3Hz : OUT STD_LOGIC);
END COMPONENT cmpt24bits;
component CounterBCD is
port( EN: in std_logic;
Clock: in std_logic;
Reset: in std_logic;
ENO : out std_logic;
Output: out std_logic_vector(3 downto 0));
end component CounterBCD;
component transcod7segs IS PORT(
e : in std_logic_vector(3 downto 0);
s7segs : out std_logic_vector(6 downto 0));
END component transcod7segs;
signal s_en3Hz : std_logic;
signal s_eno, s_eno2, s_eno3 : std_logic;
signal s_eno2_heure : std_logic_vector(8 downto 0);
signal s_reset : std_logic; -- pour les heures
signal s_data16 : std_logic_vector(15 downto 0);
begin
i1: cmpt24bits port map (
clk => clk,
eno3Hz => s_en3Hz);
bcdUnit: CounterBCD port map(
EN => s_en3Hz,
Clock => clk,
Reset => '0',
ENO => s_eno,
Output => s_data16(3 downto 0));
bcdDiz: CounterBCD port map(
EN => s_eno,
Clock => clk,
Reset => '0',
ENO => s_eno2,
Output => s_data16(7 downto 4));
bcdCent: CounterBCD port map(
EN => s_eno2,
Clock => clk,
Reset => s_reset,
ENO => s_eno3,
Output => s_data16(11 downto 8));
bcdMil: CounterBCD port map(
EN => s_eno3,
Clock => clk,
Reset => '0',
ENO => open,
Output => s_data16(15 downto 12));
i4: transcod7segs port map (
e => s_data16(3 downto 0),
s7segs => DIGIT0) ;
i5: transcod7segs port map (
e => s_data16(7 downto 4),
s7segs => DIGIT1) ;
i6: transcod7segs port map (
e => s_data16(11 downto 8),
s7segs => DIGIT2);
i7: transcod7segs port map (
e => s_data16(15 downto 12),
s7segs => DIGIT3) ;
end arch_Compteur4Digits;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity CounterBCD is
port( EN: in std_logic;
Clock: in std_logic;
Reset: in std_logic;
ENO : out std_logic;
Output: out std_logic_vector(3 downto 0));
end CounterBCD;
architecture Behavioral of CounterBCD is
signal cmpt: std_logic_vector(3 downto 0);
signal s_en_cmpt: std_logic_vector(4 downto 0);
begin process(Clock,Reset)
begin
if(rising_edge(Clock)) then
if Reset='1' then
cmpt <= "0000";
elsif EN='1' then
if cmpt="1001" then
cmpt<="0000";
else
cmpt <= cmpt + 1;
end if;
end if;
end if;
end process;
Output <= cmpt;
s_en_cmpt <= en & cmpt;
with s_en_cmpt select
ENO <= '1' when "11001",
'0' when others;
end Behavioral;
Le fichier de contraintes sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved CLK,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, DIGIT0[0],Output,PIN_H22,6,B6_N0,2.5 V, DIGIT0[1],Output,PIN_J22,6,B6_N0,2.5 V, DIGIT0[2],Output,PIN_L25,6,B6_N1,2.5 V, DIGIT0[3],Output,PIN_L26,6,B6_N1,2.5 V, DIGIT0[4],Output,PIN_E17,7,B7_N2,2.5 V, DIGIT0[5],Output,PIN_F22,7,B7_N0,2.5 V, DIGIT0[6],Output,PIN_G18,7,B7_N2,2.5 V, DIGIT1[0],Output,PIN_U24,5,B5_N0,2.5 V, DIGIT1[1],Output,PIN_U23,5,B5_N1,2.5 V, DIGIT1[2],Output,PIN_W25,5,B5_N1,2.5 V, DIGIT1[3],Output,PIN_W22,5,B5_N0,2.5 V, DIGIT1[4],Output,PIN_W21,5,B5_N1,2.5 V, DIGIT1[5],Output,PIN_Y22,5,B5_N0,2.5 V, DIGIT1[6],Output,PIN_M24,6,B6_N2,2.5 V, DIGIT2[0],Output,PIN_W28,5,B5_N1,2.5 V, DIGIT2[1],Output,PIN_W27,5,B5_N1,2.5 V, DIGIT2[2],Output,PIN_Y26,5,B5_N1,2.5 V, DIGIT2[3],Output,PIN_W26,5,B5_N1,2.5 V, DIGIT2[4],Output,PIN_Y25,5,B5_N1,2.5 V, DIGIT2[5],Output,PIN_AA26,5,B5_N1,2.5 V, DIGIT2[6],Output,PIN_AA25,5,B5_N1,2.5 V, DIGIT3[0],Output,PIN_Y19,4,B4_N0,3.3-V LVTTL, DIGIT3[1],Output,PIN_AF23,4,B4_N0,3.3-V LVTTL, DIGIT3[2],Output,PIN_AD24,4,B4_N0,3.3-V LVTTL, DIGIT3[3],Output,PIN_AA21,4,B4_N0,3.3-V LVTTL, DIGIT3[4],Output,PIN_AB20,4,B4_N0,3.3-V LVTTL, DIGIT3[5],Output,PIN_U21,5,B5_N0,2.5 V, DIGIT3[6],Output,PIN_V21,5,B5_N1,2.5 V,
Exercice 2 : Compteur modulo6
[modifier | modifier le wikicode]Pour compter les dizaines des minutes, il nous faut un compteur modulo 6 qui soit cascadable. Modifier le compteur BCD donné pour qu'il compte de 0 à 5 et génère un signal ENO quand il est à 5 et quand son entrée EN est à 1.
Indication : le compteur BCD à modifier est donné ci-dessous :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity CounterBCD is
port( EN: in std_logic;
Clock: in std_logic;
Reset: in std_logic;
ENO : out std_logic;
Output: out std_logic_vector(3 downto 0));
end CounterBCD;
architecture Behavioral of CounterBCD is
signal cmpt: std_logic_vector(3 downto 0);
signal s_en_cmpt: std_logic_vector(4 downto 0);
begin process(Clock,Reset)
begin
if(rising_edge(Clock)) then
if Reset='1' then
cmpt <= "0000";
elsif EN='1' then
if cmpt="1001" then
cmpt<="0000";
else
cmpt <= cmpt + 1;
end if;
end if;
end if;
end process;
Output <= cmpt;
s_en_cmpt <= en & cmpt;
with s_en_cmpt select
ENO <= '1' when "11001",
'0' when others;
end Behavioral;
Vous remplacerez le compteur des dizaines des minutes par ce nouveau compteur et en ferez un test. Restera donc la réalisation correcte des heures.
Exercice 3 : Compteur des heures
[modifier | modifier le wikicode]Nous allons ajouter un comptage correct des heures au compteur de l'exercice 2 qui compte normalement la partie minutes.
Le comptage des heures est lui aussi très particulier. Les unités nécessitent un compteur décimal mais qui doit être interrompu lorsque les dizaines passent à 2 ! On ne doit pas alors dépasser 3. Nous allons utiliser un montage bouclé pour réaliser cela : lorsque l'on est à 23, un EN='1' fait repasser les heures à 0. Ce n'est pas la peine de le faire pour les minutes, cela sera automatique.
Indication : la gestion du bouclage peut être réalisé par quelque chose du genre :
HeurUnit: CounterBCD port map(
EN => s_eno2,
Clock => clk,
Reset => s_reset,
ENO => s_eno3,
Output => s_data16(11 downto 8));
HeurDiz: CounterBCD port map(
EN => s_eno3,
Clock => clk,
Reset => s_reset,
ENO => open,
Output => s_data16(15 downto 12));
-- reset quand 23 est detecte par rebouclage
s_eno2_heure <= s_eno2 & s_data16(15 downto 8);
with s_eno2_heure select
s_reset <= '1' when "100100011",
'0' when others;
Faites un schéma pour comprendre.
D'abord la correction du schéma :

Vous devez retirer le multiplexeur pour ce que l'on vous demande et le remplacer par 4 transcodeurs binaire vers sept segments.
Vient ensuite le code source :
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
entity Reveil is
port (
clk : in std_logic;
HHD, HHU, MMD, MMU : out std_logic_vector(6 downto 0)
);
end Reveil;
architecture arch_Reveil of Reveil is
COMPONENT cmpt24bits IS
PORT(clk : IN STD_LOGIC;
eno3Hz : OUT STD_LOGIC);
END COMPONENT cmpt24bits;
component CounterBCD is
port( EN: in std_logic;
Clock: in std_logic;
Reset: in std_logic;
ENO : out std_logic;
Output: out std_logic_vector(3 downto 0));
end component CounterBCD;
component CounterModulo6 is -- pour dizaines minutes
port( EN: in std_logic;
Clock: in std_logic;
Reset: in std_logic;
ENO : out std_logic;
Output: out std_logic_vector(3 downto 0));
end component CounterModulo6;
component transcod7segs IS PORT(
e : in std_logic_vector(3 downto 0);
s7segs : out std_logic_vector(6 downto 0));
END component transcod7segs;
signal s_en3Hz : std_logic;
signal s_eno, s_eno2, s_eno3 : std_logic;
signal s_eno2_heure : std_logic_vector(8 downto 0);
signal s_reset : std_logic; -- pour les heures
signal s_data16 : std_logic_vector(15 downto 0);
begin
i1: cmpt24bits port map (
clk => clk,
eno3Hz => s_en3Hz);
MinUnit: CounterBCD port map(
EN => s_en3Hz,
Clock => clk,
Reset => '0',
ENO => s_eno,
Output => s_data16(3 downto 0));
MinDiz: CounterModulo6 port map(
EN => s_eno,
Clock => clk,
Reset => '0',
ENO => s_eno2,
Output => s_data16(7 downto 4));
HeurUnit: CounterBCD port map(
EN => s_eno2,
Clock => clk,
Reset => s_reset,
ENO => s_eno3,
Output => s_data16(11 downto 8));
HeurDiz: CounterBCD port map(
EN => s_eno3,
Clock => clk,
Reset => s_reset,
ENO => open,
Output => s_data16(15 downto 12));
-- reset quand 23 est detecte par rebouclage
s_eno2_heure <= s_eno2 & s_data16(15 downto 8);
with s_eno2_heure select
s_reset <= '1' when "100100011",
'0' when others;
i4: transcod7segs port map (
e => s_data16(3 downto 0),
s7segs => MMU) ;
i5: transcod7segs port map (
e => s_data16(7 downto 4),
s7segs => MMD) ;
i6: transcod7segs port map (
e => s_data16(11 downto 8),
s7segs => HHU);
i7: transcod7segs port map (
e => s_data16(15 downto 12),
s7segs => HHD) ;
end arch_Reveil;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity CounterBCD is
port( EN: in std_logic;
Clock: in std_logic;
Reset: in std_logic;
ENO : out std_logic;
Output: out std_logic_vector(3 downto 0));
end CounterBCD;
architecture Behavioral of CounterBCD is
signal cmpt: std_logic_vector(3 downto 0);
signal s_en_cmpt: std_logic_vector(4 downto 0);
begin process(Clock,Reset)
begin
if(rising_edge(Clock)) then
if Reset='1' then
cmpt <= "0000";
elsif EN='1' then
if cmpt="1001" then
cmpt<="0000";
else
cmpt <= cmpt + 1;
end if;
end if;
end if;
end process;
Output <= cmpt;
s_en_cmpt <= en & cmpt;
with s_en_cmpt select
ENO <= '1' when "11001",
'0' when others;
end Behavioral;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- pour les dizaines de minutes
entity CounterModulo6 is
port( EN: in std_logic;
Clock: in std_logic;
Reset: in std_logic;
ENO : out std_logic;
Output: out std_logic_vector(3 downto 0));
end CounterModulo6;
architecture Behavioral of CounterModulo6 is
signal cmpt: std_logic_vector(3 downto 0);
signal s_en_cmpt: std_logic_vector(4 downto 0);
begin process(Clock,Reset)
begin
if(rising_edge(Clock)) then
if Reset='1' then
cmpt <= "0000";
elsif EN='1' then
if cmpt="0101" then
cmpt<="0000";
else
cmpt <= cmpt + 1;
end if;
end if;
end if;
end process;
Output <= cmpt;
s_en_cmpt <= en & cmpt;
with s_en_cmpt select
ENO <= '1' when "10101",
'0' when others;
end Behavioral;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
ENTITY cmpt24bits IS
PORT(clk : IN STD_LOGIC;
eno3Hz : OUT STD_LOGIC);
END cmpt24bits;
ARCHITECTURE acmpt24bits OF cmpt24bits IS
signal cmpt : std_logic_vector(23 downto 0);
BEGIN
process(clk) begin
if rising_edge(clk) then
cmpt <= cmpt + 1;
end if;
end process;
eno3Hz <= '1' when cmpt(23 downto 0) = "111111111111111111111111" else
'0';
END acmpt24bits;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
ENTITY transcod7segs IS PORT(
e : in std_logic_vector(3 downto 0);
s7segs : out std_logic_vector(6 downto 0));
END ENTITY transcod7segs;
ARCHITECTURE arch of transcod7segs IS
BEGIN
with e select
--abcdefg
s7segs <= "0000001" when "0000",
"1001111" when "0001",
"0010010" when "0010",
"0000110" when "0011",
"1001100" when "0100",
"0100100" when "0101",
"0100000" when "0110",
"0001111" when "0111",
"0000000" when "1000",
"0000100" when "1001",
"0001000" when "1010",
"1100000" when "1011",
"0110001" when "1100",
"1000010" when "1101",
"0110000" when "1110",
"0111000" when others;
END;
Le fichier de contraintes correspondant est :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved CLK,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, MMU[0],Output,PIN_H22,6,B6_N0,2.5 V, MMU[1],Output,PIN_J22,6,B6_N0,2.5 V, MMU[2],Output,PIN_L25,6,B6_N1,2.5 V, MMU[3],Output,PIN_L26,6,B6_N1,2.5 V, MMU[4],Output,PIN_E17,7,B7_N2,2.5 V, MMU[5],Output,PIN_F22,7,B7_N0,2.5 V, MMU[6],Output,PIN_G18,7,B7_N2,2.5 V, MMD[0],Output,PIN_U24,5,B5_N0,2.5 V, MMD[1],Output,PIN_U23,5,B5_N1,2.5 V, MMD[2],Output,PIN_W25,5,B5_N1,2.5 V, MMD[3],Output,PIN_W22,5,B5_N0,2.5 V, MMD[4],Output,PIN_W21,5,B5_N1,2.5 V, MMD[5],Output,PIN_Y22,5,B5_N0,2.5 V, MMD[6],Output,PIN_M24,6,B6_N2,2.5 V, HHU[0],Output,PIN_W28,5,B5_N1,2.5 V, HHU[1],Output,PIN_W27,5,B5_N1,2.5 V, HHU[2],Output,PIN_Y26,5,B5_N1,2.5 V, HHU[3],Output,PIN_W26,5,B5_N1,2.5 V, HHU[4],Output,PIN_Y25,5,B5_N1,2.5 V, HHU[5],Output,PIN_AA26,5,B5_N1,2.5 V, HHU[6],Output,PIN_AA25,5,B5_N1,2.5 V, HHD[0],Output,PIN_Y19,4,B4_N0,3.3-V LVTTL, HHD[1],Output,PIN_AF23,4,B4_N0,3.3-V LVTTL, HHD[2],Output,PIN_AD24,4,B4_N0,3.3-V LVTTL, HHD[3],Output,PIN_AA21,4,B4_N0,3.3-V LVTTL, HHD[4],Output,PIN_AB20,4,B4_N0,3.3-V LVTTL, HHD[5],Output,PIN_U21,5,B5_N0,2.5 V, HHD[6],Output,PIN_V21,5,B5_N1,2.5 V,
TP3b : Réaliser un réveil qui sonne
[modifier | modifier le wikicode]
Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.
Le graphe d'évolution du réveil qui sonne est présenté ci-contre. La réalisation de ce graphe d'évolution nécessite la réalisation de ses deux entrées :
- Key : sera simplement une entrée (un switch). Il sert pour l'armement de la sonnerie du réveil.
- Trip : détecte que l'heure de sonnerie est égale à l'heure courante. L'heure courante est naturellement réalisé par le travail du TP2. L'heure de réveil à ce stade sera réalisée par 16 interrupteurs
La sortie sonnerie sera matérialisée par une LED. Une autre LED peut être utilisée pour matérialisée l'armement du réveil
Exercice : Un réveil qui sonne
[modifier | modifier le wikicode]Le graphe d'évolution sera réalisé avec un case when et sans initialisation. Le test de bon fonctionnement ne peut se faire qu'avec deux interrupteurs en entrées (un pour ArmKey et un pour Trip) et pas à 50 MHz pour éviter la prise en compte des rebonds. La sortie se fera sur une LED.
Indication 1 : On rappelle encore une fois que le style « case when » permet de ne pas chercher les équations de récurrences. Mais comme nos diagrammes d'évolutions se sont compliqués (par l'ajout d'étiquettes sur les transitions), il nous faudra ajouter des "if then". Cela est tellement intuitif que nous passons directement aux exemples. Nous commençons par présenter partiellement le réveil.
Indication 2 : Programmation sans initialisation : le principe consiste à déclarer d'abord un type énuméré avec une définition symbolique de chacun des états (ici Armed, Off, Ringing) :
TYPE typetat IS (Armed, Off, Ringing); -- dans architecture
SIGNAL etat : typetat;
Ensuite dans un « case when » on détaillera toutes les transitions possibles comme montré ci-dessous dans le cas où l'on ne s'intéresse pas à une initialisation :
-- sans initialisation
BEGIN
PROCESS (clock) BEGIN
IF clock'EVENT AND clock='1' THEN
CASE etat IS
WHEN Off => IF key ='1' THEN etat <= Armed;
ELSE etat <= Off;
END IF;
....
END CASE;
END IF;
END PROCESS;
....
L'idée générale est donc d'utiliser un « case » sur les états avec des « if » pour gérer l'ensemble des transitions.
Indication 3 : Il serait bon d'ajouter au réveil une entrée ENA qui sera une division de l'horloge par 2**20. Ceci pour éviter les rebonds des interrupteurs.
Indication 4 : le réveil est abordé dans un autre livre sous toutes les formes et il est même possible d'y trouver une solution sans initialisation et avec l'entrée ena.
Exercice 2 : Un réveil qui sonne (suite)
[modifier | modifier le wikicode]Compléter l'exercice 1 pour utiliser le séquenceur de la sonnerie mais avec Trip réalisé avec l'heure courante qui défile. Par contre l'heure de sonnerie sera réalisée par des interrupteurs.
TP4b : Un processeur seul
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.

Indication : Pour la mise à jour du contenu de la mémoire seulement, sans passer par un temps de recompilation complet de l’application (donc plus ou moins équivalent à data2mem), il faut valider l'option "use smart compilation". Pour y accéder : menu assignements => settings => compilation process settings => puis cocher l'option. Ne pas oublier de faire "apply" avant de quitter la fenêtre !
Voir le TP4 de ce chapitre pour la présentation.
Évidemment, la ressource donnée n'est pas identique à celle du TP4 puisque nous avons besoin de 4 digits d'affichage. C'est ce qui est présenté dans la figure.
Votre objectif sera de prendre contact avec un processeur qui ne possède aucun périphérique. Votre travail consistera à réaliser :
- quelques chenillards
- un comptage BCD sur 4 digits, en utilisant les sous-programmes du TP4, puis
TP5b : Déplacer les transcodeurs et s'en servir comme périphérique
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.
Indication : Pour la mise à jour du contenu de la mémoire seulement, sans passer par un temps de recompilation complet de l’application (donc plus ou moins équivalent à data2mem), il faut valider l'option "use smart compilation". Pour y accéder : menu assignements => settings => compilation process settings => puis cocher l'option. Ne pas oublier de faire "apply" avant de quitter la fenêtre !

L'utilisation de l'affichage sur quatre digits avec le Tiny861 nécessite l'utilisation de quatre registres : un par afficheur. Elle nécessite aussi l'utilisation d'un tableau pour transcoder. En clair pour que 0x1234 finisse sur les afficheurs sept segments il faut d'abord découper 0x1234 en quatre parties contenant 1, 2, 3 et 4 et les transcoder. Cela représente un travail important à réaliser par programme. Nous désirons simplifier le travail du programmeur en déplaçant le transcodage logiciel (par tableau) dans le matériel à l'aide des transcodeurs déjà réalisés dans les TPs précédents. L'affichage de 0x1234 sera alors simplement coupé en deux 0x12 et 0x34 et envoyés sur deux ports. Le transcodage sera alors complètement automatique.
Exercice 1
[modifier | modifier le wikicode]- Réaliser le périphérique demandé. Ce qui est demandé est dessiné en rouge dans la figure de cette section.
- Modifier le programme de l'exercice précédent pour que l'on affiche toujours les heures d'un réveil sur ce nouveau matériel.
TP6b : Un compteur complet comme périphérique
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.
Indication : Pour la mise à jour du contenu de la mémoire seulement, sans passer par un temps de recompilation complet de l’application (donc plus ou moins équivalent à data2mem), il faut valider l'option "use smart compilation". Pour y accéder : menu assignements => settings => compilation process settings => puis cocher l'option. Ne pas oublier de faire "apply" avant de quitter la fenêtre !

Nous allons utiliser la correction de l'exercice 3 du TP2b et en faire un périphérique. Il s'agit de l'ajouter à ce qui a été fait dans le TP5b. Ainsi l'heure du TP 5b correspond à une heure de réveil. Il est très facile de manipuler celle-ci en incrémentation ou décrémentation par un processeur.
Exercice 1
[modifier | modifier le wikicode]Prendre les 4 compteurs HH:MM du TP2 et en faire un composant.
Il devra donc avoir 2 entrées :
- horloge encore appelée clk,
- EN entrée de validation qui est reliée à un signal de 3 Hz. Le processeur devra pouvoir accéder directement aux sorties de tous les compteurs, avant le transcodage. Celles-ci devront donc être des entrées pour le processeur.
Les nouvelles sorties globales sept segments devront utiliser quatre autre digits.
Montrer qu'après réalisation de la partie matérielle, vous êtes capable de détecter que l'heure réveil est égale à l'heure courante en allumant une LED.
Exercice 2
[modifier | modifier le wikicode]Pouvez-vous améliorer le comportement de votre réveil pour un réglage de l'heure réveil ?
![]() |
À partir de maintenant, c'est une autre série de TP qui commence. |
TP1c : premier contact avec Quartus et l'affichage LCD
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7. Cette carte DE2-115 possède un afficheur LCD de deux lignes de 16 caractères.
Contrairement à ce qu'il se passe pour les TPs TP1 et TP1b, la sortie ne peut pas être directe sur l'afficheur LCD. Nous voulons dire que ce que vous allez réaliser est enfoui dans une partie de code VHDL qui est donné. Vous devez donc repérer l'entité et l'architecture à modifier pour arriver à vos fins.
Exercice 1
[modifier | modifier le wikicode]On vous donne complètement le module qui affiche sur l'afficheur. Pour cela, nous allons essayer dans cette section, d'utiliser un module VHDL touvé chez Opencores. Il se trouve dans Projets → Other → 16x2 LCD controller et a été réalisé par Daniel Drescher. Pour éviter d'aller les chercher sur Internet, ces fichiers vous sont fournis maintenant.
- d'abord le fichier de démonstration
-------------------------------------------------------------------------------
-- Title : Synthesizable demo for design "lcd16x2_ctrl"
-- Project :
-------------------------------------------------------------------------------
-- File : lcd16x2_ctrl_tb.vhd
-- Author : <stachelsau@T420>
-- Company :
-- Created : 2012-07-28
-- Last update: 2012-07-29
-- Platform :
-- Standard : VHDL'93/02
-------------------------------------------------------------------------------
-- Description: This demo writes writes a "hello world" to the display and
-- interchanges both lines periodically.
-------------------------------------------------------------------------------
-- Copyright (c) 2012
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2012-07-28 1.0 stachelsau Created
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
-------------------------------------------------------------------------------
entity lcd16x2_ctrl_demo is
port (
clk : in std_logic;
lcd_e : out std_logic;
lcd_rs : out std_logic;
lcd_rw : out std_logic;
lcd_db : out std_logic_vector(7 downto 4));
end entity lcd16x2_ctrl_demo;
-------------------------------------------------------------------------------
architecture behavior of lcd16x2_ctrl_demo is
--
signal timer : natural range 0 to 100000000 := 0;
signal switch_lines : std_logic := '0';
signal line1 : std_logic_vector(127 downto 0);
signal line2 : std_logic_vector(127 downto 0);
-- component generics
constant CLK_PERIOD_NS : positive := 20; -- 50 Mhz
-- component ports
signal rst : std_logic;
signal line1_buffer : std_logic_vector(127 downto 0);
signal line2_buffer : std_logic_vector(127 downto 0);
begin -- architecture behavior
-- component instantiation
DUT : entity work.lcd16x2_ctrl
generic map (
CLK_PERIOD_NS => CLK_PERIOD_NS)
port map (
clk => clk,
rst => rst,
lcd_e => lcd_e,
lcd_rs => lcd_rs,
lcd_rw => lcd_rw,
lcd_db => lcd_db,
line1_buffer => line1_buffer,
line2_buffer => line2_buffer);
rst <= '0';
-- see the display's datasheet for the character map
line1(127 downto 120) <= X"20";
line1(119 downto 112) <= X"20";
line1(111 downto 104) <= X"48"; -- H
line1(103 downto 96) <= X"65"; -- e
line1(95 downto 88) <= X"6c"; -- l
line1(87 downto 80) <= X"6c"; -- l
line1(79 downto 72) <= X"6f"; -- o
line1(71 downto 64) <= X"20";
line1(63 downto 56) <= X"57"; -- W
line1(55 downto 48) <= X"6f"; -- o
line1(47 downto 40) <= X"72"; -- r
line1(39 downto 32) <= X"6c"; -- l
line1(31 downto 24) <= X"64"; -- d
line1(23 downto 16) <= X"21"; -- !
line1(15 downto 8) <= X"20";
line1(7 downto 0) <= X"20";
line2(127 downto 120) <= X"30";
line2(119 downto 112) <= X"31";
line2(111 downto 104) <= X"32";
line2(103 downto 96) <= X"33";
line2(95 downto 88) <= X"34";
line2(87 downto 80) <= X"35";
line2(79 downto 72) <= X"36";
line2(71 downto 64) <= X"37";
line2(63 downto 56) <= X"38";
line2(55 downto 48) <= X"39";
line2(47 downto 40) <= X"3a";
line2(39 downto 32) <= X"3b";
line2(31 downto 24) <= X"3c";
line2(23 downto 16) <= X"3d";
line2(15 downto 8) <= X"3e";
line2(7 downto 0) <= X"3f";
line1_buffer <= line2 when switch_lines = '1' else line1;
line2_buffer <= line1 when switch_lines = '1' else line2;
-- switch lines every second
process(clk)
begin
if rising_edge(clk) then
if timer = 0 then
timer <= 100000000;
switch_lines <= not switch_lines;
else
timer <= timer - 1;
end if;
end if;
end process;
end architecture behavior;
- puis le fichier du cœur lcd, fichier que l'on gardera tout au long de ce TP
-------------------------------------------------------------------------------
-- Title : 16x2 LCD controller
-- Project :
-------------------------------------------------------------------------------
-- File : lcd16x2_ctrl.vhd
-- Author : <stachelsau@T420>
-- Company :
-- Created : 2012-07-28
-- Last update: 2012-11-28
-- Platform :
-- Standard : VHDL'93/02
-------------------------------------------------------------------------------
-- Description: The controller initializes the display when rst goes to '0'.
-- After that it writes the contend of the input signals
-- line1_buffer and line2_buffer continously to the display.
-------------------------------------------------------------------------------
-- Copyright (c) 2012
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2012-07-28 1.0 stachelsau Created
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
entity lcd16x2_ctrl is
generic (
CLK_PERIOD_NS : positive := 20); -- 50MHz
port (
clk : in std_logic;
rst : in std_logic;
lcd_e : out std_logic;
lcd_rs : out std_logic;
lcd_rw : out std_logic;
lcd_db : out std_logic_vector(7 downto 4);
line1_buffer : in std_logic_vector(127 downto 0); -- 16x8bit
line2_buffer : in std_logic_vector(127 downto 0));
end entity lcd16x2_ctrl;
architecture rtl of lcd16x2_ctrl is
constant DELAY_15_MS : positive := 15 * 10**6 / CLK_PERIOD_NS + 1;
constant DELAY_1640_US : positive := 1640 * 10**3 / CLK_PERIOD_NS + 1;
constant DELAY_4100_US : positive := 4100 * 10**3 / CLK_PERIOD_NS + 1;
constant DELAY_100_US : positive := 100 * 10**3 / CLK_PERIOD_NS + 1;
constant DELAY_40_US : positive := 40 * 10**3 / CLK_PERIOD_NS + 1;
constant DELAY_NIBBLE : positive := 10**3 / CLK_PERIOD_NS + 1;
constant DELAY_LCD_E : positive := 230 / CLK_PERIOD_NS + 1;
constant DELAY_SETUP_HOLD : positive := 40 / CLK_PERIOD_NS + 1;
constant MAX_DELAY : positive := DELAY_15_MS;
-- this record describes one write operation
type op_t is record
rs : std_logic;
data : std_logic_vector(7 downto 0);
delay_h : integer range 0 to MAX_DELAY;
delay_l : integer range 0 to MAX_DELAY;
end record op_t;
constant default_op : op_t := (rs => '1', data => X"00", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
constant op_select_line1 : op_t := (rs => '0', data => X"80", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
constant op_select_line2 : op_t := (rs => '0', data => X"C0", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US);
-- init + config operations:
-- write 3 x 0x3 followed by 0x2
-- function set command
-- entry mode set command
-- display on/off command
-- clear display
type config_ops_t is array(0 to 5) of op_t;
constant config_ops : config_ops_t
:= (5 => (rs => '0', data => X"33", delay_h => DELAY_4100_US, delay_l => DELAY_100_US),
4 => (rs => '0', data => X"32", delay_h => DELAY_40_US, delay_l => DELAY_40_US),
3 => (rs => '0', data => X"28", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
2 => (rs => '0', data => X"06", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
1 => (rs => '0', data => X"0C", delay_h => DELAY_NIBBLE, delay_l => DELAY_40_US),
0 => (rs => '0', data => X"01", delay_h => DELAY_NIBBLE, delay_l => DELAY_1640_US));
signal this_op : op_t;
type op_state_t is (IDLE,
WAIT_SETUP_H,
ENABLE_H,
WAIT_HOLD_H,
WAIT_DELAY_H,
WAIT_SETUP_L,
ENABLE_L,
WAIT_HOLD_L,
WAIT_DELAY_L,
DONE);
signal op_state : op_state_t := DONE;
signal next_op_state : op_state_t;
signal cnt : natural range 0 to MAX_DELAY;
signal next_cnt : natural range 0 to MAX_DELAY;
type state_t is (RESET,
CONFIG,
SELECT_LINE1,
WRITE_LINE1,
SELECT_LINE2,
WRITE_LINE2);
signal state : state_t := RESET;
signal next_state : state_t;
signal ptr : natural range 0 to 15 := 0;
signal next_ptr : natural range 0 to 15;
begin
proc_state : process(state, op_state, ptr, line1_buffer, line2_buffer) is
begin
case state is
when RESET =>
this_op <= default_op;
next_state <= CONFIG;
next_ptr <= config_ops_t'high;
when CONFIG =>
this_op <= config_ops(ptr);
next_ptr <= ptr;
next_state <= CONFIG;
if op_state = DONE then
next_ptr <= ptr - 1;
if ptr = 0 then
next_state <= SELECT_LINE1;
end if;
end if;
when SELECT_LINE1 =>
this_op <= op_select_line1;
next_ptr <= 15;
if op_state = DONE then
next_state <= WRITE_LINE1;
else
next_state <= SELECT_LINE1;
end if;
when WRITE_LINE1 =>
this_op <= default_op;
this_op.data <= line1_buffer(ptr*8 + 7 downto ptr*8);
next_ptr <= ptr;
next_state <= WRITE_LINE1;
if op_state = DONE then
next_ptr <= ptr - 1;
if ptr = 0 then
next_state <= SELECT_LINE2;
end if;
end if;
when SELECT_LINE2 =>
this_op <= op_select_line2;
next_ptr <= 15;
if op_state = DONE then
next_state <= WRITE_LINE2;
else
next_state <= SELECT_LINE2;
end if;
when WRITE_LINE2 =>
this_op <= default_op;
this_op.data <= line2_buffer(ptr*8 + 7 downto ptr*8);
next_ptr <= ptr;
next_state <= WRITE_LINE2;
if op_state = DONE then
next_ptr <= ptr - 1;
if ptr = 0 then
next_state <= SELECT_LINE1;
end if;
end if;
end case;
end process proc_state;
reg_state : process(clk)
begin
if rising_edge(clk) then
if rst = '1' then
state <= RESET;
ptr <= 0;
else
state <= next_state;
ptr <= next_ptr;
end if;
end if;
end process reg_state;
-- we never read from the lcd
lcd_rw <= '0';
proc_op_state : process(op_state, cnt, this_op) is
begin
case op_state is
when IDLE =>
lcd_db <= (others => '0');
lcd_rs <= '0';
lcd_e <= '0';
next_op_state <= WAIT_SETUP_H;
next_cnt <= DELAY_SETUP_HOLD;
when WAIT_SETUP_H =>
lcd_db <= this_op.data(7 downto 4);
lcd_rs <= this_op.rs;
lcd_e <= '0';
if cnt = 0 then
next_op_state <= ENABLE_H;
next_cnt <= DELAY_LCD_E;
else
next_op_state <= WAIT_SETUP_H;
next_cnt <= cnt - 1;
end if;
when ENABLE_H =>
lcd_db <= this_op.data(7 downto 4);
lcd_rs <= this_op.rs;
lcd_e <= '1';
if cnt = 0 then
next_op_state <= WAIT_HOLD_H;
next_cnt <= DELAY_SETUP_HOLD;
else
next_op_state <= ENABLE_H;
next_cnt <= cnt - 1;
end if;
when WAIT_HOLD_H =>
lcd_db <= this_op.data(7 downto 4);
lcd_rs <= this_op.rs;
lcd_e <= '0';
if cnt = 0 then
next_op_state <= WAIT_DELAY_H;
next_cnt <= this_op.delay_h;
else
next_op_state <= WAIT_HOLD_H;
next_cnt <= cnt - 1;
end if;
when WAIT_DELAY_H =>
lcd_db <= (others => '0');
lcd_rs <= '0';
lcd_e <= '0';
if cnt = 0 then
next_op_state <= WAIT_SETUP_L;
next_cnt <= DELAY_SETUP_HOLD;
else
next_op_state <= WAIT_DELAY_H;
next_cnt <= cnt - 1;
end if;
when WAIT_SETUP_L =>
lcd_db <= this_op.data(3 downto 0);
lcd_rs <= this_op.rs;
lcd_e <= '0';
if cnt = 0 then
next_op_state <= ENABLE_L;
next_cnt <= DELAY_LCD_E;
else
next_op_state <= WAIT_SETUP_L;
next_cnt <= cnt - 1;
end if;
when ENABLE_L =>
lcd_db <= this_op.data(3 downto 0);
lcd_rs <= this_op.rs;
lcd_e <= '1';
if cnt = 0 then
next_op_state <= WAIT_HOLD_L;
next_cnt <= DELAY_SETUP_HOLD;
else
next_op_state <= ENABLE_L;
next_cnt <= cnt - 1;
end if;
when WAIT_HOLD_L =>
lcd_db <= this_op.data(3 downto 0);
lcd_rs <= this_op.rs;
lcd_e <= '0';
if cnt = 0 then
next_op_state <= WAIT_DELAY_L;
next_cnt <= this_op.delay_l;
else
next_op_state <= WAIT_HOLD_L;
next_cnt <= cnt - 1;
end if;
when WAIT_DELAY_L =>
lcd_db <= (others => '0');
lcd_rs <= '0';
lcd_e <= '0';
if cnt = 0 then
next_op_state <= DONE;
next_cnt <= 0;
else
next_op_state <= WAIT_DELAY_L;
next_cnt <= cnt - 1;
end if;
when DONE =>
lcd_db <= (others => '0');
lcd_rs <= '0';
lcd_e <= '0';
next_op_state <= IDLE;
next_cnt <= 0;
end case;
end process proc_op_state;
reg_op_state : process (clk) is
begin
if rising_edge(clk) then
if state = RESET then
op_state <= IDLE;
else
op_state <= next_op_state;
cnt <= next_cnt;
end if;
end if;
end process reg_op_state;
end architecture rtl;
1°) En suivant ce que fait l'enseignant, on vous demande de compiler et d'essayer le module d'Opencores.org
2°) Modifier le texte affiché à votre convenance
Indications :
- chercher les informations sur le code ASCII dans Wikipédia
- le fichier de contraintes vous est fourni :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved CLK,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, LCD_E,Output,PIN_L4,1,B1_N1,3.3-V LVTTL, LCD_ON,Output,PIN_L5,1,B1_N1,3.3-V LVTTL, LCD_RS,Output,PIN_M2,1,B1_N2,3.3-V LVTTL, LCD_RW,Output,PIN_M1,1,B1_N2,3.3-V LVTTL, LCD_DB[7],Output,PIN_M5,1,B1_N2,3.3-V LVTTL, LCD_DB[6],Output,PIN_M3,1,B1_N1,3.3-V LVTTL, LCD_DB[5],Output,PIN_K2,1,B1_N1,3.3-V LVTTL, LCD_DB[4],Output,PIN_K1,1,B1_N1,3.3-V LVTTL,
Exercice 2
[modifier | modifier le wikicode]Notre objectif étant d'afficher des heures minutes, nous allons spécialiser un peu cet afficheur. Dans un premier temps, on le destine à afficher les 4 digits d'un compteur binaire. En clair il doit afficher entre 0000 et FFFF. Mais l'exercice précédent vous a montré que l'afficheur lcd ne connaît que les codes ASCII. Vous aller réaliser un circuit combinatoire qui réalise cela. Comme il nous faut garder le gestionnaire de l'écran lcd, nous allons vous proposer un fichier lcd16x2_ctrl_demo.vhd modifié dans lequel on vous demande de modifier la partie combinatoire en fin de fichier.
-------------------------------------------------------------------------------
-- Title : Synthesizable demo for design "lcd16x2_ctrl"
-- Project :
-------------------------------------------------------------------------------
-- File : lcd16x2_ctrl_tb.vhd
-- Author : <stachelsau@T420>
-- Company :
-- Created : 2012-07-28
-- Last update: 2012-07-29
-- Platform :
-- Standard : VHDL'93/02
-------------------------------------------------------------------------------
-- Description: This demo writes writes a "hello world" to the display and
-- interchanges both lines periodically.
-------------------------------------------------------------------------------
-- Copyright (c) 2012
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2012-07-28 1.0 stachelsau Created
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-------------------------------------------------------------------------------
entity lcd16x2_ctrl_demo is
port (
clk : in std_logic;
lcd_e : out std_logic;
lcd_rs : out std_logic;
lcd_rw : out std_logic;
lcd_db : out std_logic_vector(7 downto 4);
led_out : out std_logic);
end entity lcd16x2_ctrl_demo;
-------------------------------------------------------------------------------
architecture behavior of lcd16x2_ctrl_demo is
component transcod is port(
entree4bits : in std_logic_vector(3 downto 0);
sortie8bits : out std_logic_vector(7 downto 0));
end component transcod;
--
signal timer : natural range 0 to 5000000 := 0;
signal eno10Hz : std_logic := '0';
signal line1 : std_logic_vector(127 downto 0);
signal line2 : std_logic_vector(127 downto 0);
signal s_cmpt16 : std_logic_vector(15 downto 0);
signal s_data32 : std_logic_vector(31 downto 0);
-- component generics
constant CLK_PERIOD_NS : positive := 20; -- 50 Mhz
-- component ports
signal rst : std_logic;
signal line1_buffer : std_logic_vector(127 downto 0);
signal line2_buffer : std_logic_vector(127 downto 0);
begin -- architecture behavior
-- component instantiation
DUT : entity work.lcd16x2_ctrl
generic map (
CLK_PERIOD_NS => CLK_PERIOD_NS)
port map (
clk => clk,
rst => rst,
lcd_e => lcd_e,
lcd_rs => lcd_rs,
lcd_rw => lcd_rw,
lcd_db => lcd_db,
line1_buffer => line1_buffer,
line2_buffer => line2_buffer);
rst <= '0';
-- see the display's datasheet for the character map
line1(127 downto 120) <= X"20";
line1(119 downto 112) <= X"20";
line1(111 downto 104) <= X"20";
line1(103 downto 96) <= X"20";
line1(95 downto 88) <= X"20";
line1(87 downto 80) <= s_data32(31 downto 24);
line1(79 downto 72) <= s_data32(23 downto 16);
line1(71 downto 64) <= s_data32(15 downto 8);
line1(63 downto 56) <= s_data32(7 downto 0);
line1(55 downto 48) <= X"20";
line1(47 downto 40) <= X"20";
line1(39 downto 32) <= X"20";
line1(31 downto 24) <= X"20";
line1(23 downto 16) <= X"20";
line1(15 downto 8) <= X"20";
line1(7 downto 0) <= X"20";
line2(127 downto 120) <= X"20";
line2(119 downto 112) <= X"20";
line2(111 downto 104) <= X"20";
line2(103 downto 96) <= X"20";
line2(95 downto 88) <= X"20";
line2(87 downto 80) <= X"20";
line2(79 downto 72) <= X"20";
line2(71 downto 64) <= X"20";
line2(63 downto 56) <= X"20";
line2(55 downto 48) <= X"20";
line2(47 downto 40) <= X"20";
line2(39 downto 32) <= X"20";
line2(31 downto 24) <= X"20";
line2(23 downto 16) <= X"20";
line2(15 downto 8) <= X"20";
line2(7 downto 0) <= X"20";
line1_buffer <= line1; -- when switch_lines = '1' else line1;
line2_buffer <= line2; -- when switch_lines = '1' else line2;
-- switch lines every second
process(clk)
begin
if rising_edge(clk) then
if timer = 0 then
timer <= 5000000;
else
timer <= timer - 1;
end if;
end if;
end process;
eno10Hz <= '1' when timer = 0 else
'0';
-- compteur 16 bits en binaire
cmpt16:process(clk) begin
if rising_edge(clk) then
if eno10Hz = '1' then
s_cmpt16 <= s_cmpt16 + 1;
end if;
end if;
end process;
-- câblage des 4 transcodeurs
unite:transcod port map(
entree4bits => s_cmpt16(3 downto 0),
sortie8bits => s_data32(7 downto 0));
dizaine:transcod port map(
entree4bits => s_cmpt16(7 downto 4),
sortie8bits => s_data32(15 downto 8));
centaine:transcod port map(
entree4bits => s_cmpt16(11 downto 8),
sortie8bits => s_data32(23 downto 16));
millier:transcod port map(
entree4bits => s_cmpt16(15 downto 12),
sortie8bits => s_data32(31 downto 24));
end architecture behavior;
-- transcodeur combinatoire
library ieee;
use ieee.std_logic_1164.all;
ENTITY transcod is port(
entree4bits : in std_logic_vector(3 downto 0);
sortie8bits : out std_logic_vector(7 downto 0));
end transcod;
architecture arch_transcod of transcod is
begin
--************ A CHANGER CAR TROP NAIF ***************
sortie8bits(3 downto 0) <= entree4bits;
sortie8bits(7 downto 4) <= X"3";
end;
Indications :
- on vous a préparé une horloge à 10 Hz pour le compteur
- L'ensemble donné fonctionne mais pas correctement : il affiche mal les chiffres au-delà de 9 !!!
- la seule chose que vous avez à réaliser est le transcodeur tout en bas du fichier. Vous remplacerez l'équation naïve par un "with select when"
TP2c : réalisation d'un affichage HH:MN
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.
Exercice 1
[modifier | modifier le wikicode]Le problème du transcodeur réalisé dans l'exercice 2 du TP1c peut être facilement résolu si l'on remplace les compteurs hexadécimaux par des compteurs BCD (qui ne comptent que de 0 à 9). C'est ce que fait le code ci-dessous. Vous y remarquerez la disparition du transcodeur de l'exercice 2 tu TP1c. Essayez de comprendre pourquoi.
-------------------------------------------------------------------------------
-- Title : Synthesizable demo for design "lcd16x2_ctrl"
-- Project :
-------------------------------------------------------------------------------
-- File : lcd16x2_ctrl_tb.vhd
-- Author : <stachelsau@T420>
-- Company :
-- Created : 2012-07-28
-- Last update: 2012-07-29
-- Platform :
-- Standard : VHDL'93/02
-------------------------------------------------------------------------------
-- Description: This demo writes writes a "hello world" to the display and
-- interchanges both lines periodically.
-------------------------------------------------------------------------------
-- Copyright (c) 2012
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2012-07-28 1.0 stachelsau Created
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
-------------------------------------------------------------------------------
entity lcd16x2_ctrl_demo is
port (
clk : in std_logic;
lcd_e : out std_logic;
lcd_rs : out std_logic;
lcd_rw : out std_logic;
lcd_db : out std_logic_vector(7 downto 4);
led_out : out std_logic);
end entity lcd16x2_ctrl_demo;
-------------------------------------------------------------------------------
architecture behavior of lcd16x2_ctrl_demo is
component CounterBCD is
port( EN: in std_logic;
Clock: in std_logic;
Reset: in std_logic;
ENO : out std_logic;
Output: out std_logic_vector(3 downto 0));
end component CounterBCD;
--
signal timer : natural range 0 to 5000000 := 0;
signal eno10Hz, s_eno, s_eno2, s_eno3 : std_logic := '0';
signal line1 : std_logic_vector(127 downto 0);
signal line2 : std_logic_vector(127 downto 0);
signal s_data16 : std_logic_vector(15 downto 0);
-- component generics
constant CLK_PERIOD_NS : positive := 20; -- 50 Mhz
-- component ports
signal rst : std_logic;
signal line1_buffer : std_logic_vector(127 downto 0);
signal line2_buffer : std_logic_vector(127 downto 0);
begin -- architecture behavior
-- component instantiation
DUT : entity work.lcd16x2_ctrl
generic map (
CLK_PERIOD_NS => CLK_PERIOD_NS)
port map (
clk => clk,
rst => rst,
lcd_e => lcd_e,
lcd_rs => lcd_rs,
lcd_rw => lcd_rw,
lcd_db => lcd_db,
line1_buffer => line1_buffer,
line2_buffer => line2_buffer);
rst <= '0';
-- see the display's datasheet for the character map
line1(127 downto 120) <= X"20";
line1(119 downto 112) <= X"20";
line1(111 downto 104) <= X"20";
line1(103 downto 96) <= X"20";
line1(95 downto 88) <= X"20";
line1(87 downto 84) <= X"3";
line1(83 downto 80) <= s_data16(15 downto 12);
line1(79 downto 76) <= X"3";
line1(75 downto 72) <= s_data16(11 downto 8);
line1(71 downto 68) <= X"3";
line1(67 downto 64) <= s_data16(7 downto 4);
line1(63 downto 60) <= X"3";
line1(59 downto 56) <= s_data16(3 downto 0);
line1(55 downto 48) <= X"20";
line1(47 downto 40) <= X"20";
line1(39 downto 32) <= X"20";
line1(31 downto 24) <= X"20";
line1(23 downto 16) <= X"20";
line1(15 downto 8) <= X"20";
line1(7 downto 0) <= X"20";
line2(127 downto 120) <= X"20";
line2(119 downto 112) <= X"20";
line2(111 downto 104) <= X"20";
line2(103 downto 96) <= X"20";
line2(95 downto 88) <= X"20";
line2(87 downto 80) <= X"20";
line2(79 downto 72) <= X"20";
line2(71 downto 64) <= X"20";
line2(63 downto 56) <= X"20";
line2(55 downto 48) <= X"20";
line2(47 downto 40) <= X"20";
line2(39 downto 32) <= X"20";
line2(31 downto 24) <= X"20";
line2(23 downto 16) <= X"20";
line2(15 downto 8) <= X"20";
line2(7 downto 0) <= X"20";
line1_buffer <= line1; -- when switch_lines = '1' else line1;
line2_buffer <= line2; -- when switch_lines = '1' else line2;
-- switch lines every second
process(clk)
begin
if rising_edge(clk) then
if timer = 0 then
timer <= 5000000;
else
timer <= timer - 1;
end if;
end if;
end process;
eno10Hz <= '1' when timer = 0 else
'0';
bcdUnit: CounterBCD port map(
EN => eno10Hz,
Clock => clk,
Reset => '0',
ENO => s_eno,
Output => s_data16(3 downto 0));
bcdDiz: CounterBCD port map(
EN => s_eno,
Clock => clk,
Reset => '0',
ENO => s_eno2,
Output => s_data16(7 downto 4));
bcdCent: CounterBCD port map(
EN => s_eno2,
Clock => clk,
Reset => '0',
ENO => s_eno3,
Output => s_data16(11 downto 8));
bcdMil: CounterBCD port map(
EN => s_eno3,
Clock => clk,
Reset => '0',
ENO => open,
Output => s_data16(15 downto 12));
end architecture behavior;
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity CounterBCD is
port( EN: in std_logic;
Clock: in std_logic;
Reset: in std_logic;
ENO : out std_logic;
Output: out std_logic_vector(3 downto 0));
end CounterBCD;
architecture Behavioral of CounterBCD is
signal cmpt: std_logic_vector(3 downto 0);
signal s_en_cmpt: std_logic_vector(4 downto 0);
begin process(Clock,Reset)
begin
if(rising_edge(Clock)) then
if Reset='1' then
cmpt <= "0000";
elsif EN='1' then
if cmpt="1001" then
cmpt<="0000";
else
cmpt <= cmpt + 1;
end if;
end if;
end if;
end process;
Output <= cmpt;
s_en_cmpt <= en & cmpt;
with s_en_cmpt select
ENO <= '1' when "11001",
'0' when others;
end Behavioral;
Dans cet exercice, on vous demande simplement d'insérer un ":" entre les deux premiers digits et les deux derniers. On vous rappelle en effet que l'on finira par afficher les heures et minutes (qui en général sont séparées par ce ":") d'où le titre de ce TP.
Exercice 2 : Compteur modulo6
[modifier | modifier le wikicode]Pour compter les dizaines des minutes, il nous faut un compteur modulo 6 qui soit cascadable. Modifier le compteur BCD donné pour qu'il compte de 0 à 5 et génère un signal ENO quand il est à 5 et quand son entrée EN est à 1.
Indication : le compteur BCD à modifier est donné ci-dessous :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity CounterBCD is
port( EN: in std_logic;
Clock: in std_logic;
Reset: in std_logic;
ENO : out std_logic;
Output: out std_logic_vector(3 downto 0));
end CounterBCD;
architecture Behavioral of CounterBCD is
signal cmpt: std_logic_vector(3 downto 0);
signal s_en_cmpt: std_logic_vector(4 downto 0);
begin process(Clock,Reset)
begin
if(rising_edge(Clock)) then
if Reset='1' then
cmpt <= "0000";
elsif EN='1' then
if cmpt="1001" then
cmpt<="0000";
else
cmpt <= cmpt + 1;
end if;
end if;
end if;
end process;
Output <= cmpt;
s_en_cmpt <= en & cmpt;
with s_en_cmpt select
ENO <= '1' when "11001",
'0' when others;
end Behavioral;
Vous remplacerez le compteur des dizaines des minutes par ce nouveau compteur et en ferez un test. Restera donc la réalisation correcte des heures.
Exercice 3 : Compteur des heures
[modifier | modifier le wikicode]Nous allons ajouter un comptage correct des heures au compteur de l'exercice 2 qui compte normalement la partie minutes.
Le comptage des heures est lui aussi très particulier. Les unités nécessitent un compteur décimal mais qui doit être interrompu lorsque les dizaines passent à 2 ! On ne doit pas alors dépasser 3. Nous allons utiliser un montage bouclé pour réaliser cela : lorsque l'on est à 23, un EN='1' fait repasser les heures à 0. Ce n'est pas la peine de le faire pour les minutes, cela sera automatique.
Indication : la gestion du bouclage peut être réalisé par quelque chose du genre :
HeurUnit: CounterBCD port map(
EN => s_eno2,
Clock => clk,
Reset => s_reset,
ENO => s_eno3,
Output => s_data16(11 downto 8));
HeurDiz: CounterBCD port map(
EN => s_eno3,
Clock => clk,
Reset => s_reset,
ENO => open,
Output => s_data16(15 downto 12));
-- reset quand 23 est detecte par rebouclage
s_eno2_heure <= s_eno2 & s_data16(15 downto 8);
with s_eno2_heure select
s_reset <= '1' when "100100011",
'0' when others;
Faites un schéma pour comprendre, avant d'en regarder la solution.
D'abord la correction du schéma :

Le multiplexeur n'existe pas dans ce que nous allons réaliser.
Nous mettons ici un lien vers les corrigés qui se trouvent sur le WIKI de l'IUT de Troyes. Comme ces TPs sont effectivement réalisés par les étudiants, les solutions en question ne sont accessibles qu'en accès réservé sur une partie de l'année. Essayez le lien, il est librement accessible une quarantaine de semaines dans l'année voire plus si le TP est abandonné :
TP3c : réalisation d'une sonnerie
[modifier | modifier le wikicode]
Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.
Quand l'heure courante est égale à l'heure de réveil et que le réveil est armé il faut que le réveil sonne. Ceci doit être géré par une machine séquentielle que l'on cherche à réaliser.
Le graphe d'évolution du réveil qui sonne est présenté ci-contre. La réalisation de ce graphe d'évolution nécessite la réalisation de ses deux entrées :
- Key : sera simplement une entrée (un switch). elle sert pour l'armement de la sonnerie du réveil.
- Trip : détecte que l'heure de sonnerie est égale à l'heure courante. L'heure courante est naturellement réalisé par le travail du TP2. L'heure de réveil à ce stade sera réalisée par 16 interrupteurs
La sortie sonnerie sera matérialisée par une LED. Une autre LED peut être utilisée pour matérialisée l'armement du réveil
Exercice 1 : Un mécanisme de sonnerie pour sonnerie de réveil
[modifier | modifier le wikicode]Vous allez partir de la correction de l'exercice 2 du TP2c disponible ICI
![]() |
Il n'y aura aucune interaction entre l'heure courante et l'heure réveil (qui n'existe pas à ce stade). On rajoute simplement l'automatisme de sonnerie. Ceci permet simplement un test de l'automatisme de sonnerie. |
Ajoutez deux entrées (ArmKey et Trip) et une sortie "led_out" pour tester votre machine d'états. L'entrée "ena" du mécanisme de sera reliée à la sortie du timer "eno10Hz" du module "lcd16x2_ctrl_demo.vhd". Le fichier de contrainte sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved CLK,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, LCD_E,Output,PIN_L4,1,B1_N1,3.3-V LVTTL, LCD_ON,Output,PIN_L5,1,B1_N1,3.3-V LVTTL, LCD_RS,Output,PIN_M2,1,B1_N2,3.3-V LVTTL, LCD_RW,Output,PIN_M1,1,B1_N2,3.3-V LVTTL, LCD_DB[7],Output,PIN_M5,1,B1_N2,3.3-V LVTTL, LCD_DB[6],Output,PIN_M3,1,B1_N1,3.3-V LVTTL, LCD_DB[5],Output,PIN_K2,1,B1_N1,3.3-V LVTTL, LCD_DB[4],Output,PIN_K1,1,B1_N1,3.3-V LVTTL, led_out,Output,PIN_G19,7,B7_N2,2.5 V, Armkey,Input,PIN_AC28,5,B5_N2,2.5 V, Trip,Input,PIN_AB28,5,B5_N1,2.5 V,
avec Trip en sw0 et ArmKey en sw1.
Le graphe d'évolution sera réalisé avec un case when et sans initialisation.
Indication 1 : On rappelle encore une fois que le style « case when » permet de ne pas chercher les équations de récurrences. Mais comme nos diagrammes d'évolutions se sont compliqués (par l'ajout d'étiquettes sur les transitions), il nous faudra ajouter des "if then". Cela est tellement intuitif que nous passons directement aux exemples. Nous commençons par présenter partiellement le réveil.
Indication 2 : Programmation sans initialisation : le principe consiste à déclarer d'abord un type énuméré avec une définition symbolique de chacun des états (ici Armed, Off, Ringing) :
TYPE typetat IS (Armed, Off, Ringing); -- dans architecture
SIGNAL etat : typetat;
Ensuite dans un « case when » on détaillera toutes les transitions possibles comme montré ci-dessous dans le cas où l'on ne s'intéresse pas à une initialisation :
-- sans initialisation
BEGIN
PROCESS (clock) BEGIN
IF clock'EVENT AND clock='1' THEN
CASE etat IS
WHEN Off => IF key ='1' THEN etat <= Armed;
ELSE etat <= Off;
END IF;
....
END CASE;
END IF;
END PROCESS;
....
L'idée générale est donc d'utiliser un « case » sur les états avec des « if » pour gérer l'ensemble des transitions.
Indication 3 : Il serait bon d'ajouter au réveil une entrée ENA qui serait une division de l'horloge par 2**20. Ceci pour éviter les rebonds des interrupteurs.
Indication 4 : le réveil est abordé dans un autre livre sous toutes les formes et il est même possible d'y trouver une solution sans initialisation et avec l'entrée "ena".
Exercice 2 : Un réveil qui sonne (suite)
[modifier | modifier le wikicode]Compléter l'exercice 1 pour utiliser le séquenceur de la sonnerie mais avec Trip réalisé avec l'heure courante qui défile. Par contre l'heure de sonnerie sera réalisée par des interrupteurs. Il serait bon aussi d'afficher correctement l'heure de réveil sur la deuxième ligne, en supposant qu'elle soit décimale.
Indications :
- Vous partirez de la correction : Corrigé du TP2c
- l'entité du fichier lcd16x_ctrl_demo.vhd devra donc évoluer maintenant :
entity lcd16x2_ctrl_demo is
port (
clk : in std_logic;
Armkey : in std_logic;
HHMMAlarm : in std_logic_vector(15 downto 0);
lcd_e : out std_logic;
lcd_rs : out std_logic;
lcd_rw : out std_logic;
lcd_db : out std_logic_vector(7 downto 4);
led_out : out std_logic);
end entity lcd16x2_ctrl_demo;
- vous remarquez l'apparition de l'entrée HHMMAlarm sur 16 bits. Elle sera donc reliée aux interrupteurs de la carte.
- l'entrée Trip a disparu. Elle doit être remplacée par un signal, par exemple "egalite" pour coller à l'égalité de l'heure courante et de l'heure de l'alarme.
- le fichier de contraintes peut être :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved CLK,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, LCD_DB[7],Output,PIN_M5,1,B1_N2,3.3-V LVTTL, LCD_DB[6],Output,PIN_M3,1,B1_N1,3.3-V LVTTL, LCD_DB[5],Output,PIN_K2,1,B1_N1,3.3-V LVTTL, LCD_DB[4],Output,PIN_K1,1,B1_N1,3.3-V LVTTL, LCD_E,Output,PIN_L4,1,B1_N1,3.3-V LVTTL, LCD_ON,Output,PIN_L5,1,B1_N1,3.3-V LVTTL, LCD_RS,Output,PIN_M2,1,B1_N2,3.3-V LVTTL, LCD_RW,Output,PIN_M1,1,B1_N2,3.3-V LVTTL, Armkey,Input,PIN_Y23,5,B5_N2,2.5 V, SW[16],Input,PIN_Y24,5,B5_N2,2.5 V, HHMMAlarm[15],Input,PIN_AA22,5,B5_N2,2.5 V, HHMMAlarm[14],Input,PIN_AA23,5,B5_N2,2.5 V, HHMMAlarm[13],Input,PIN_AA24,5,B5_N2,2.5 V, HHMMAlarm[12],Input,PIN_AB23,5,B5_N2,2.5 V, HHMMAlarm[11],Input,PIN_AB24,5,B5_N2,2.5 V, HHMMAlarm[10],Input,PIN_AC24,5,B5_N2,2.5 V, HHMMAlarm[9],Input,PIN_AB25,5,B5_N1,2.5 V, HHMMAlarm[8],Input,PIN_AC25,5,B5_N2,2.5 V, HHMMAlarm[7],Input,PIN_AB26,5,B5_N1,2.5 V, HHMMAlarm[6],Input,PIN_AD26,5,B5_N2,2.5 V, HHMMAlarm[5],Input,PIN_AC26,5,B5_N2,2.5 V, HHMMAlarm[4],Input,PIN_AB27,5,B5_N1,2.5 V, HHMMAlarm[3],Input,PIN_AD27,5,B5_N2,2.5 V, HHMMAlarm[2],Input,PIN_AC27,5,B5_N2,2.5 V, HHMMAlarm[1],Input,PIN_AC28,5,B5_N2,2.5 V, HHMMAlarm[0],Input,PIN_AB28,5,B5_N1,2.5 V, led_out,Output,PIN_G19,7,B7_N2,2.5 V,
- le code VHDL pour réaliser un test d'égalité peut être :
egalite <= '1' when HHMMAlarm = s_data16 else
'0';
Nous mettons ici un lien vers les corrigés qui se trouvent sur le WIKI de l'IUT de Troyes. Comme ces TPs sont effectivement réalisés par les étudiants, les solutions en question ne sont accessibles qu'en accès réservé sur une partie de l'année. Essayez le lien, il est librement accessible une quarantaine de semaines dans l'année voir plus si le TP est abandonné :
TP4c : Un processeur seul
[modifier | modifier le wikicode]Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.

Indications :
- Pour la mise à jour du contenu de la mémoire seulement, sans passer par un temps de recompilation complet de l’application (donc plus ou moins équivalent à data2mem), il faut valider l'option "use smart compilation". Pour y accéder : menu assignements => settings => compilation process settings => puis cocher l'option. Ne pas oublier de faire "apply" avant de quitter la fenêtre !
- La figure représente schématiquement la ressource que vous avez à disposition. Elle utilise directement l'afficheur lcd comme en TP3c. Pour être précis, l'affichage ne se fera pas exactement au même endroit qu'en TP3c ! L'heure courante du TP3c est gardée en matériel et le processeur n'agit que sur l'heure réveil.
Voir le TP4 de ce chapitre pour la présentation.
Évidemment, la ressource donnée n'est pas identique à celle du TP4 puisque nous avons besoin de 4 digits d'affichage et en plus l'affichage se fait sur lcd. Nous mettons la ressource complète sur notre site : TinyReveilLCDStart.zip. Cette ressource est destinée uniquement aux circuits Altera. Pour une version Xilinx, cherchez dans le chapitre précédent.

Votre objectif sera de prendre contact avec un processeur qui est câblé comme dans la figure. Votre travail consistera à :
- comprendre l'organisation de la ressource avec le sous-répertoire soft qui est absolument obligatoire
- comprendre comment compiler un programme. Un script est disponible pour cela "compile_c.sh". il compile un programme qui a pour nom "test_tiny.c" pour en faire un fichier "test_tiny.mif"
- si vous êtes curieux, ouvrez le fichier "pm.vhd" et vous verrez que le contenu de la mémoire est donné par "test_tiny.mif"
- essayer de comprendre le rapport entre ce que vous voyez, sur les leds et le lcd, et le contenu du programme
- réaliser quelques chenillards
- réaliser un compteur binaire et afficher... Encore le problème des codes ASCII à résoudre. Comment le faire ?
- réaliser un comptage BCD sur 4 digits, en utilisant les sous-programmes du TP4, puis
- réaliser un comptage HH:MM (Pour la réalisation de l'arithmétique particulière des heures minutes :
void incrementHHMM(uint16_t *hh_mm) {
(*hh_mm)++;
if ((*hh_mm & 0x000F) > 0x0009)
*hh_mm += 0x0006;
if ((*hh_mm & 0x00F0) > 0x0050)
*hh_mm += 0x00A0;
if ((*hh_mm & 0x0F00) > 0x0900)
*hh_mm += 0x0600;
if ((*hh_mm & 0xFF00) > 0x2300)
*hh_mm = 0x0000;
}
void decrementHHMM(uint16_t *hh_mm) {
(*hh_mm)--;
if ((*hh_mm & 0x000F) == 0x000F)
*hh_mm -= 0x0006;
if ((*hh_mm & 0x00F0) == 0x00F0)
*hh_mm -= 0x00A0;
if ((*hh_mm & 0x0F00) == 0x0F00)
*hh_mm -= 0x0600;
if ((*hh_mm & 0xFFFF) > 0x2359)
*hh_mm = 0x2359;
}
)
- lancer un Terminal (qui restera ouvert)
- aller dans soft qui doit ABSOLUMENT être un sous-répertoire du répertoire du PROJET
- lancer compile_c.sh (avec la commande ./compile_c.sh)
- retourner dans Quartus et compiler (en 2019 la smart compilation met 7s contre 1mn45 sur Intel Core i5)
- utiliser le programmateur pour télécharger dans le FPGA
TP5c : Économiser deux PORTs
[modifier | modifier le wikicode]
La figure du TP4c montrait l'utilisation d'un PORT par digit affiché sur l'écran lcd. Ainsi, il est possible d'afficher n'importe quoi sur les 4 digits à disposition.
Cependant, cela ne nous sert pas puisque nous avons l'intention de n'y afficher que des chiffres de 0 à 9.
Exercice
[modifier | modifier le wikicode]1°) On vous demande donc de faire la gestion des affichages des nombres de 0 à 9 sur 4 digits avec deux PORTs seulement et, malgré tout, sans utiliser de transcodeur.
2°) Réaliser ensuite la gestion des heures minutes sur ces afficheurs. On vous donne un petit bout de programme en C capable de réaliser l'arithmétique spéciale des Heures minutes. Il n'y a plus qu'à sortir sur les afficheurs.
#include <avr/io.h>
#include <avr/interrupt.h>
#undef F_CPU
#define F_CPU 15000000UL
#include "util/delay.h"
int main(int argc, char * argv[])
{ uint16_t hh_mm = 0; // pour les heures et minutes
for (;;) {
hh_mm++;
if ((hh_mm & 0x000F) > 0x0009)
hh_mm += 0x0006;
if ((hh_mm & 0x00F0) > 0x0050)
hh_mm += 0x00A0;
if ((hh_mm & 0x0F00) > 0x0900)
hh_mm += 0x0600;
if ((hh_mm & 0xFF00) > 0x2300)
hh_mm = 0x0000;
_delay_ms(300);
}
}
3°) Sur le même principe, on vous demande de réaliser une décrémentation heures/minutes.
Nous mettons ici un lien vers les corrigés qui se trouvent sur le WIKI de l'IUT de Troyes. Comme ces TPs sont effectivement réalisés par les étudiants, les solutions en question ne sont accessibles qu'en accès réservé sur une partie de l'année. Essayez le lien, il est librement accessible une quarantaine de semaines dans l'année voir plus si le TP est abandonné :
Voici le dode d'incrémentation et décrémentation d'un réveil HH:MM :
void incrementHHMM(uint16_t *hh_mm) {
(*hh_mm)++;
if ((*hh_mm & 0x000F) > 0x0009)
*hh_mm += 0x0006;
if ((*hh_mm & 0x00F0) > 0x0050)
*hh_mm += 0x00A0;
if ((*hh_mm & 0x0F00) > 0x0900)
*hh_mm += 0x0600;
if ((*hh_mm & 0xFF00) > 0x2300)
*hh_mm = 0x0000;
}
void decrementHHMM(uint16_t *hh_mm) {
(*hh_mm)--;
if ((*hh_mm & 0x000F) == 0x000F)
*hh_mm -= 0x0006;
if ((*hh_mm & 0x00F0) == 0x00F0)
*hh_mm -= 0x00A0;
if ((*hh_mm & 0x0F00) == 0x0F00)
*hh_mm -= 0x0600;
if ((*hh_mm & 0xFFFF) > 0x2359)
*hh_mm = 0x2359;
}
Voir aussi
[modifier | modifier le wikicode]- un comptage HH:MM (Voir aussi cet autre chapitre pour des explications sur l'arithmétique particulière des heures minutes)
TP6c : Réalisation du réglage de l'heure de réveil par le processeur
[modifier | modifier le wikicode]Le travail du TP5c n'a pas modifié fondamentalement les responsabilités du processeur :
- d'un côté le processeur gère complètement une heure réveil avec deux PORTs maintenant, c'est-à-dire 16 bits
- de l'autre l'heure courante défile sur l'afficheur.
Exercice 1
[modifier | modifier le wikicode]
Comme le processeur n'est pas responsable de l'heure courante, vous devez lui donner la possibilité de lire cette heure sur deux PORTs.
Indications :
- vous devez naturellement modifier l'entité de "lcd16x2_ctrl_demo.vhd" comme :
entity lcd16x2_ctrl_demo is
port (
clk : in std_logic;
Armkey : in std_logic;
HHMMAlarm : in std_logic_vector(15 downto 0);
HHMMCourante : out std_logic_vector(15 downto 0); -- ajouté pour besoins de cet exercice
lcd_e : out std_logic;
lcd_rs : out std_logic;
lcd_rw : out std_logic;
lcd_db : out std_logic_vector(7 downto 4);
led_out : out std_logic);
end entity lcd16x2_ctrl_demo;
- il n'est pas difficile de trouver un signal 16 bits à l'intérieur de l'architecture qui contient cette information et de la sortir
- chercher le process de lecture et modifier le en conséquence.
Exercice 2
[modifier | modifier le wikicode]À partir du code C donné au TP précédent, on vous demande d'imaginer comment se passe la décrémentation de HH:MN ?
Modifier le programme pour qu'un interrupteur au choix incrémente l'heure de réveil et qu'un autre interrupteur la décrémente.
Exercice 3
[modifier | modifier le wikicode]Réaliser un programme qui gère complètement le réveil, c'est-à-dire que celui-ci sonne lorsqu'il est armé et que l'heure courante est égale à l'heure de réveil.
Nous mettons ici un lien vers les corrigés qui se trouvent sur le WIKI de l'IUT de Troyes. Comme ces TPs sont effectivement réalisés par les étudiants, les solutions en question ne sont accessibles qu'en accès réservé sur une partie de l'année. Essayez le lien, il est librement accessible une quarantaine de semaines dans l'année voir plus si le TP est abandonné :
Exercice 4
[modifier | modifier le wikicode]Pour cet exercice, vous pouvez soit partir de la ressource initiale du TP4c (TinyReveilLCDStart.zip) ou de cette ressource avec le Corrigé du TP5c. Votre choix déterminera simplement votre façon d'afficher l'heure de réveil par le nombre de ports utilisés : 4 dans la première version et deux dans la deuxième.
Nous n'avons jamais retiré l'automate qui gère la sonnerie du réveil du fichier lcd16x2_ctrl_demo.vhd. Pouvez-vous imaginer une architecture matérielle capable de gérer collectivement :
- heure courante comme tout au long de ce TP, réalisée en externe : ce n'est pas le processeur qui s'en occupe.
- heure alarme réglée par le processeur en incrémentation et décrémentation : un bouton pour incrémenter, un bouton pour décrémenter.
- bouton ArmKey géré par le processeur mais passé à l'automate. L'automate de sonnerie s'appelle SequSonnerie et si vous regardez son câblage dans lcd16x2_ctrl_demo vous verrez qu'il est relié à 0.
- sonnerie gérée par l'automate. Vous devez la sortir sur une led.
ANNEXE I : Comment utiliser l'ATTiny861 avec Altera
[modifier | modifier le wikicode]Nous avons réalisé une version pour Altera de ce processeur. Comme nous nous y attendions le seul écueil pour le portage est la mémoire programme.
Comme déjà évoqué, nous mettons la ressource sur notre site : M4209TinyStart.zip. Cela représente l’ensemble des fichiers nécessaires à la réalisation sous Linux de tous les TPs de ce chapitre. Nous mettrons à jour au plus vite pour Windows.
L'organisation des répertoires doit être la suivante :
- dans le répertoire du projet mettre les fichiers vhd et cvs
- dans un sous répertoire soft mettre le script de compilation, et le programme .c à compiler ainsi que mifwrite.cpp et sa compilation.
Le programme micrcontroleur.vhd
[modifier | modifier le wikicode]Il est possible de prendre la version donnée plus haut. Altera semble utiliser le nom de l'entité top pour se faire une idée de la hiérarchie (ce n'est peut être qu'une option qui peut être retirée). Il vous faudra donc éventuellement changer le nom de l'entité. Mais si vous êtes arrivé ici jusqu'ici dans ce livre, cela ne doit être qu'une promenade de santé.
Version de pm.vhd
[modifier | modifier le wikicode]Comme déjà évoqué, la gestion de mémoire est très différente chez Altera de chez Xilinx. Voici donc une version de la mémoire programme. Elle utilise la librairie LPM plutôt que le MegaWizzard IP proposé par Quartus.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
-- for memory
LIBRARY lpm;
USE lpm.lpm_components.ALL;
LIBRARY altera_mf;
USE altera_mf.altera_mf_components.all;
entity pm is
Port (
clk : in std_logic;
Rst : in std_logic;
PM_A : in std_logic_vector(15 downto 0);
PM_Drd : out std_logic_vector(15 downto 0));
end pm;
architecture Arch of pm is
begin
progMemory: lpm_rom GENERIC MAP (
lpm_widthad => 12,
lpm_outdata => "REGISTERED",
--
lpm_address_control => "UNREGISTERED",
lpm_file => "./soft/chenillar.mif",-- fill ram with content of file program.mif
lpm_width => 16)
PORT MAP (
address => PM_A(11 downto 0), --8
memenab => '1',
-- inclock => Clk,
outclock => clk,
q => PM_Drd);
end Arch;
On voit qu'elle est associée à un fichier "soft/chenillar.mif". A priori un fichier .HEX peut faire aussi l'affaire.
Pour obtenir un fichier .mif, nous avons utilisé l'utilitaire trouvé sur Internet :
// this prog is taken from http://www.johnloomis.org/ece595c/notes/isa/mifwrite.html
// and slightly changed to satisfy quartus6.1 *.mif eating engine.
//
// it takes binary file of arbitrary length and treating it as collection of 16-bit words,
// writes *.mif file for quartus
//
//
#include <stdio.h>
#include <stdlib.h>
void mifwrite(FILE *in, FILE *out, int offset);
int main(int argc, char *argv[])
{
char *filename;
if (argc<3) {
printf("usage: mifwrite input_file output_file [offset]\n");
printf("The default offset is zero");
return -1;
}
FILE *in, *out;
filename = argv[1];
in = fopen(filename,"rb");
if (!in) {
printf("file: %s not found\n",filename);
return -1;
}
filename = argv[2];
out = fopen(filename,"wt");
if (!out) {
printf("file: %s not opened\n",filename);
return -1;
}
int offset = 0;
if (argc>3) sscanf(argv[3],"%x",&offset);
if (offset) printf("address_offset %x\n",offset);
mifwrite(in,out,offset);
return 0;
}
void mifwrite(FILE *in, FILE *out,int offset)
{
int count;
unsigned int data;
unsigned int address = 0;
int ndepth;
int nwidth = 16;
fseek(in,0,SEEK_END);
ndepth = ftell(in)/2;
fseek(in,0,SEEK_SET);
fprintf(out,"DEPTH = %d;\n",ndepth);
fprintf(out,"WIDTH = %d;\n\n",nwidth);
fprintf(out,"ADDRESS_RADIX = HEX;\n");
fprintf(out,"DATA_RADIX = HEX;\n");
fprintf(out,"CONTENT\n BEGIN\n");
fprintf(out,"[0..%x] : 0;\n",ndepth-1);
address = 0;
offset = offset>>2;
data=0;
while (count = fread(&data,2,1,in)) {
if (address<offset) {
offset--;
continue;
}
fprintf(out,"%04x : %04x;\n",address,data);
address++;
// if (address>=ndepth) break;
}
fprintf(out,"END;\n");
fclose(in);
fclose(out);
}
qui doit être compilé.
Script de compilation
[modifier | modifier le wikicode]Le script donné ci-dessous est fonctionnel.
#!/bin/bash
avr-gcc -g -mmcu=attiny861 -Wall -Os -c chenillar.c
avr-gcc -g -mmcu=attiny861 -o chenillar.elf -Wl,-Map,chenillar.map chenillar.o
avr-objdump -h -S chenillar.elf > chenillar.lss
#avr-objcopy -R .eeprom -O ihex chenillar.elf chenillar.hex
avr-objcopy -O binary -R .eeprom chenillar.elf chenillar.bin
./mifwrite chenillar.bin chenillar.mif
Pour la mise à jour du contenu de la mémoire seulement, sans passer par un temps de recompilation complet de l’application (donc plus ou moins équivalent à data2mem), il faut valider l'option "use smart compilation". Pour y accéder : menu assignements => settings => compilation process settings => puis cocher l'option . ne pas oublier de faire "apply" avant de quitter la fenêtre !
À partir de là une seule compilation fera le travail. Un peu plus d'une minute de compilation. C'est quand même bien plus long que data2mem qui ne prend qu'une dizaine de secondes !
Script d'assemblage
[modifier | modifier le wikicode]Il est en cours de développement. Seule la partie assemblage a été testée mais pas les deux dernières lignes qui sont sensée automatiser la mise en mémoire du programme. Nous avons eu des essais non concluants avec ces deux lignes et les laissons donc en commentaire.
#!/bin/bash
avr-gcc -mmcu=attiny861 chenillar.S -o chenillar.elf
avr-objdump -h -S chenillar.elf > chenillar.lss
#avr-objcopy -R .eeprom -O ihex chenillar.elf chenillar.hex
avr-objcopy -O binary -R .eeprom chenillar.elf chenillar.bin
./mifwrite chenillar.bin chenillar.mif
#!!! par defaut est installé dans /opt/altera mais ici dans /opt/Altera
#/opt/Altera/15.0/quartus/bin/quartus_cdb Tiny861 -c Tiny861 --update_mif
#/opt/Altera/15.0/quartus/bin/quartus_cdb --update_mif Tiny861
#/opt/Altera/15.0/quartus/bin/quartus_asm Tiny861
Un petit programme d'essai en assembleur pourrait être :
;********* chenillar.S ****************
#define __SFR_OFFSET 0 //obligatoire pour fonctionnement correct de "out" et "in"
.nolist
#include <avr/io.h>
.list
.section .text ; denotes code section
.global main
main:
LDI R16,1 ; setup
loop:
LSL R16 ; declage perpetuelle
brne suite
LDI R16,1 ; setup
suite:
OUT PORTA, R16 ; result to port A
rcall delay
RJMP loop
delay:
ldi r23, 0x40 ; 0x80 sur Nexys3 mais probablement 0x40 Ã l'UTT
delayloop_ext:
ldi r24, 0xff ; load 0xffff to r24:25
ldi r25, 0xff
delayloop:
sbiw r24,1 ; decrement r24:25
brne delayloop ; branch if not 0
dec r23
brne delayloop_ext
ret
.END
ANNEXE II
[modifier | modifier le wikicode]Nous avions pour habitude d’utiliser les primitives d'instanciation des LUTs avec les outils Xilinx. Nous avons donc chercher comment faire avec les outils Altera et, à notre grande surprise, avons découvert que la primitive correspondante n'existe pas. Bien sûr les FPGAs Altera sont aussi bâtis autour des LUT (LUT4, LUT5, LUT6) mais leurs utilisation est un peu plus complexe. Pourquoi ? Parce que les seules primitives disponibles sont "lut_input" et "lut_output".
Pour rendre l’utilisation plus compréhensibles pour les habitués, nous avons procédé comme ceci.
1 - Création d'un composant LUT4
[modifier | modifier le wikicode]Voici le composant correspondant :
library ieee;
use ieee.std_logic_1164.all;
library altera;
use altera.altera_primitives_components.all;
entity LUT4 is
generic(mask : std_logic_vector(15 downto 0):=X"0000");
port (
in4 : in std_logic_vector(3 downto 0);
out1 : out std_logic);
end LUT4;
architecture arch_lut4 of LUT4 is
signal s_lut : std_logic_vector(3 downto 0);
signal s_out : std_logic;
begin
ic1:lut_input port map(a_in => in4(0),a_out=>s_lut(0));
ic2:lut_input port map(a_in => in4(1),a_out=>s_lut(1));
ic3:lut_input port map(a_in => in4(2),a_out=>s_lut(2));
ic4:lut_input port map(a_in => in4(3),a_out=>s_lut(3));
with s_lut select
s_out <= mask(0) when "0000",
mask(1) when "0001",
mask(2) when "0010",
mask(3) when "0011",
mask(4) when "0100",
mask(5) when "0101",
mask(6) when "0110",
mask(7) when "0111",
mask(8) when "1000",
mask(9) when "1001",
mask(10) when "1010",
mask(11) when "1011",
mask(12) when "1100",
mask(13) when "1101",
mask(14) when "1110",
mask(15) when "1111";
ic5: lut_output port map(a_in=>s_out,a_out=>out1);
end arch_lut4;
Cela peut surprendre mais les seules primitives Altera sont "lut_input" et "lut_output", d'où le code ci-dessus.
2 - Instanciation
[modifier | modifier le wikicode]Comme d'habitude :
-- declaration avant begin architecture
component LUT4 is
generic(mask : std_logic_vector(15 downto 0):=X"0000");
port (
in4 : in std_logic_vector(3 downto 0);
out1 : out std_logic);
end component LUT4;
-- puis après begin :
mylut:LUT4 generic map(mask => X"FFFE")
port map (in4 => compteur(14 downto 11),
OUT1 => led);
Chaque connaisseur aura reconnu la réalisation d'un OU à quatre entrées.
Voir aussi
[modifier | modifier le wikicode]