Very High Speed Integrated Circuit Hardware Description Language/Travail pratique/Projets pour ATMEL ATTiny861
L'ATTiny 861 est une architecture simplifiée par rapport à l'ATMega8. Dans ce chapitre, nous allons donc utiliser cette nouvelle architecture. Ce TP cible essentiellement les FPGA de chez Xilinx. Mais le TP suivant est dédié aux FPGA de chez Altera.
Architecture
[modifier | modifier le wikicode]Architecture des registres mémoires
[modifier | modifier le wikicode]Il existe 3 espaces distincts en mémoire :
- Les 32 premières adresses correspondent aux 32 registres. Ces registres sont directement reliés à l'ALU et tous les calculs ne peuvent être effectués qu’à partir de ces registres (addition, soustraction, multiplication, opérations logiques, tests ).
- L'espace IO de l'adresse 0x20 à 0x5F. Dans cet espace se trouvent beaucoup de registres de gestion du matériel (les ports, Timers, UART,...). Pour accéder à ces registres les instructions in et out utilisent l'adresse 0 pour le premier registre et non l'adresse 0x20. Les instructions cli et sbi permettent de mettre à 0 ou à 1 un bit d'un registre
- Enfin à partir de l'adresse 0x60 se trouve la mémoire à proprement parler (le Tiny861 possède 512 octets de RAM). Pour accéder à tout l'espace mémoire (Registre et zone I/O incluse) les instructions ST,LD et STS et LDS peuvent être utilisées. L'adressage peut être direct (STS ou LDS) ou indirect (ST et LD). Un adressage indirect utilise les registres X, Y ou Z (respectivement les registres R27:R26, R29:R28 et R31:R30) pour pointer sur une zone mémoire.
Quelques registres importants du banc de registres
[modifier | modifier le wikicode]Ce tableau (partiel) des registres respecte les fichiers d'inclusion du compilateur avr-gcc. Par exemple, dans la documentation officielle, le bit b0 du PORTB s’appelle PORTB0 tandis que dans le fichier d'inclusion du GNU-C il s’appelle PB0. Comme vous pouvez le constater, chaque registre est caractérisé par deux adresses. Chacune des deux adresses est espacée de 0x20=32. C'est le nombre de registres à usage général. Notez que le fichier d'inclusion avr/io.h que l’on utilisera très vite utilise les nombres qui ne sont pas entre parenthèses :
/* Port B */
#define PINB _SFR_IO8(0x16)
#define DDRB _SFR_IO8(0x17)
#define PORTB _SFR_IO8(0x18)
Comment mettre un programme en ROM/RAM
[modifier | modifier le wikicode]Commençons par nous poser la question de savoir comment on peut compiler un programme en langage assembleur ? Le lien suivant décrit plus en détail les problèmes et les solutions : Transformer HEX en VHDL
L'assemblage
[modifier | modifier le wikicode]L'assemblage se fait en ligne de commande. Son objectif est de réaliser un fichier au format elf. Voici un exemple de commande :
avr-gcc -mmcu=attiny861 exo1.S -o exo1.elf
Nous avons décidé d’utiliser « avr-gcc » pour assembler (au lieu de avr-as). Cette décision a des conséquences importantes :
- Utilisation du pré-processeur qui permet d’utiliser les noms des registres mais aussi les #include traditionnels en langage C
- La table des vecteurs d'interruptions est automatiquement créée même si le RESET seul est utilisé
- Le code doit se situer dans un main
Programmation de la mémoire programme
[modifier | modifier le wikicode]La programmation de la mémoire programme peut être réalisée de plusieurs manières. Si la manière la plus simple reste la transformation du programme en fichier VHDL, nous n'utiliserons pas cette méthode bien trop lente : elle nécessite une recompilation globale du projet, ce qui peut prendre quelques minutes. Nous allons plutôt utiliser un utilitaire data2mem comme indiqué dans la figure ci-après.
La commande qui fait cela est :
data2mem -bm memory.bmm -bd demo.elf -bt microcontroleur.bit -o uh microcontroleur
qui construit à partir du fichier microcontroleur.bit (résultat de la compilation de l'ISE) un ficher microcontroleur_rp.bit : c’est ce deuxième fichier que l’on va envoyer dans le FPGA. Cette commande nécessite un fichier memory.bmm qui sera fourni et d'ajouter quelques contraintes importantes dans le fichier ucf.
Pour la carte Spartan3E
[modifier | modifier le wikicode]Les contraintes dans le fichier ucf peuvent être :
INST prgmem/pe_0 LOC = RAMB16_X1Y5 ; INST prgmem/pe_1 LOC = RAMB16_X1Y6 ; INST prgmem/pe_2 LOC = RAMB16_X1Y4 ; INST prgmem/pe_3 LOC = RAMB16_X1Y3 ;
Ces contraintes sont déjà présentes dans le fichier ucf donné dans les ressources mais sont en commentaires.
Le fichier memory.bmm correspondant contiendra :
ADDRESS_SPACE prgmem RAMB16 [0x00000000:0x00001FFF] BUS_BLOCK prgmem/pe_1 [7:4] PLACED = X1Y6; prgmem/pe_0 [3:0] PLACED = X1Y5; prgmem/pe_3 [15:12] PLACED = X1Y3 ; prgmem/pe_2 [11:8] PLACED = X1Y4; END_BUS_BLOCK; END_ADDRESS_SPACE;
Voici donc schématiquement comment cela se passe.
Pour la carte Nexys6
[modifier | modifier le wikicode]Les contraintes peuvent être :
## version microcontroleur seul INST prgmem/pe_0 LOC = RAMB16_X0Y0; INST prgmem/pe_1 LOC = RAMB16_X1Y4; INST prgmem/pe_2 LOC = RAMB16_X0Y2; INST prgmem/pe_3 LOC = RAMB16_X1Y6;
Quant au fichier memory.bmm associé, il doit alors absolument être
/////////////////////////////////////////////////////////////////////////////// // // Address space 'prgmem' 0x00000000:0x00001FFF (8 KBytes). // /////////////////////////////////////////////////////////////////////////////// ADDRESS_SPACE prgmem RAMB16 [0x00000000:0x00001FFF] BUS_BLOCK prgmem/pe_1 RAMB16 [7:4] [0:4095] PLACED = X1Y4; prgmem/pe_0 RAMB16 [3:0] [0:4095] PLACED = X0Y0; prgmem/pe_3 RAMB16 [15:12] [0:4095] PLACED = X1Y6; prgmem/pe_2 RAMB16 [11:8] [0:4095] PLACED = X0Y2; END_BUS_BLOCK; END_ADDRESS_SPACE;
Travail introductif
[modifier | modifier le wikicode]Comprendre la notion de PORT d'entrée et de PORT de sorties Voici un premier exemple de programme assembleur
#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, 255 ; load R16 with 255
; STS DDRB, R16 ; set all bits of port B as output
; LDI R16, 2 ; load R16 with 2
; LDI R17, 4 ; load R17 with 4
; ADD R16, r17 ; R16 <- R16 + R17
IN R16, PINA
OUT PORTA, R16 ; result to port A
RJMP main
.END
Ce programme ne fait que recopier l'état des interrupteurs (PINA) sur les LEDs (PORTA). Un certain nombre d'instructions ont été volontairement laissées en commentaire (qui rappelons-le, commencent par un « ; »). Intéressez-vous seulement à ce qu’il y a entre main : et .END
À ce stade, vous devez franchement être choqué par le fait que l’on ne positionne pas DDRA avant de sortir sur le PORTA. On rappelle donc que dans notre FPGA les PORTs ne seront pas bidirectionnels sauf si nécessaire (comme pour l'i2c par exemple). En clair, les fils qui arrivent sur PINA ne sont pas les mêmes que ceux qui sortent par PORTA. Cette propriété ne peut être vraie que pour un FPGA, pas pour un Tiny861 du commerce.
Travail à réaliser
[modifier | modifier le wikicode]Faites tourner le programme ci-dessus dans votre processeur (qui sera dans le FPGA). Si vous avez souvenir de quelques instructions assembleur, vous pouvez essayer de faire autre chose que la recopie sur vos LEDs
Travaux pratiques d’introduction à l'assembleur
[modifier | modifier le wikicode]Travail à réaliser (Exercice 1)
[modifier | modifier le wikicode]On réalisera un programme qui réalise une addition de 255 et de 1 et qui sort le registre de statut (SREG) sur les Leds (PORTA). Si vous voulez savoir ce qui sort, la seule solution est de sortir dans PORTB sur les afficheurs 7 segments.
Indication 1 : Le registre SREG peut être accédé comme un PORT par les instructions IN et OUT.
Indication 2 : 127 + 1 fait un overflow car on additionne deux nombres positifs et le résultat est négatif (en complément à deux)
#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, 255 ; load R16 with 255 ou 127
LDI R17, 1 ; load R16 with 1
ADD R16, r17 ; R16 <- R16 + R17
IN R16, SREG
OUT PORTA, R16 ; result to port A
RJMP main
.END
Travail à réaliser (Exercice 2)
[modifier | modifier le wikicode]Charger R16 et R17 avec deux nombres et étudier les résultats et les bits de statut (drapeaux) pour les opérations suivantes.
COM R16 ; Complement
NEG R16 ; 2's complement
TST R16 ; test for zero or minus
AND R16, R17 ; bitwise AND
OR R16, R17 ; bitwise OR
ADD R16, R17 ; summing
Indication : Un des deux registres peut être chargé à l'aide des interrupteurs.
#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:
IN R16,PINA ; lecture des interrupteurs
; LDI R17, 1 ; load R16 with 1
COM R16 ; R16 <- /R16
IN R16, SREG
OUT PORTA, R16 ; result to port A
RJMP main
.END
Travail à réaliser (Exercice 3)
[modifier | modifier le wikicode]Additionnez un nombre et son complément à 2, et faites la même chose avec son complément à 1 et comparez les résultats
LDI R16, 10 ; load number
MOV R17, R16
NEG R16 ; 2's complement
ADD R17, R16
Travail à réaliser (Exercice 4)
[modifier | modifier le wikicode]On vous demande de réaliser un compteur binaire qui sort sur des LEDs. La seule difficulté de cet exercice est de réaliser une temporisation.
Indication : essayer de comprendre et d’utiliser ce code :
delay:
ldi r24, 0xff ; load 0xffff to r25:r24
ldi r25, 0xff
delayloop:
sbiw r24,1 ; decrement r25:r24
brne delayloop ; branch if not 0
ret
Montrer qu'un tel delay est encore trop rapide. Pour avoir un temps correct il faudra le faire environ 0x40 fois.
#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:
INC R16 ; incrementation perpetuelle
OUT PORTA, R16 ; result to port A
rcall delay
RJMP main
delay:
ldi r23, 0x80 ; sur ma nexys 3
delayloop_ext:
ldi r24, 0xff ; load 0xffff to r25:r24
ldi r25, 0xff
delayloop:
sbiw r24,1 ; decrement r25:r24
brne delayloop ; branch if not 0
dec r23
brne delayloop_ext
ret
.END
Travail à réaliser (Exercice 5)
[modifier | modifier le wikicode]On vous demande de rechercher les instructions à mettre en œuvre pour la réalisation de chenillards et de réaliser les programmes correspondants.
1°) Réaliser un chenillard simple avec l'instruction « lsl » en détectant la disparition du '1'. Indication : Si l'instruction « lsl » est appliquée dans une boucle, le '1' finit par disparaître !!!!
2°) Réaliser ce même chenillard en initialisant la SRAM avec un code du style :
main:
; setup : initialisation de la SRAM
LDI R16,0x01
STS 0x0060,R16 ; debut mémoire SRAM ATMega8
LDI R16,0x02
STS 0x0061,R16
Puis aller chercher sans arrêt ces valeurs dans la RAM avec l'instruction « LD R16,Y+ » sachant que Y=R29:R28.
Indication : La SRAM d'un ATTiny26/46/86 commence à partir de l'adresse 0x0060. Sa fin dépend de la série (128/256/512 octets) 0x0DF/0x015F/025F
Un algorithme est donné sous forme d'organigramme un peu plus loin.
1°) Correction chenillard simple :
#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 ; déclage perpetuelle
brne suite
LDI R16,1 ; setup
suite:
OUT PORTA, R16 ; result to port A
rcall delay
RJMP loop
delay:
ldi r23, 0x80 ; 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
"BRNE suite" peut être remplacé par "BRCC suite" mais pas par "BRVC suite".
2°) Correction chenillard simple avec SRAM :
#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:
; setup : initialisation de la SRAM
LDI R16,0x01
STS 0x0060,R16 ; debut mémoire SRAM ATMega8
LDI R16,0x02
STS 0x0061,R16
LDI R16,0x04
STS 0x0062,R16
LDI R16,0x08
STS 0x0063,R16
LDI R16,0x10
STS 0x0064,R16
LDI R16,0x20
STS 0x0065,R16
LDI R16,0x40
STS 0x0066,R16
LDI R16,0x80
STS 0x0067,R16
CLR R29 ; poids fort de Y
LDI R28, 0x60 ; debut RAM
LDI R17, 0x68 ; fin comptage
loop:
LD R16,Y+
CPI R28,0x68 ; compare and skip if not equal
BRNE suite
LDI R28, 0x60 ; debut RAM
suite:
OUT PORTA, R16 ; result to port A
rcall delay
RJMP loop
delay:
ldi r23, 0x80 ; 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
Réalisation d'une émission RS232
[modifier | modifier le wikicode]Sur le tiny861 du commerce, il n'y a pas de liaison série. Nous allons donc en ajouter une.
Un Logicore pour ne pas réinventer la roue
[modifier | modifier le wikicode]On vous donne l’ensemble matériel qui réalise la partie transmission de la liaison série.

Réaliser la liaison série
[modifier | modifier le wikicode]Insérer ce logicore dans le composant travail à réaliser comme indiqué dans le schéma ci-dessous :
Exercice de préparation
[modifier | modifier le wikicode]Le compteur mod_m_counter est à configurer. Pour ce faire, il faut savoir que l’on va travailler à 19200 bauds et que la fréquence d'horloge est de 50 MHz. En déduire la valeur à mettre dans le generic de ce compteur.
Comment interfacer des PORTs/Registres dans l'ATTiny861
[modifier | modifier le wikicode]Pour information, le cœur original d'Andreas Hilvarsson qui réalise le PORTA est donné maintenant :
entity ioport is
Generic (BASE_ADDR : integer := 16#3B#);
Port ( clk : in STD_LOGIC;
addr : in STD_LOGIC_VECTOR (5 downto 0);
ioread : out STD_LOGIC_VECTOR (7 downto 0);
iowrite : in STD_LOGIC_VECTOR (7 downto 0);
rd : in STD_LOGIC;
wr : in STD_LOGIC;
inport : in STD_LOGIC_VECTOR (7 downto 0);
outport : out STD_LOGIC_VECTOR (7 downto 0));
end ioport;
architecture ioport_architecture of ioport is
begin
im : 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);
if (a_int = BASE_ADDR) then
rdwr := rd & wr;
ioread <= (others => '0');
case rdwr is
when "10" => -- rd
ioread <= inport;
when "01" => -- wr
outport <= iowrite;
when others => NULL; -- do nothing
end case;
end if;
end if;
end process im;
end ioport_architecture;
Cet ensemble était transformé en composants puis câblé pour réaliser le PORT. Mais nous ne le garderons pas pour deux raisons :
- son côté bidirectionnel est inutile dans un FPGA (en tout cas très souvent)
- nous avons utilisé un ATMega8 très souvent dans ce cours et avons apprécié ses deux process séparés pour les entrées et sorties. Les PORTs et registres de l'ATMega8 restent pour nous une référence.
Nous avons donc essayé de les recopier dans ce projet en retirant donc l'aspect bidirectionnel des PORTs que nous jugeons absolument inutile dans un FPGA. Ainsi voici comment sera réalisée les entrées/sorties avec des PORTs :
----------------------------------------------------------------------------------
-- 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;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity microcontroleur 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);
Aff7segs : out STD_LOGIC_VECTOR (7 downto 0));
-- O_reg_pc : out std_logic_vector(15 downto 0));
end microcontroleur;
architecture microcontroleur_architecture of microcontroleur 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)
-- DEBUG
O_reg_pc : out std_logic_vector(15 downto 0)
);
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;
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,
-- DEBUG
O_reg_pc => open
);
prgmem : pm port map (
Clk => clk,
Rst => Rst,
-- 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" (0X3B)
Aff7segs <= 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;
-- managing FIFO
-- L_WE_UART <= IO_wr when (IO_A = UDR) else '0'; -- write UART UDR
-- L_RD_UART <= IO_rd when (IO_A = UDR) else '0'; -- read UART UDR
end microcontroleur_architecture;
Travail à réaliser (exercice 1)
[modifier | modifier le wikicode]On testera les communications avec gtkterm (application Linux) configuré comme ci-dessous (à part peut-être le PORT) :
PORT :/dev/ttyS0, vitesse : 19200, Parité : none, Bits 8, Bit de stop : 1, contrôle de flux : none
1°) Réaliser la partie matérielle complète en gérant le bit TXEN du registre UCSRB. L'écriture dans le registre UDR devra envoyer par la liaison série. De plus, les bits UDRE et TXC du registre UCSRA seront reliés au complément de "buffer_full". Pour simplifier, les registres UCSRB et UDR seront en écriture seulement et UCSRA sera en lecture seulement.
2°) Réaliser un programme qui lit sans arrêt les switchs et envoie sur la RS232 leur valeur seulement lorsqu’ils ont changé. Pour réaliser cet exercice vous allez procéder en deux temps :
2-1) Réaliser un programme qui lit sans arrêt les interrupteurs et envoie un '1' lorsqu’ils ont changés (et seulement dans ce cas). L'envoi du '1' se fera par un sous-programme. Bien faire la distinction entre '1' et 1 !!!!
Indication : il faut réserver un registre pour l'ancienne valeur des interrupteurs et un pour la nouvelle de manière à pouvoir comparer. En fin de boucle ne pas oublier de mettre la nouvelle valeur dans l'ancienne. Ceci est expliqué dans l'organigramme ci-dessus. L'envoi d'un caractère par la liaison série se fait par la simple instruction :
OUT UDR,R16
2-2) Modifier le sous programme « serial_tx » pour qu’il envoie maintenant deux caractères correspondant à la valeur hexadécimale des interrupteurs. Indication : voici comment on procède à l'envoi d'un caractère hexadécimal correspondant à la valeur des poids faibles :
;**** poids faible
MOV R18,R16 ; pour garder la valeur de R16
ANDI R18,0x0F
CPI R18,0x0A
BRGE ss_3 ; if greater or equal
LDI R19,'0'; code ASCII de '0'
ADD R18,R19
RJMP ss_4
ss_3:
LDI R19,'A'-0x0A ; code ASCII de 'A'-A
ADD R18,R19
ss_4:
OUT UDR,R18
Faites la même chose avec les poids forts. Le fin du fin serait d'ajouter un espace pour séparer les futures valeurs. Essayez avec :
wait:
SBIS UCSRA,UDRE ;attente envoi précédent terminé bit UDRE
RJMP wait
LDI R18,' ' ; pour séparer la suite
OUT UDR,R18
#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
#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:
; setup()
LDI R16, (1<<TXEN) ; initialisation liaison série
OUT UCSRB,R16
CLR R17 ; r17 = OLD interrupts
loop:
IN R16,PINB ;R16 = NEW interrupts
CP R16,R17 ; copare new and old
BREQ suite
rcall serial_tx ; avec parametre dans R16
suite:
MOV R17,R16
RJMP loop
serial_tx: ;parametre dans R16 doit être conservé
;**** poids fort
MOV R18,R16 ; pour garder la valeur de R16
LSR R18
LSR R18
LSR R18
LSR R18
ANDI R18,0x0F
CPI R18,0x0A
BRGE ss_1 ; if greater or equal
LDI R19,'0'; code ASCII de '0'
ADD R18,R19
RJMP ss_2
ss_1:
LDI R19,0x37 ; code ASCII de 'A'-A
ADD R18,R19
ss_2:
OUT UDR,R18
;**** poids faible
MOV R18,R16 ; pour garder la valeur de R16
ANDI R18,0x0F
CPI R18,0x0A
BRGE ss_3 ; if greater or equal
LDI R19,'0'; code ASCII de '0'
ADD R18,R19
RJMP ss_4
ss_3:
LDI R19,'A'-0x0A ; code ASCII de 'A'-A
ADD R18,R19
ss_4:
OUT UDR,R18
wait:
SBIS UCSRA,UDRE
RJMP wait
LDI R18,' ' ; pour séparer la suite
OUT UDR,R18
RET
.END
Travail à réaliser (exercice 2)
[modifier | modifier le wikicode]Réaliser un compteur décimal sur deux digits sortant par la liaison série (avec une temporisation bien sûr).
#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
#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:
; setup : initialisation de la SRAM
LDI R16,0x01
STS 0x0060,R16 ; debut mémoire SRAM ATMega8
LDI R16,0x02
STS 0x0061,R16
LDI R16,0x04
STS 0x0062,R16
LDI R16,0x08
STS 0x0063,R16
LDI R16,0x10
STS 0x0064,R16
LDI R16,0x20
STS 0x0065,R16
LDI R16,0x40
STS 0x0066,R16
LDI R16,0x80
STS 0x0067,R16
STS 0x0068,R16
LDI R16,0x40
STS 0x0069,R16
LDI R16,0x20
STS 0x006A,R16
LDI R16,0x10
STS 0x006B,R16
LDI R16,0x08
STS 0x006C,R16
LDI R16,0x04
STS 0x006D,R16
LDI R16,0x02
STS 0x006E,R16
LDI R16,0x01
STS 0x006F,R16
CLR R29 ; poids fort de Y
LDI R28, 0x60 ; debut RAM
LDI R17, 0x68 ; fin comptage
loop:
IN R16,PINB ; ne positionne aucun flag !!!
ANDI R16,0xFF ; un de mes interr ne marche pas
BREQ not_programming_mode
RCALL serial_rx
not_programming_mode:
LD R16,Y+
CPI R28,0x70 ; compare and skip if not equal
BRNE suite
LDI R28, 0x60 ; debut RAM
suite:
OUT PORTC, R16 ; result to port B
rcall delay
RJMP loop
serial_rx:
; Affichage du L
LDI R16,0xC7
OUT PORTB, R16
; X pour adressage indexé
CLR R27 ; poids fort de X
LDI R26, 0x60 ; debut RAM
; compteur de boucle
LDI R17,0x0F
rcve:
SBIS UCSRA,RXC
RJMP rcve
IN r16, UDR
ST X+,r16
DEC R17
BRNE rcve
RET
delay:
ldi r23, 0x80 ; 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
Réalisation matérielle d'une réception RS232
[modifier | modifier le wikicode]Par rapport au TP précédent la liaison devra être dans l'autre sens : c’est le PC qui maintenant va fournir des valeurs au FPGA. La réalisation de cette liaison se fait pratiquement comme dans le TP précédent (_rx au lieu de _tx) :

Travail à réaliser (exercice 1)
[modifier | modifier le wikicode]Modifier la partie matérielle de l'exercice 1 du TP précédent pour réaliser une réception maintenant. Le bit RXEN du registre UCSRB autorisera la réception. Le registre UDR sera maintenant en écriture pour envoyer par la liaison série, mais aussi en lecture pour lire ce qui est arrivé. De plus, le bit TXC du registre UCSRA sera relié au "data_present".
Indications :
Les chenillards et la RS232
[modifier | modifier le wikicode]On désire maintenant reprendre le chenillard déjà réalisé en assembleur, particulièrement celui qui utilisait une mémoire RAM pour s'exécuter. Pour simplifier tout sera maintenant réalisé en C.
Travail à réaliser (exercice 2)
[modifier | modifier le wikicode]On cherche maintenant à utiliser la réception RS232. L'objectif est de réaliser le chenillard normalement, sauf quand des données sont envoyées à partir du PC. Elle sont alors disposées dans la mémoire en lieu et place des anciennes valeurs. Pour simplifier votre travail, on adoptera le protocole suivant : quand une donnée arrive alors on sait qu’il y en aura forcément 16 et on les attendra toutes les 16. Une fois terminé on retournera tranquillement à notre chenillard.
N'oubliez pas la temporisation pour voir les chenillards.
Pour passer en mode programmation nous allons utiliser un interrupteur. En début de chaque boucle la position de cet interrupteur est testée et s'il est positionné on se dirige vers le programme de chargement. Un 'L' sera affiché sur les afficheurs dans ce cas mais un 'U' dans le cas d'un non chargement.
Exercice 2 en version assembleur
[modifier | modifier le wikicode]L'organigramme du programme à réaliser est présenté maintenant :
#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
#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:
; setup : initialisation de la SRAM
LDI R16,0x01
STS 0x0060,R16 ; debut mémoire SRAM ATMega8
LDI R16,0x02
STS 0x0061,R16
LDI R16,0x04
STS 0x0062,R16
LDI R16,0x08
STS 0x0063,R16
LDI R16,0x10
STS 0x0064,R16
LDI R16,0x20
STS 0x0065,R16
LDI R16,0x40
STS 0x0066,R16
LDI R16,0x80
STS 0x0067,R16
STS 0x0068,R16
LDI R16,0x40
STS 0x0069,R16
LDI R16,0x20
STS 0x006A,R16
LDI R16,0x10
STS 0x006B,R16
LDI R16,0x08
STS 0x006C,R16
LDI R16,0x04
STS 0x006D,R16
LDI R16,0x02
STS 0x006E,R16
LDI R16,0x01
STS 0x006F,R16
CLR R29 ; poids fort de Y
LDI R28, 0x60 ; debut RAM
LDI R17, 0x68 ; fin comptage
loop:
IN R16,PORTA ; ne positionne aucun flag !!!
ANDI R16,0xFF ; pour positionner le flag !!!
BREQ not_programming_mode
RCALL serial_rx
not_programming_mode:
LD R16,Y+
CPI R28,0x70 ; compare and skip if not equal
BRNE suite
LDI R28, 0x60 ; debut RAM
suite:
OUT PORTC, R16 ; result to port B
rcall delay
RJMP loop
serial_rx:
; Affichage du L
LDI R16,0xC7
OUT PORTB, R16
; X pour adressage indexé
CLR R27 ; poids fort de X
LDI R26, 0x60 ; debut RAM
; compteur de boucle
LDI R17,0x0F
rcve:
SBIS UCSRA,RXC
RJMP rcve
IN r16, UDR
ST X+,r16
DEC R17
BRNE rcve
RET
delay:
ldi r23, 0x80 ; 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
Réalisation d'une transmission RS232 pour carte Spartan6
[modifier | modifier le wikicode]Il est impossible d’utiliser les logicores précédents avec la carte Spartan6. La raison est que les logicores déjà utilisés sont certes écrit en VHDL, mais d'une manière très matérielle. En particulier, ils utilisent des LUT4 qui sont disponibles dans les circuits de typa Spartan3, mais pas dans les circuits Spartan6 qui utilisent des LUT6.
Le nouveau logicore adapté à notre problème présent s’appelle uart_tx6 et est disponible chez Xilinx. Il a à peu près les mêmes entrées/sorties que uart_tx, mais quelques noms ont été inversés.
Schéma de principe
[modifier | modifier le wikicode]Comme d'habitude, nous allons présenter le travail à réaliser sous forme schématique.
Réalisation matérielle
[modifier | modifier le wikicode]La solution présentée ci-dessous gère à la fois la réception et l'émission par la liaison série. Elle utilise par contre les logicores adaptés au FPGA de type Spartan6. Si vous utilisez ceux du spartan3 il vous faudra légèrement adapter le code ci-dessous car quelques entrées ont changé de nom.
entity microcontroleur 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);
serial_in : in std_logic;
Led : out STD_LOGIC_VECTOR (7 downto 0);
Serial_out : out std_logic;
Aff7segs : out STD_LOGIC_VECTOR (7 downto 0));
end microcontroleur;
architecture microcontroleur_architecture of microcontroleur 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)
-- DEBUG
O_reg_pc : out std_logic_vector(15 downto 0)
);
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;
component uart_tx6 is
Port ( data_in : in std_logic_vector(7 downto 0);
buffer_write : in std_logic;
buffer_reset : in std_logic;
en_16_x_baud : in std_logic;
serial_out : out std_logic;
buffer_data_present : out std_logic;
buffer_full : out std_logic;
buffer_half_full : out std_logic;
clk : in std_logic);
end component;
component mod_m_counter is
generic(
N : integer := 4; -- number of bits
M : integer := 10 -- mod-M
);
port (
clk, reset : in std_logic;
max_tick : out std_logic;
q : out std_logic_vector(N-1 downto 0)
);
end component mod_m_counter;
component uart_rx6 is
Port ( serial_in : in std_logic;
en_16_x_baud : in std_logic;
data_out : out std_logic_vector(7 downto 0);
buffer_read : in std_logic;
buffer_data_present : out std_logic;
buffer_half_full : out std_logic;
buffer_full : out std_logic;
buffer_reset : in std_logic;
clk : in std_logic);
end component uart_rx6;
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;
-- rs232
signal s_en_16_x_baud, s_en, s_full : std_logic;
signal s_UCSRB : std_logic_vector(7 downto 0);
signal s_buffer_read, s_RXC, IO_rd_d : std_logic;
signal s_in_UDR : 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,
-- DEBUG
O_reg_pc => open
);
prgmem : pm port map (
Clk => clk,
Rst => Rst,
-- 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
);
rs232rx:uart_tx6 port map (
data_in => IO_Dwr,
buffer_write => s_en,
buffer_reset => rst,
en_16_x_baud => s_en_16_x_baud,
serial_out => serial_out,
buffer_data_present => open,
buffer_full => s_full,
buffer_half_full => open,
clk => clk
);
baud19200: mod_m_counter
generic map (
N => 9, -- number of bits
M => 326 -- mod-M
)
port map (
clk => clk,
reset => Rst,
max_tick => s_en_16_x_baud,
q => open
);
rs232tx: uart_rx6 port map (
serial_in => serial_in,
en_16_x_baud => s_en_16_x_baud,
data_out => s_in_UDR,
buffer_read => s_buffer_read,
buffer_data_present => s_RXC,
buffer_half_full => open,
buffer_full => open,
buffer_reset => rst,
clk => clk
);
-- 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 UCSRB =>
s_UCSRB <= 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 UCSRA => IO_Drd <= (s_RXC & not s_full & not s_full & "00000");
when UDR => IO_Drd <= s_in_UDR;
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;
-- managing FIFO
s_en <= IO_wr and s_UCSRB(3) when (IO_A = UDR) else '0'; -- write UART UDR
delay:process(clk) begin
if rising_edge(clk) then
IO_rd_d <= IO_rd;
end if;
end process;
s_buffer_read <= IO_rd_d and s_UCSRB(4) when (IO_A = UDR) else '0'; -- read UART UDR
end microcontroleur_architecture;
Réalisation partielle du timer 1
[modifier | modifier le wikicode]Nous allons donc partiellement réaliser le timer 1.
- Partiellement car il est normalement sur 10 bits et nous n'en garderons que 8.
- Partiellement car nous nous contenterons d'une division sur 3 bits (du registre TCCR1B) contre 4 bits dans celui du commerce. Seules les divisions par 0, 1, 2, 4, 8, 16, 32 et 64 seront implantées. Il manque donc les divisions par 128, 256, 512, 1024, 2048, 4096, 8192 et 16384.
- Partiellement car nous n'implémenterons ni le dépassement de capacité ni les
comparaisons....
- Partiellement car donc seuls TCNT1 (0x2E) et TCCR1B (0x2F) seront implémentés.

- écrire dans TCNT1
- régler le pré diviseur en écrivant dans TCCR1B
- mais le bit TOV1 du registre TIFR n’est pas implanté
La dernière remarque peut sembler une limitation importante, mais il vous faut savoir qu’il n'y a aucun mécanisme d'interruption dans ce cœur !
Indication 1
[modifier | modifier le wikicode]On prendra soin de définir les deux constantes TCNT1 et TCCR1B. On réalisera ensuite le célèbre :
s_wr_TCNT1 <= IO_wr when IO_A = TCNT1 else '0';
Le timer pourra être un simple process dans "microcontroleur.vhd"
process(clk) begin
if rising_edge(clk) then
if s_wr_TCNT1 = '1' then
s_s_tcnt1 <= s_tcnt1; -- s_tcnt1 vient du process iowr (à ajouter)
elsif s_en_timer1 = '1' then
s_s_tcnt1 <= s_s_tcnt1 + 1;
end if;
end if;
end process;
Indication 2
[modifier | modifier le wikicode]La prédivision peut être faite par :
-- Timer 1
-- prescaler
process(clk) begin
if rising_edge(clk) then
s_prescaler <= s_prescaler + 1;
end if;
end process;
-- tick generation
s_div2 <= '1' when s_prescaler(0) = '1' else '0';
s_div4 <= '1' when s_prescaler(1 downto 0) = "11" else '0';
s_div8 <= '1' when s_prescaler(2 downto 0) = "111" else '0';
s_div16 <= '1' when s_prescaler(3 downto 0) = "1111" else '0';
s_div32 <= '1' when s_prescaler(4 downto 0) = "11111" else '0';
s_div64 <= '1' when s_prescaler(5 downto 0) = "111111" else '0';
-- grand multiplexeur maintenant
s_en_timer1 <= '0' when s_tccr1b(2 downto 0) = "000" else -- arrêt timer
'1' when s_tccr1b(2 downto 0) = "001" else -- division par 1
s_div2 when s_tccr1b(2 downto 0) = "010" else
s_div4 when s_tccr1b(2 downto 0) = "011" else
s_div8 when s_tccr1b(2 downto 0) = "100" else
s_div16 when s_tccr1b(2 downto 0) = "101" else
s_div32 when s_tccr1b(2 downto 0) = "110" else
s_div64 when s_tccr1b(2 downto 0) = "111";
Vous complétez correctement les deux process iowr et iord et c’est tout.
Voici maintenant la solution partielle.
--******* fichier microcontroleur.vhd partiel ************
architecture microcontroleur_architecture of microcontroleur is
--Registres et PORTs de l'ATTiny861
constant TCCR1B : std_logic_vector(5 downto 0) := "101111";
constant TCNT1 : std_logic_vector(5 downto 0) := "101110";
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";
-- voici seulement les déclarations ajoutées
-- Timer 1
signal s_en_timer1,s_div2,s_div4,s_div8,s_div16,s_div32,s_div64 : std_logic;
signal s_wr_TCNT1_d,s_wr_TCNT1 : std_logic;
signal s_tcnt1, s_tccr1b, s_s_tcnt1 : std_logic_vector(7 downto 0);
signal s_prescaler : std_logic_vector(5 downto 0);
BEGIN
-- PORT MAP et autres retirés
-- 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 DDRA =>
an <= IO_Dwr(3 downto 0);
when PORTB => -- PORTB=X"18" (0X38)
Aff7segs <= IO_Dwr;
when UCSRB =>
s_UCSRB <= IO_Dwr;
when TCNT1 => s_tcnt1 <= IO_Dwr;
when TCCR1B => s_tccr1b <= 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 UCSRA => IO_Drd <= (s_RXC & not s_full & not s_full & "00000");
when UDR => IO_Drd <= s_in_UDR;
when PINA => IO_Drd <= sw; -- PINA=X"19" (0X39)
when PINB => IO_Drd <= In_PINB; -- PINB=X"16" (0X36)
when TCNT1 => IO_Drd <= s_s_TCNT1;
when others => IO_Drd <= X"AA";
end case;
end if;
end process;
--************* Timer 1 ******************
-- prescaler
process(clk) begin
if rising_edge(clk) then
s_prescaler <= s_prescaler + 1;
end if;
end process;
-- tick generation
s_div2 <= '1' when s_prescaler(0) = '1' else '0';
s_div4 <= '1' when s_prescaler(1 downto 0) = "11" else '0';
s_div8 <= '1' when s_prescaler(2 downto 0) = "111" else '0';
s_div16 <= '1' when s_prescaler(3 downto 0) = "1111" else '0';
s_div32 <= '1' when s_prescaler(4 downto 0) = "11111" else '0';
s_div64 <= '1' when s_prescaler(5 downto 0) = "111111" else '0';
s_en_timer1 <= '0' when s_tccr1b(2 downto 0) = "000" else -- arrêt timer
'1' when s_tccr1b(2 downto 0) = "001" else -- division par 1
s_div2 when s_tccr1b(2 downto 0) = "010" else
s_div4 when s_tccr1b(2 downto 0) = "011" else
s_div8 when s_tccr1b(2 downto 0) = "100" else
s_div16 when s_tccr1b(2 downto 0) = "101" else
s_div32 when s_tccr1b(2 downto 0) = "110" else
s_div64 when s_tccr1b(2 downto 0) = "111";
-- TCNT1
s_wr_TCNT1 <= IO_wr when IO_A = TCNT1 else '0';
process(clk) begin
if rising_edge(clk) then
s_wr_TCNT1_d <= s_wr_TCNT1;
if s_wr_TCNT1 = '1' then
s_s_tcnt1 <= s_tcnt1;
elsif s_en_timer1 = '1' then
s_s_tcnt1 <= s_s_tcnt1 + 1;
end if;
end if;
end process;
END microcontroleur_architecture;
ANNEXE I : les fichiers nécessaires aux TPs de ce chapitre (Version pour Spartan 6)
[modifier | modifier le wikicode]Le cœur ATTiny861 est disponible ICI chez Opencores.org. Nous en donnons une version modifiée pour un fonctionnement avec data2mem ce qui simplifie sa programmation mais le rend très dépendant des outils Xilinx.
La mémoire programme modifiée
[modifier | modifier le wikicode]Nous avons modifié la mémoire programme par rapport au cœur original. En voici la nouvelle version destinée à réaliser 8 ko pour la version ATTiny861 donc.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
library unisim;
use unisim.vcomponents.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
constant p0_00 : BIT_VECTOR := X"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
begin
pe_0 : RAMB16_S4 ---------------------------------------------------------
generic map(
INIT_00 => p0_00, INIT_01 => p0_00, INIT_02 => p0_00, INIT_03 => p0_00,
INIT_04 => p0_00, INIT_05 => p0_00, INIT_06 => p0_00, INIT_07 => p0_00,
INIT_08 => p0_00, INIT_09 => p0_00, INIT_0A => p0_00, INIT_0B => p0_00,
INIT_0C => p0_00, INIT_0D => p0_00, INIT_0E => p0_00, INIT_0F => p0_00,
INIT_10 => p0_00, INIT_11 => p0_00, INIT_12 => p0_00, INIT_13 => p0_00,
INIT_14 => p0_00, INIT_15 => p0_00, INIT_16 => p0_00, INIT_17 => p0_00,
INIT_18 => p0_00, INIT_19 => p0_00, INIT_1A => p0_00, INIT_1B => p0_00,
INIT_1C => p0_00, INIT_1D => p0_00, INIT_1E => p0_00, INIT_1F => p0_00,
INIT_20 => p0_00, INIT_21 => p0_00, INIT_22 => p0_00, INIT_23 => p0_00,
INIT_24 => p0_00, INIT_25 => p0_00, INIT_26 => p0_00, INIT_27 => p0_00,
INIT_28 => p0_00, INIT_29 => p0_00, INIT_2A => p0_00, INIT_2B => p0_00,
INIT_2C => p0_00, INIT_2D => p0_00, INIT_2E => p0_00, INIT_2F => p0_00,
INIT_30 => p0_00, INIT_31 => p0_00, INIT_32 => p0_00, INIT_33 => p0_00,
INIT_34 => p0_00, INIT_35 => p0_00, INIT_36 => p0_00, INIT_37 => p0_00,
INIT_38 => p0_00, INIT_39 => p0_00, INIT_3A => p0_00, INIT_3B => p0_00,
INIT_3C => p0_00, INIT_3D => p0_00, INIT_3E => p0_00, INIT_3F => p0_00)
port map(ADDR => PM_A(11 downto 0),
CLK => CLK,
DI => "0000",
EN => '1',
SSR => '0',
WE => '0',
DO => PM_Drd(3 downto 0));
pe_1 : RAMB16_S4 ---------------------------------------------------------
generic map(
INIT_00 => p0_00, INIT_01 => p0_00, INIT_02 => p0_00, INIT_03 => p0_00,
INIT_04 => p0_00, INIT_05 => p0_00, INIT_06 => p0_00, INIT_07 => p0_00,
INIT_08 => p0_00, INIT_09 => p0_00, INIT_0A => p0_00, INIT_0B => p0_00,
INIT_0C => p0_00, INIT_0D => p0_00, INIT_0E => p0_00, INIT_0F => p0_00,
INIT_10 => p0_00, INIT_11 => p0_00, INIT_12 => p0_00, INIT_13 => p0_00,
INIT_14 => p0_00, INIT_15 => p0_00, INIT_16 => p0_00, INIT_17 => p0_00,
INIT_18 => p0_00, INIT_19 => p0_00, INIT_1A => p0_00, INIT_1B => p0_00,
INIT_1C => p0_00, INIT_1D => p0_00, INIT_1E => p0_00, INIT_1F => p0_00,
INIT_20 => p0_00, INIT_21 => p0_00, INIT_22 => p0_00, INIT_23 => p0_00,
INIT_24 => p0_00, INIT_25 => p0_00, INIT_26 => p0_00, INIT_27 => p0_00,
INIT_28 => p0_00, INIT_29 => p0_00, INIT_2A => p0_00, INIT_2B => p0_00,
INIT_2C => p0_00, INIT_2D => p0_00, INIT_2E => p0_00, INIT_2F => p0_00,
INIT_30 => p0_00, INIT_31 => p0_00, INIT_32 => p0_00, INIT_33 => p0_00,
INIT_34 => p0_00, INIT_35 => p0_00, INIT_36 => p0_00, INIT_37 => p0_00,
INIT_38 => p0_00, INIT_39 => p0_00, INIT_3A => p0_00, INIT_3B => p0_00,
INIT_3C => p0_00, INIT_3D => p0_00, INIT_3E => p0_00, INIT_3F => p0_00)
port map(ADDR => PM_A(11 downto 0),
CLK => CLK,
DI => "0000",
EN => '1',
SSR => '0',
WE => '0',
DO => PM_Drd(7 downto 4));
pe_2 : RAMB16_S4 ---------------------------------------------------------
generic map(
INIT_00 => p0_00, INIT_01 => p0_00, INIT_02 => p0_00, INIT_03 => p0_00,
INIT_04 => p0_00, INIT_05 => p0_00, INIT_06 => p0_00, INIT_07 => p0_00,
INIT_08 => p0_00, INIT_09 => p0_00, INIT_0A => p0_00, INIT_0B => p0_00,
INIT_0C => p0_00, INIT_0D => p0_00, INIT_0E => p0_00, INIT_0F => p0_00,
INIT_10 => p0_00, INIT_11 => p0_00, INIT_12 => p0_00, INIT_13 => p0_00,
INIT_14 => p0_00, INIT_15 => p0_00, INIT_16 => p0_00, INIT_17 => p0_00,
INIT_18 => p0_00, INIT_19 => p0_00, INIT_1A => p0_00, INIT_1B => p0_00,
INIT_1C => p0_00, INIT_1D => p0_00, INIT_1E => p0_00, INIT_1F => p0_00,
INIT_20 => p0_00, INIT_21 => p0_00, INIT_22 => p0_00, INIT_23 => p0_00,
INIT_24 => p0_00, INIT_25 => p0_00, INIT_26 => p0_00, INIT_27 => p0_00,
INIT_28 => p0_00, INIT_29 => p0_00, INIT_2A => p0_00, INIT_2B => p0_00,
INIT_2C => p0_00, INIT_2D => p0_00, INIT_2E => p0_00, INIT_2F => p0_00,
INIT_30 => p0_00, INIT_31 => p0_00, INIT_32 => p0_00, INIT_33 => p0_00,
INIT_34 => p0_00, INIT_35 => p0_00, INIT_36 => p0_00, INIT_37 => p0_00,
INIT_38 => p0_00, INIT_39 => p0_00, INIT_3A => p0_00, INIT_3B => p0_00,
INIT_3C => p0_00, INIT_3D => p0_00, INIT_3E => p0_00, INIT_3F => p0_00)
port map(ADDR => PM_A(11 downto 0),
CLK => CLK,
DI => "0000",
EN => '1',
SSR => '0',
WE => '0',
DO => PM_Drd(11 downto 8));
pe_3 : RAMB16_S4 ---------------------------------------------------------
generic map(
INIT_00 => p0_00, INIT_01 => p0_00, INIT_02 => p0_00, INIT_03 => p0_00,
INIT_04 => p0_00, INIT_05 => p0_00, INIT_06 => p0_00, INIT_07 => p0_00,
INIT_08 => p0_00, INIT_09 => p0_00, INIT_0A => p0_00, INIT_0B => p0_00,
INIT_0C => p0_00, INIT_0D => p0_00, INIT_0E => p0_00, INIT_0F => p0_00,
INIT_10 => p0_00, INIT_11 => p0_00, INIT_12 => p0_00, INIT_13 => p0_00,
INIT_14 => p0_00, INIT_15 => p0_00, INIT_16 => p0_00, INIT_17 => p0_00,
INIT_18 => p0_00, INIT_19 => p0_00, INIT_1A => p0_00, INIT_1B => p0_00,
INIT_1C => p0_00, INIT_1D => p0_00, INIT_1E => p0_00, INIT_1F => p0_00,
INIT_20 => p0_00, INIT_21 => p0_00, INIT_22 => p0_00, INIT_23 => p0_00,
INIT_24 => p0_00, INIT_25 => p0_00, INIT_26 => p0_00, INIT_27 => p0_00,
INIT_28 => p0_00, INIT_29 => p0_00, INIT_2A => p0_00, INIT_2B => p0_00,
INIT_2C => p0_00, INIT_2D => p0_00, INIT_2E => p0_00, INIT_2F => p0_00,
INIT_30 => p0_00, INIT_31 => p0_00, INIT_32 => p0_00, INIT_33 => p0_00,
INIT_34 => p0_00, INIT_35 => p0_00, INIT_36 => p0_00, INIT_37 => p0_00,
INIT_38 => p0_00, INIT_39 => p0_00, INIT_3A => p0_00, INIT_3B => p0_00,
INIT_3C => p0_00, INIT_3D => p0_00, INIT_3E => p0_00, INIT_3F => p0_00)
port map(ADDR => PM_A(11 downto 0),
CLK => CLK,
DI => "0000",
EN => '1',
SSR => '0',
WE => '0',
DO => PM_Drd(15 downto 12));
end architecture Arch;
La mémoire donnée modifiée
[modifier | modifier le wikicode]Nous avons modifié la mémoire donnée par rapport au cœur original. En voici la nouvelle version destinée à réaliser 512 octets pour la version ATTiny861 donc.
----------------------------------------------------------------------------------
-- 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 1023) 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;
Notre enseignement nous a obligé à cibler un FPGA spartan3E500 ce qui a nécessité 10 min de compilation sur un PC récent, pour nous entendre dire qu’il n'y avait pas assez de ressource dans ce FPGA pour la compilation correspondante !
Le problème est tout simplement que la mémoire donnée est compilée dans des LUTs ! Il existe des options pour l'obliger à inférer des BROM mais nous ne voulions pas modifier les options de compilation. Nous avons décidé d’utiliser aussi directement des BRAM pour la RAM (voir un peu plus loin).
Pour information, nous savions depuis longtemps que le compilateur VHDL utilisé n'était pas le même pour les deux séries spartan3 et spartan6.
----------------------------------------------------------------------------------
-- 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
signal add : std_logic_vector(11 downto 0);
signal DO,DI : std_logic_vector(7 downto 0);
signal we,en,ssr : std_logic;
begin
add <= "000" & addr(8 downto 0);
dataread <= DO;
DI <= datawrite;
ssr <= '0';
we <= '1' when (rd='0' and wr='1') else '0';
en <= '1'; -- when (rd='1' or wr='1') else '0';
DM_RAMB16_S4_inst_MSB : RAMB16_S4
generic map (
INIT => X"0", -- Value of output RAM registers at startup
SRVAL => X"0", -- Output value upon SSR assertion
WRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
-- The following INIT_xx declarations specify the initial contents of the RAM
-- Address 0 to 1023
INIT_00 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_01 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_02 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_03 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_04 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_05 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_06 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_07 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_08 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_09 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 1024 to 2047
INIT_10 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_11 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_12 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_13 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_14 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_15 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_16 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_17 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_18 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_19 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 2048 to 3071
INIT_20 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_21 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_22 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_23 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_24 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_25 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_26 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_27 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_28 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_29 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 3072 to 4095
INIT_30 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_31 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_32 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_33 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_34 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_35 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_36 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_37 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_38 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_39 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3F => X"0000000000000000000000000000000000000000000000000000000000000000")
port map (
DO => DO(7 downto 4), -- 4-bit Data Output
ADDR => ADD, -- 12-bit Address Input
CLK => CLK, -- Clock
DI => DI(7 downto 4), -- 4-bit Data Input
EN => EN, -- RAM Enable Input
SSR => SSR, -- Synchronous Set/Reset Input
WE => WE -- Write Enable Input
);
DM_RAMB16_S4_inst_LSB : RAMB16_S4
generic map (
INIT => X"0", -- Value of output RAM registers at startup
SRVAL => X"0", -- Output value upon SSR assertion
WRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
-- The following INIT_xx declarations specify the initial contents of the RAM
-- Address 0 to 1023
INIT_00 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_01 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_02 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_03 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_04 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_05 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_06 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_07 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_08 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_09 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 1024 to 2047
INIT_10 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_11 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_12 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_13 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_14 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_15 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_16 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_17 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_18 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_19 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 2048 to 3071
INIT_20 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_21 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_22 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_23 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_24 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_25 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_26 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_27 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_28 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_29 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 3072 to 4095
INIT_30 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_31 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_32 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_33 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_34 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_35 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_36 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_37 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_38 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_39 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3F => X"0000000000000000000000000000000000000000000000000000000000000000")
port map (
DO => DO(3 downto 0), -- 4-bit Data Output
ADDR => ADD, -- 12-bit Address Input
CLK => CLK, -- Clock
DI => DI(3 downto 0), -- 4-bit Data Input
EN => EN, -- RAM Enable Input
SSR => SSR, -- Synchronous Set/Reset Input
WE => WE -- Write Enable Input
);
end dm_architecture;
Le fichier ucf
[modifier | modifier le wikicode]NET "CLK" LOC = "V10" | IOSTANDARD = "LVCMOS33"; #Bank = 2, pin name = IO_L30N_GCLK0_USERCCLK, Sch name = GCLK Net "CLK" TNM_NET = sys_clk_pin; TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 100000 kHz; ## 7 segment display #NET "Q_7segs<6>" LOC = "T17" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L51P_M1DQ12, Sch name = CA #NET "Q_7segs<5>" LOC = "T18" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L51N_M1DQ13, Sch name = CB #NET "Q_7segs<4>" LOC = "U17" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L51N_M1DQ13, Sch name = CC #NET "Q_7segs<3>" LOC = "U18" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L52N_M1DQ15, Sch name = CD #NET "Q_7segs<2>" LOC = "M14" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L53P, Sch name = CE #NET "Q_7segs<1>" LOC = "N14" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L53N_VREF, Sch name = CF #NET "Q_7segs<0>" LOC = "L14" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L61P, Sch name = CG #NET "Q_7segs<0>" LOC = "M13" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L61N, Sch name = DP #NET "an<0>" LOC = "N16" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L50N_M1UDQSN, Sch name = AN0 #NET "Q_an<0>" LOC = "N16" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L50N_M1UDQSN, Sch name = AN0 #NET "Q_an<1>" LOC = "N15" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L50P_M1UDQS, Sch name = AN1 #NET "Q_an<2>" LOC = "P18" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L49N_M1DQ11, Sch name = AN2 #NET "Q_an<3>" LOC = "P17" | IOSTANDARD = "LVCMOS33"; #Bank = 1, Pin name = IO_L49P_M1DQ10, Sch name = AN3 # Leds NET "led<0>" LOC = "U16" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L2P_CMPCLK, Sch name = LD0 NET "led<1>" LOC = "V16" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L2N_CMPMOSI, Sch name = LD1 NET "led<2>" LOC = "U15" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L5P, Sch name = LD2 NET "led<3>" LOC = "V15" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L5N, Sch name = LD3 NET "led<4>" LOC = "M11" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L15P, Sch name = LD4 NET "led<5>" LOC = "N11" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L15N, Sch name = LD5 NET "led<6>" LOC = "R11" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L16P, Sch name = LD6 NET "led<7>" LOC = "T11" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L16N_VREF, Sch name = LD7 # Switches NET "sw<0>" LOC = "T10" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L29N_GCLK2, Sch name = SW0 NET "sw<1>" LOC = "T9" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L32P_GCLK29, Sch name = SW1 NET "sw<2>" LOC = "V9" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L32N_GCLK28, Sch name = SW2 NET "sw<3>" LOC = "M8" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L40P, Sch name = SW3 NET "sw<4>" LOC = "N8" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L40N, Sch name = SW4 NET "sw<5>" LOC = "U8" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L41P, Sch name = SW5 NET "sw<6>" LOC = "V8" | IOSTANDARD = "LVCMOS33"; #Bank = 2, Pin name = IO_L41N_VREF, Sch name = SW6 NET "sw<7>" LOC = "T5" | IOSTANDARD = "LVCMOS33"; #Bank = MISC, Pin name = IO_L48N_RDWR_B_VREF_2, Sch name = SW7 ## Buttons NET "Rst" LOC = "B8" | IOSTANDARD = "LVCMOS33"; #Bank = 0, Pin name = IO_L33P, Sch name = BTNS ## version microcontroleur seul INST prgmem/pe_0 LOC = RAMB16_X0Y0; INST prgmem/pe_1 LOC = RAMB16_X1Y4; INST prgmem/pe_2 LOC = RAMB16_X0Y2; INST prgmem/pe_3 LOC = RAMB16_X1Y6;Intéressez-vous aux seules lignes non commentées.
Cœur complet
[modifier | modifier le wikicode]Recherchez ICI chez Opencores.org et prenez le fichier mcu_core.vhd
Nous ne le reproduisons pas ici car nous n'y avons fait aucune modification.
Microcontroleur complet
[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;
---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity microcontroleur 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);
Aff7segs : out STD_LOGIC_VECTOR (7 downto 0));
-- O_reg_pc : out std_logic_vector(15 downto 0));
end microcontroleur;
architecture microcontroleur_architecture of microcontroleur 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)
-- DEBUG
O_reg_pc : out std_logic_vector(15 downto 0)
);
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;
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,
-- DEBUG
O_reg_pc => open
);
prgmem : pm port map (
Clk => clk,
Rst => Rst,
-- 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" (0X3B)
Aff7segs <= 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;
-- managing FIFO
-- L_WE_UART <= IO_wr when (IO_A = UDR) else '0'; -- write UART UDR
-- L_RD_UART <= IO_rd when (IO_A = UDR) else '0'; -- read UART UDR
end microcontroleur_architecture;
Il ne vous manque maintenant que les outils pour travailler du point de vue logicielle.
Outils pour programmer
[modifier | modifier le wikicode]Si vous voulez utiliser l'outil Xilinx data2mem, il vous faut le fichier bmm. En voici un pour la carte Nexys 3 :
// BMM LOC annotation file. // // Release 14.5 - P.58f, build 3.0.7 Mar 3, 2013 // Copyright (c) 1995-2015 Xilinx, Inc. All rights reserved. /////////////////////////////////////////////////////////////////////////////// // // Address space 'prgmem' 0x00000000:0x00001FFF (8 KBytes). // /////////////////////////////////////////////////////////////////////////////// ADDRESS_SPACE prgmem RAMB16 [0x00000000:0x00001FFF] BUS_BLOCK prgmem/pe_1 RAMB16 [7:4] [0:4095] PLACED = X1Y4; prgmem/pe_0 RAMB16 [3:0] [0:4095] PLACED = X0Y0; prgmem/pe_3 RAMB16 [15:12] [0:4095] PLACED = X1Y6; prgmem/pe_2 RAMB16 [11:8] [0:4095] PLACED = X0Y2; END_BUS_BLOCK; END_ADDRESS_SPACE;
et voici un exemple de script pour assembler et télécharger :
#!/bin/bash
#export PATH=$PATH:/usr/bin
export PATH=$PATH:/opt/Xilinx/14.5/ISE_DS/ISE/bin/lin64:/usr/local/bin
cp ../microcontroleur.bit .
avr-gcc -mmcu=attiny861 Td7exo5.S -o Td7exo5.elf
avr-objdump -h -S Td7exo5.elf > Td7exo5.lss
data2mem -bm memoryS6.bmm -bd Td7exo5.elf -bt microcontroleur.bit -o uh microcontroleur
djtgcfg prog -d Nexys3 --index 0 --file microcontroleur_rp.bit
que vous devrez adapter à votre configuration. Nous travaillons toujours dans un sous répertoire du projet que l’on appelle soft. Cela explique pourquoi le script commence par aller chercher le fichier .bit dans un répertoire parent (cp ../microcontroleur.bit .). Ce script compile (assemble plutôt) un fichier appelé Td7exo5.S met le programme dans le fichier .bit pour le télécharger avec l'outil de Digilent djtgcfg.
ANNEXE II : les fichiers nécessaires aux TPs de ce chapitre (Version pour Spartan 3E)
[modifier | modifier le wikicode]Le fichier global microcontroleur.vhd de la section précédente est identique.
Notre mémoire donnée
[modifier | modifier le wikicode]La mémoire donnée VHDL ci-dessous peut être utilisée sans problème. Elle est réalisée avec des RAMB16_S4 propre à Xilinx et ne peut donc pas être portée telle quelle.
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
signal add : std_logic_vector(11 downto 0);
signal DO,DI : std_logic_vector(7 downto 0);
signal we,en,ssr : std_logic;
begin
add <= "000" & addr(8 downto 0);
dataread <= DO;
DI <= datawrite;
ssr <= '0';
we <= '1' when (rd='0' and wr='1') else '0';
en <= '1'; -- when (rd='1' or wr='1') else '0';
DM_RAMB16_S4_inst_MSB : RAMB16_S4
generic map (
INIT => X"0", -- Value of output RAM registers at startup
SRVAL => X"0", -- Output value upon SSR assertion
WRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
-- The following INIT_xx declarations specify the initial contents of the RAM
-- Address 0 to 1023
INIT_00 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_01 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_02 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_03 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_04 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_05 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_06 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_07 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_08 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_09 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 1024 to 2047
INIT_10 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_11 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_12 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_13 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_14 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_15 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_16 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_17 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_18 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_19 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 2048 to 3071
INIT_20 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_21 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_22 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_23 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_24 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_25 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_26 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_27 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_28 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_29 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 3072 to 4095
INIT_30 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_31 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_32 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_33 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_34 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_35 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_36 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_37 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_38 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_39 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3F => X"0000000000000000000000000000000000000000000000000000000000000000")
port map (
DO => DO(7 downto 4), -- 4-bit Data Output
ADDR => ADD, -- 12-bit Address Input
CLK => CLK, -- Clock
DI => DI(7 downto 4), -- 4-bit Data Input
EN => EN, -- RAM Enable Input
SSR => SSR, -- Synchronous Set/Reset Input
WE => WE -- Write Enable Input
);
DM_RAMB16_S4_inst_LSB : RAMB16_S4
generic map (
INIT => X"0", -- Value of output RAM registers at startup
SRVAL => X"0", -- Output value upon SSR assertion
WRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGE
-- The following INIT_xx declarations specify the initial contents of the RAM
-- Address 0 to 1023
INIT_00 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_01 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_02 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_03 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_04 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_05 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_06 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_07 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_08 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_09 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_0F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 1024 to 2047
INIT_10 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_11 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_12 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_13 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_14 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_15 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_16 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_17 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_18 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_19 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_1F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 2048 to 3071
INIT_20 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_21 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_22 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_23 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_24 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_25 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_26 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_27 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_28 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_29 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_2F => X"0000000000000000000000000000000000000000000000000000000000000000",
-- Address 3072 to 4095
INIT_30 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_31 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_32 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_33 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_34 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_35 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_36 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_37 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_38 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_39 => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3A => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3B => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3C => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3D => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3E => X"0000000000000000000000000000000000000000000000000000000000000000",
INIT_3F => X"0000000000000000000000000000000000000000000000000000000000000000")
port map (
DO => DO(3 downto 0), -- 4-bit Data Output
ADDR => ADD, -- 12-bit Address Input
CLK => CLK, -- Clock
DI => DI(3 downto 0), -- 4-bit Data Input
EN => EN, -- RAM Enable Input
SSR => SSR, -- Synchronous Set/Reset Input
WE => WE -- Write Enable Input
);
end dm_architecture;
Cette mémoire de données est bien plus grande que celle du 861 du commerce (512 octets) puisqu'elle a une taille de 4 ko. Est-ce gênant ? Il est clair que c’est du gaspillage. Pour information, nous avons déjà utilisé des surplus de mémoire RAM à l'aide de pointeurs que l’on initialise nous-même. Si l’on tente de réaliser cela, il nous faudra modifier la première ligne de l'architecture :
begin
add <= "000" & addr(8 downto 0);
qui limite la mémoire RAM à 512 octets. Ceci est laissé comme exercice pour le lecteur.
Venons-en à la mémoire programme.
Notre mémoire programme
[modifier | modifier le wikicode]Commençons par donner la mémoire programme pour la carte Spartan 3E :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
library unisim;
use unisim.vcomponents.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
constant p0_00 : BIT_VECTOR := X"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
begin
pe_0 : RAMB16_S4 ---------------------------------------------------------
generic map(
INIT_00 => p0_00, INIT_01 => p0_00, INIT_02 => p0_00, INIT_03 => p0_00,
INIT_04 => p0_00, INIT_05 => p0_00, INIT_06 => p0_00, INIT_07 => p0_00,
INIT_08 => p0_00, INIT_09 => p0_00, INIT_0A => p0_00, INIT_0B => p0_00,
INIT_0C => p0_00, INIT_0D => p0_00, INIT_0E => p0_00, INIT_0F => p0_00,
INIT_10 => p0_00, INIT_11 => p0_00, INIT_12 => p0_00, INIT_13 => p0_00,
INIT_14 => p0_00, INIT_15 => p0_00, INIT_16 => p0_00, INIT_17 => p0_00,
INIT_18 => p0_00, INIT_19 => p0_00, INIT_1A => p0_00, INIT_1B => p0_00,
INIT_1C => p0_00, INIT_1D => p0_00, INIT_1E => p0_00, INIT_1F => p0_00,
INIT_20 => p0_00, INIT_21 => p0_00, INIT_22 => p0_00, INIT_23 => p0_00,
INIT_24 => p0_00, INIT_25 => p0_00, INIT_26 => p0_00, INIT_27 => p0_00,
INIT_28 => p0_00, INIT_29 => p0_00, INIT_2A => p0_00, INIT_2B => p0_00,
INIT_2C => p0_00, INIT_2D => p0_00, INIT_2E => p0_00, INIT_2F => p0_00,
INIT_30 => p0_00, INIT_31 => p0_00, INIT_32 => p0_00, INIT_33 => p0_00,
INIT_34 => p0_00, INIT_35 => p0_00, INIT_36 => p0_00, INIT_37 => p0_00,
INIT_38 => p0_00, INIT_39 => p0_00, INIT_3A => p0_00, INIT_3B => p0_00,
INIT_3C => p0_00, INIT_3D => p0_00, INIT_3E => p0_00, INIT_3F => p0_00)
port map(ADDR => PM_A(11 downto 0),
CLK => CLK,
DI => "0000",
EN => '1',
SSR => '0',
WE => '0',
DO => PM_Drd(3 downto 0));
pe_1 : RAMB16_S4 ---------------------------------------------------------
generic map(
INIT_00 => p0_00, INIT_01 => p0_00, INIT_02 => p0_00, INIT_03 => p0_00,
INIT_04 => p0_00, INIT_05 => p0_00, INIT_06 => p0_00, INIT_07 => p0_00,
INIT_08 => p0_00, INIT_09 => p0_00, INIT_0A => p0_00, INIT_0B => p0_00,
INIT_0C => p0_00, INIT_0D => p0_00, INIT_0E => p0_00, INIT_0F => p0_00,
INIT_10 => p0_00, INIT_11 => p0_00, INIT_12 => p0_00, INIT_13 => p0_00,
INIT_14 => p0_00, INIT_15 => p0_00, INIT_16 => p0_00, INIT_17 => p0_00,
INIT_18 => p0_00, INIT_19 => p0_00, INIT_1A => p0_00, INIT_1B => p0_00,
INIT_1C => p0_00, INIT_1D => p0_00, INIT_1E => p0_00, INIT_1F => p0_00,
INIT_20 => p0_00, INIT_21 => p0_00, INIT_22 => p0_00, INIT_23 => p0_00,
INIT_24 => p0_00, INIT_25 => p0_00, INIT_26 => p0_00, INIT_27 => p0_00,
INIT_28 => p0_00, INIT_29 => p0_00, INIT_2A => p0_00, INIT_2B => p0_00,
INIT_2C => p0_00, INIT_2D => p0_00, INIT_2E => p0_00, INIT_2F => p0_00,
INIT_30 => p0_00, INIT_31 => p0_00, INIT_32 => p0_00, INIT_33 => p0_00,
INIT_34 => p0_00, INIT_35 => p0_00, INIT_36 => p0_00, INIT_37 => p0_00,
INIT_38 => p0_00, INIT_39 => p0_00, INIT_3A => p0_00, INIT_3B => p0_00,
INIT_3C => p0_00, INIT_3D => p0_00, INIT_3E => p0_00, INIT_3F => p0_00)
port map(ADDR => PM_A(11 downto 0),
CLK => CLK,
DI => "0000",
EN => '1',
SSR => '0',
WE => '0',
DO => PM_Drd(7 downto 4));
pe_2 : RAMB16_S4 ---------------------------------------------------------
generic map(
INIT_00 => p0_00, INIT_01 => p0_00, INIT_02 => p0_00, INIT_03 => p0_00,
INIT_04 => p0_00, INIT_05 => p0_00, INIT_06 => p0_00, INIT_07 => p0_00,
INIT_08 => p0_00, INIT_09 => p0_00, INIT_0A => p0_00, INIT_0B => p0_00,
INIT_0C => p0_00, INIT_0D => p0_00, INIT_0E => p0_00, INIT_0F => p0_00,
INIT_10 => p0_00, INIT_11 => p0_00, INIT_12 => p0_00, INIT_13 => p0_00,
INIT_14 => p0_00, INIT_15 => p0_00, INIT_16 => p0_00, INIT_17 => p0_00,
INIT_18 => p0_00, INIT_19 => p0_00, INIT_1A => p0_00, INIT_1B => p0_00,
INIT_1C => p0_00, INIT_1D => p0_00, INIT_1E => p0_00, INIT_1F => p0_00,
INIT_20 => p0_00, INIT_21 => p0_00, INIT_22 => p0_00, INIT_23 => p0_00,
INIT_24 => p0_00, INIT_25 => p0_00, INIT_26 => p0_00, INIT_27 => p0_00,
INIT_28 => p0_00, INIT_29 => p0_00, INIT_2A => p0_00, INIT_2B => p0_00,
INIT_2C => p0_00, INIT_2D => p0_00, INIT_2E => p0_00, INIT_2F => p0_00,
INIT_30 => p0_00, INIT_31 => p0_00, INIT_32 => p0_00, INIT_33 => p0_00,
INIT_34 => p0_00, INIT_35 => p0_00, INIT_36 => p0_00, INIT_37 => p0_00,
INIT_38 => p0_00, INIT_39 => p0_00, INIT_3A => p0_00, INIT_3B => p0_00,
INIT_3C => p0_00, INIT_3D => p0_00, INIT_3E => p0_00, INIT_3F => p0_00)
port map(ADDR => PM_A(11 downto 0),
CLK => CLK,
DI => "0000",
EN => '1',
SSR => '0',
WE => '0',
DO => PM_Drd(11 downto 8));
pe_3 : RAMB16_S4 ---------------------------------------------------------
generic map(
INIT_00 => p0_00, INIT_01 => p0_00, INIT_02 => p0_00, INIT_03 => p0_00,
INIT_04 => p0_00, INIT_05 => p0_00, INIT_06 => p0_00, INIT_07 => p0_00,
INIT_08 => p0_00, INIT_09 => p0_00, INIT_0A => p0_00, INIT_0B => p0_00,
INIT_0C => p0_00, INIT_0D => p0_00, INIT_0E => p0_00, INIT_0F => p0_00,
INIT_10 => p0_00, INIT_11 => p0_00, INIT_12 => p0_00, INIT_13 => p0_00,
INIT_14 => p0_00, INIT_15 => p0_00, INIT_16 => p0_00, INIT_17 => p0_00,
INIT_18 => p0_00, INIT_19 => p0_00, INIT_1A => p0_00, INIT_1B => p0_00,
INIT_1C => p0_00, INIT_1D => p0_00, INIT_1E => p0_00, INIT_1F => p0_00,
INIT_20 => p0_00, INIT_21 => p0_00, INIT_22 => p0_00, INIT_23 => p0_00,
INIT_24 => p0_00, INIT_25 => p0_00, INIT_26 => p0_00, INIT_27 => p0_00,
INIT_28 => p0_00, INIT_29 => p0_00, INIT_2A => p0_00, INIT_2B => p0_00,
INIT_2C => p0_00, INIT_2D => p0_00, INIT_2E => p0_00, INIT_2F => p0_00,
INIT_30 => p0_00, INIT_31 => p0_00, INIT_32 => p0_00, INIT_33 => p0_00,
INIT_34 => p0_00, INIT_35 => p0_00, INIT_36 => p0_00, INIT_37 => p0_00,
INIT_38 => p0_00, INIT_39 => p0_00, INIT_3A => p0_00, INIT_3B => p0_00,
INIT_3C => p0_00, INIT_3D => p0_00, INIT_3E => p0_00, INIT_3F => p0_00)
port map(ADDR => PM_A(11 downto 0),
CLK => CLK,
DI => "0000",
EN => '1',
SSR => '0',
WE => '0',
DO => PM_Drd(15 downto 12));
end architecture Arch;
Cette mémoire programme a une taille de 8 ko, ce qui est sa taille dans le Tiny861 du commerce.
Les contraintes dans le fichier ucf peuvent être :
INST prgmem/pe_0 LOC = RAMB16_X1Y5 ; INST prgmem/pe_1 LOC = RAMB16_X1Y6 ; INST prgmem/pe_2 LOC = RAMB16_X1Y4 ; INST prgmem/pe_3 LOC = RAMB16_X1Y3 ;
et elles doivent être associées au fichier attiny861.bmm suivant :
ADDRESS_SPACE prgmem RAMB16 [0x00000000:0x00001FFF] BUS_BLOCK prgmem/pe_1 [7:4] PLACED = X1Y6; prgmem/pe_0 [3:0] PLACED = X1Y5; prgmem/pe_3 [15:12] PLACED = X1Y3 ; prgmem/pe_2 [11:8] PLACED = X1Y4; END_BUS_BLOCK; END_ADDRESS_SPACE;
Cœur complet
[modifier | modifier le wikicode]Recherchez ICI chez Opencores.org et prenez le fichier mcu_core.vhd
Nous ne le reproduisons pas ici car nous n'y avons fait aucune modification.
Script d'assemblage
[modifier | modifier le wikicode]Nous avons laissé un certain nombre de commentaires qui montrent que nous utilisons une carte Nexys3 et l'ISE 14.5, mais que pour les besoins de l'enseignement, l'ISE 11.1 est utilisé avec une carte Spartan3E500
#!/bin/bash
#export PATH=$PATH:/usr/bin
#export PATH=$PATH:/opt/Xilinx/14.5/ISE_DS/ISE/bin/lin64:/usr/local/bin
export PATH=$PATH:/usr/bin:/opt/Xilinx/11.1/ISE/bin/lin
cp ../microcontroleur.bit .
avr-gcc -mmcu=attiny861 tp7.S -o tp7.elf
avr-objdump -h -S tp7.elf > tp7.lss
data2mem -bm attiny861.bmm -bd tp7.elf -bt microcontroleur.bit -o uh microcontroleur
#djtgcfg prog -d Nexys3 --index 0 --file microcontroleur_rp.bit
djtgcfg prog -d DCabUsb --index 0 --file microcontroleur_rp.bit
Commentez la dernière ligne si vous n'avez pas le câble USB JTAG et utilisez alors IMPACT pour télécharger!