Utilisateur:SergeMoutou

Une page de Wikiversité.
Aller à : navigation, rechercher

Voir aussi Site web personnel

France
France
Âge
Mardi 1 août 2017
Enseignant(e) au tableau
Enseignant(e)
fr
en-2
Physique

Serge Moutou est né en 1956 à Saint-Dizier (52)

Après un DEA de physique (méthodes d'analyses des structures de la matière) en 1981, j'entame une thèse de 3° cycle jamais soutenue.

Certifié en 1983 en physique chimie, mes centres d'intérêts vont se déplacer de la physique vers l'électronique numérique et l'informatique. J'obtiens une agrégation de physique appliquée en 1990. Serge Moutou enseigne à l'IUT de Troyes (Université de reims) depuis 1989 dans le département Génie électrique et Informatique Industrielle et peut être contacté : serge.moutou-at-univ-reims.fr en remplaçant -at- par @ bien sûr !

Travail en cours[modifier | modifier le wikicode]

Mon travail de documentation commence en 2004 avec la programmation OpenOffice qui va être transformé en un WIKI (privé) en 2006 :

Ce travail va me permettre de me familiariser avec les WIKI. Ainsi un certain nombre de projets WIKI sont commencés en parallèle :

  • Logique combinatoire (WikiBook) commencé en février 2006. Il manque encore de pédagogie, et d'exercices corrigés.
  • Logique séquentielle (Wikiversité) commencé en février 2006. Il manque encore de pédagogie, et peu d'exercices sont corrigés.
  • Conception et VHDL (WikiBook) commencé en août 2006 et toujours en évolution. J’ai en projet d'y ajouter un chapitre sur les mémoires (dans les FPGA)
  • Utiliser les PIC 16F et 18F commencé en janvier 2011. Doit être complété par l'interfaçage à des circuits externes. Ce sera probablement fait d'ici 2012 car je vais modifier mes TDs pour retirer la partie assembleur et la remplacer par de la circuiterie extérieure... et donc en faire profiter ce cours.
  • Transferts thermiques (WikiBook) commencé en 2007 mis à jour en 2011. Il n’est pas encore assez pédagogique à mon goût pour un wikibook ! Mais Wikipédia est tellement bien documenté sur le sujet que j'avoue ne pas bien savoir comment poursuivre ce livre.
  • VHDL avancé (Wikiversité) pour les processeurs softcore commencé en 2007 pour la partie TP et mis à jour en 2010 pour la partie cours. Probablement un de mes cours les plus abouti, mais il y encore à faire et c’est là que je passe le plus clair de mon temps.
  • Automatisme : norme 1131-3 et GRAFCET. Un cours d'automatisme et GRAFCET avec exercices (en cours de rédaction commencé le 26 juillet 2011)
  • Systèmes du premier ordre est un projet dont je ne suis pas à l'origine. Mais j’ai décidé en juillet 2012 de le compléter. Il n'avait que deux chapitres très courts quand je l'ai repris, il en possède maintenant neuf en cours de rédaction (SergeMoutou (discussion) 27 juillet 2012 à 15:51 (UTC)).
  • Micro contrôleurs AVR commencé en décembre 2012... démarre tout doucement. La bonne nouvelle est que nous allons probablement changer nos enseignements et passer du PIC à l'AVR (en 2013/2014) ce qui accélérera un peu la rédaction.

Cet ensemble de projets est encore mis à jour petit à petit aujourd'hui, en fait au rythme de mes cours d'amphi. Espérons qu’ils seront complétés par d'autres bénévoles...

Travail futur[modifier | modifier le wikicode]

J’ai encore des projets dans mes cartons mais seul l'avenir dira s'ils seront réalisés ou pas.

  • Un cours d'architecture des systèmes répartis, peut-être car plus enseigné aujourd'hui.
  • Un cours sur la cryptographie
  • J’aimerai bien écrire des choses sur la symétrie et la théorie des groupes... mais je n'enseigne pas dans ce domaine et je ne possède aucun document informatique. Si je commence ce projet il prendra beaucoup de temps.

Ne pas lire[modifier | modifier le wikicode]

Je laisse dans cette section des choses importantes pour moi que je suis sûr de retrouver mais qui n'intéressent certainement pas grand monde (même probablement personne).

quelques indications pour plus tard pour l'auteur ! Lecteur, s'abstenir, ne pas lire !!!

Projets AVR[modifier | modifier le wikicode]

Realisation SMP[modifier | modifier le wikicode]

Le programme :

fonctionne à peu près comme un bootloader, sauf qu’il charge le programme en adresse 0x800. Il faut donc changer mais nous mettreons un peu de temps...

Le programme Arduino ci-dessous peut être téléchargé et fonctionne à peu près :

int main(){
  PORTC=0xFF;
  while(1){
    long int nb;
    PORTC=0xFF;
    for(nb=0;nb<2000000;nb++)
      PORTB=3;
    PORTC=0x00;
    for(nb=0;nb<2000000;nb++)
      PORTB=5;
  }
  return 0;
}

On perçoit un clignottement mais pas de toutes les LEDs !!!!???

Correspondance avec Juergen[modifier | modifier le wikicode]

Quelques mails (en anglais) sont déposés ici pour être sûr de ne pas les perdre... mais ils n'intéressent personne. Je les ferai disparaître quand les problèmes évoqués seront résolus.

Hi Serge,

I'll try to help you, but my PC broke down some time ago and I haven't restored all development environments yet.
And my wife has complained about too much hardware on my desk. I can try to locate problems but I may need to ask you
to test that on real FPGAs.
-------------------------
Regarding RETI, the AVR manual does not say anything about clearing interrupt vectors. I have attached the version that
I am using (there could be a newer one that I am not aware of). According to my version of the document, RETI is RET plus
setting the I-flag to 1. The theory is then this:

opc_deco.vhd line 459 ff:

                     when "0001" =>  -- RETI
                          Q_ALU_OP <= ALU_INTR;
                          Q_IMM(6) <= '1';
                          Q_AMOD <= AMOD_iiSP;
                          Q_RD_M <= I_T0;
                          Q_WE_F    <= not I_T0;  -- I flag
                          Q_WE_XYZS <= not I_T0;
                          if (I_T0 = '0') then
                             Q_PC_OP <= PC_LD_S;
                          end if;

We set  Q_WE_F  to 1 (after T0) so that the flag register will be written in T1.
We set Q_ALU_OP to ALU_INTR and  Q_IMM(6)  to  1 (=the new value of the I flag).
In the alu we then have:

alu.vhd line 259 ff:

           when ALU_INTR =>
                L_DOUT <= I_PC;
                Q_FLAGS(7) <= I_IMM(6);    -- ena/disable interrupts


The rest of RETI in opc_deco.vhd is the same as for RET (popping the PC from the stack in the T0 cycle).

Normally there is no need to modify the interrupt vector (in program memory) after an interrupt. And normally the interrupting device (like your
timer) has some operation that resets the interrupt request line towards the CPU and the operation that resets the interrupt
request line is called from the interrupt service routine. What is probably missing right now is a way to clear L_INTVEC in io.vhd (process ioint).
This is more a task for your timer than for the RETI instruction (which cannot know which interrupt to reset). That is, one of your timer
registers (typically the one that reads the current value) should clear L_INTVEC but only if the currently active interrupt (as per L_INTVEC) is
the timer interrupt, and that register should be accessed by your interrupt service routine before it returns via RETI. Generally speaking if you
add devices in io.vhd and if these devices generate interrupts, then the ioint process must be adjusted to take care of the new interrupts
(and their priorities) and the devices must have means to reset the 'interrupt line' from the device to the ioint process.
------------------
Regarding SPM that is a multi-variant opcode with device specific properties. A few things such as page erase are not applicable
because the FPGA uses RAM instead of flash ROM so there is no need for page erase. The other functions (apart from writing a
single byte to flash) are not possible to do in one or two cycles. And it uses registers that are not available on all devices.

It is probably much simpler to modify the bootloader so that it uses a small loop over the page data. Another approach could
be a DMA peripheral that writes to the program memory (but since this can interfere with the CPU operations it will not be much
simpler than modifying the SPM opcode).
------------------

Best Regards,
Jürgen Sauermann
Hi Serge,

...
> I agree with your analysis : we don't have to change the interrupt vector in program memory. In fact I never read how works your interrupt mechanism but only the IOINT process to make my own interrupt working. I didn't see, for instance, that when enterring in interrupt the global Interrupt flag is cleared and setting back to 1 with RETI. Did you agree ?
>
> If I read your io.vhd I am sure this global interrupt flag is not available... and then cannot imagine automatic clearing of a Flag (not a vector interrupt but TOV0 for instance).
>

There is one signal from io to the cpu which is related to interrupt: I_INTVEC(5 downto 0) with different prefixes in different modules.
This signal corresponds 1:1 to the interrupt vectors of the CPU (which have to be set up properly, of course).

If opc_fetch.vhd sees this signal at line 195 ff then it inserts a "pseudo opcode" into the CPU's instruction stream:

    Q_OPC <= X"00000000" when (L_INVALIDATE = '1')
        else P_OPC       when (I_INTVEC(5) = '0')
        else (X"000000" & "00" & I_INTVEC);     -- "interrupt opcode"

If opc_deco.vhd sees this opcode then it pushes the PC etc. and wirites the status register of the CPU
with 0. There is no extra interrupt flag in the cpu - the I-bit of the status register does it. One thing I did not
mention earlier was that bit 5 of the interrupt vector must be 1 (to distinguish the interrupt opcode from NOP.


                        -- 0000 0000 0000 0000 - NOP
                        -- 0000 0000 001v vvvv - INTERRUPT
                        --
                        if (I_OPC(5)) = '1' then   -- interrupt
                            Q_ALU_OP <= ALU_INTR;
                            Q_AMOD <= AMOD_SPdd;
                            Q_JADR <= "0000000000" & I_OPC(4 downto 0) & "0";
                            Q_PC_OP <= PC_LD_I;
                            Q_WE_F <= '1';      -- clear I-flag
                            Q_WE_M <= "11";     -- write return address
                            Q_WE_XYZS <= '1';   -- write new SP
                        end if;

The prerequisites are, as usual: proper interrupt vector in the program memory (pointing to the ISR), and interrupts
 enabled in the CPU status register.

I suppose that it is possible to "emulate" the interrupt behavior by inserting an interrupt instruction into the program
code. With that you may be able to narrow down where the problem is (in io.vhd or in the cpu)

The "global Interrupt flag"  (= the I-bit in the CPU status register) is cleared by Q_WE_F <= '1' and Q_IMM(6) <= '0'
(which is the default) when an interrupt opcode is executed, and set by Q_WE_F <= '1' and Q_IMM(6)  <= '1' in RETI.

The interworking between io.vhd and the CPU is then (supposed to be) this:

1. Initially INTVEC is 0 (no interrupt pending).
2. some device asserts its interrupt line
3. ioint sets INTVEC to the vector corresponding to the interrupt line with the highest priority. It is important that ioint does not
   change INTVEC until the interrupt line is cleared (by the ISR) This is because ioint cannot know when the CPU handles the interrupt.
4. opc_fetch inserts an interrupt opcode into the CPU's instruction stream
5. opc_deco detects the interrupt opcode and jumps to the corresponding ISR (and pushes the PC, and clears the I-bit).
6. The ISR reads or writes the device in order to clear the interrupt line
7. The ISR returns with RETI, setting the I-bit again.

/// Jürgen
Hi Serge,

attached is a tar file with my changes to fix the SPM instruction.
I have tried to SVN check-in the code to opencores.org but my account is broken and
they don't respond.

If you use your own program memory modules then you should add two signals
I_PM_WE (write enable) and I_PM_DIN(15 downto 0) to your modules.

As I said, I can't test this myself, so please let me know know it works.

/// Jürgen
Le 02/07/2015 19:50, Juergen Sauermann a écrit :
> Hi Serge,
> 
> you are right - the code cannot compile. As I said I do not have a
> running development environment right now.
> 
> I agree that a RAMB4_S4_S2 would be needed, but then the B port
> becomes a problem because
> the DOB needs the same size as DIB. Ideally we would have 4-bit DOB
> and 2-bit DIB but that is
> not a valid configuration.
> 
> I believe this is primarily caused by the LPM being 8-bit read while
> SPM is 16-bit write. For this to
> work one would need a 16-bit read/write and a mux after that for LPM
> to select the proper byte.
> 
> I have used SPM successfully in my own code (also having a loader
> program). However I did an 8-bit SPM that works
> exactly like LPM (see attached file). Note also that the ALU_OP in
> OPC_DECO.VHD is different for 8-bit and 16-bit SPMs.
> You can use that with word arrays but you have to write them byte by
> byte (= 2 SPM calls per word).
> 
> I would also say that using  RAMB4_S4_S2  is very tricky. A simpler
> approach is probably this:
> 
> PROG_MEM.VHD:
>     Use  RAMB4_S4_S4 or similar,
>     16-bit DIA and DIB (internally),
>     16-bit DOA and DOB (internally),
>     replicate 8-bit I_PM_DIN  to 16-bit DIB
>    output mux at DOB for LPM
> 
> If you want to give it a try, I'd be happy to have a look at it and
> give comments. And no worries about my time, I have
> retired so I have got time for it. Thee worst thing that can happen is
> that my reponse is a little slow.
> 
> /// Jürgen
Le 03/07/2015 15:46, Juergen Sauermann a écrit :
> Hi Serge,
> 
> I see. The relevant sentence in the AVR manual is probably this:
> 
> _THE OCF0A FLAG IS AUTOMATICALLY__ _cleared when the interrupt is
> executed.
> Alternatively, the OCF0A flag can be cleared by software_ BY WRITING A
> LOGICAL ONE TO ITS I/O BIT LOCATION._
> 
> So the method I was talking about was the second one (_WRITING A
> LOGICAL ONE TO ITS I/O BIT LOCATION._) while
> you were referring to the first one (_THE OCF0A FLAG IS
> AUTOMATICALLY__ __CLEARED WHEN THE INTERRUPT IS EXECUTED._).
> 
> That was actually not invented by AVR but already known as interrupt
> acknowledge (or INT_ACK) by the Motorola MC68020 CPU
> (and maybe even earlier). The proper place for generating INT_ACK is
> in OPC_DECO.VHD, but not in RETI but in the INTERRUPT
> pseudo opcode. This is because INT_ACK should be generated at the
> beginning of the interrupt processing and not  at its end.
> 
> Adding INT_ACK in OPC_DECO.VHD is relatively simple:
> 
> 1. Add an output INT_ACK to entity opc_deco
> 
> entity opc_deco is
> ...
>               Q_INT_ACK                    : out   std_logic;
> ...
> 
> 2. Set INT_ACK to 0 by default (around LINE 72 FF):
> 
>                 ALU <= ALU_NOP;
>                 DDDDD <= Rd;
>                 RRRRR <= Rr;
>                 IMM <= X"0000";
>                 Q_INT_ACK <= '0';
>                 PC_OP <= PC_NEXT;
> 
> 3. Set  INT_ACK to 1 when the INTERRUPT opcode is executed (LINE 95
> FF):
> 
>                     when "00" =>
>                         --
>                         -- 0000 0000 0000 0000 - NOP
>                         -- 0000 0000 001v vvvv - INTERRUPT
>                         --
>                         if (I_OPC(5)) = '1' then   -- interrupt
>                             Q_ALU_OP <= ALU_INTR;
>                             Q_AMOD <= AMOD_ddSP;
>                             Q_JADR <= "0000000000" & I_OPC(4 downto 0)
> & "0";
>                             Q_PC_OP <= PC_LD_I;
>                             Q_WE_F <= '1';
>                             Q_WE_M <= "11";
>                             Q_INT_ACK <= '1';    -- ACKNOWLEDGE THE
> INTERRUPT
>                         end if;
> 
> That  sets Q_INT_ACK for one cycle and can be used to reset the
> interrupt source. In theory one could
> have created one INT_ACK per possible interrupt vector instead of one
> INT_ACK for all vectors, but since
> most interrupts are non-automatic, I wouldn't do that here but rather
> in the IOINT process.
> 
> You can now connect INT_ACK to the IOINT process which then matches it
> with the currently pending INTVEC
> in order to generate the proper per-interrupt-source INT_ACK.
> 
> /// Jürgen

Très important : à décortiquer :

Le 19/07/2015 Juergen a écrit :
Hi Serge,

I had a first look at your code and noticed a few things that I would like to
mention. Not that it will work then but at least a start:

opc_deco.vhd:

I believe ALU_OP is wrong. ALU_OP determines how the data that you write
to memory is computed. Your current value of ALU_D_MV_Q causes R0 (because Q_DDDDD <= "00000")
to be copied to the upper and lower bytes of the ALU output. So you are writing R0:R0 instead of R1:R0.
Instead you should set Q_ALU_OP <= ALU_MV_16; which writes the register pair selected by
Q_DDDDD to the ALU output.

I would also set Q_WE_M <= "11"; to indicate a word-write instead of a byte-write (even though it is up
to the memory module how it interprets Q_WE_M). So the current "01" may or may not work, but "11" is
always cleaner.

prog_mem.vhd:

The L_WE_E_H etc signal and friends look wrong. My understanding would be that the letters
H and L mean the high and low bytes of a 16-bit word, while the letters E and O mean the even and
odd 16-bit addresses of a 32-bit word. Therefore the H/L selection is made by ADR(0) while the O/E
selection is made by ADR(1). In your code it look the other way around.

The WE_ signals in prog_mem.vhd are related to the Q_WE_M of opcode_deco.vhd. There are,
in principle two variants that should bot work:

1. A single WE line for the program memory. This is what you currently have. In this
case the write is always a 16-bit write, ADR(0) is ignored, and  Q_WE_M (1) of opcode_deco is
also ignored. This also seems to be the Atmel understanding, whi say that ADR is supposed to be even.

2. Two WE lines for the odd and even bytes of the program memory. In that case 8-bit writes would also
be possible, ADR(0) would be decoded (like in your current code) and  Q_WE_M (1) is not ignored
(and should be "11" for SPM as I said above. There is no official Atmel opcode for 8-bit program memory writes.

I believe that your current code is a mix of 1. and 2. which is maybe not that good.

A final note on testing this: If you write, say, AA 55 AA 55 AA 55 then it is difficult so figure what goes wrong.
AA 55 ...  is good for checking if your addresses are roughly OK, but details are difficult to see.
I would, in addition, test a single write and also a sequence like F0 C1 F2 C3 F4... to see exactly which
 byte was written in which instruction.

/// Jürgen

Ancien programme d'essai[modifier | modifier le wikicode]

On vous propose dans cette section un moyen simple de tester si vous allez pouvoir réaliser votre bootloader avec la version de l'ATMega8 dont vous disposez. Il s'agit d'un programme simple fonctionnant avec un terminal RS232 (hyperterminal ou GTKTerm) que nous présentons maintenant :

#include "avr/io.h"
#include <avr/interrupt.h>
#include "avr/pgmspace.h" // pour acces à la mémoire programme
 
#undef F_CPU
#define F_CPU 25000000UL
 
inline void pgm_write_byte(uint16_t addr, uint8_t value)
{
    __asm__ volatile (
        "push r0"       "\n\t"
        "push r30"       "\n\t"
        "push r31"       "\n\t"
        "movw r30, %0"   "\n\t"
        "mov r0, %1"     "\n\t"
        "spm"            "\n\t"
        "pop r31"       "\n\t"
        "pop r30"       "\n\t"
        "pop r0"        "\n\t"
        :: "w" (addr), "r" (value)
        );
}

//************************************************************************
// function uart_init()
// purpose: init first rs232 PORT
// arguments:
// no argument
// return:
// note: 38400,8,n,2 hard coded : transmission and reception
//************************************************************************
void uart_init(void) {
  UCSRB = (1<<TXEN)|((1<<RXEN)); // transmission et reception
}

     //----------------------------------------------------------------------//
    //                                                                      //
   //   print char cc on UART.                                             //
  //    return number of chars printed (i.e. 1).                          //
 //                                                                      //
//----------------------------------------------------------------------//
uint8_t uart_putc(uint8_t cc)
{
    while ((UCSRA & (1 << UDRE)) == 0)      ;
    UDR = cc;
    return 1;
}

     //----------------------------------------------------------------------//
    //                                                                      //
   //   print string s on UART.                                            //
  //    return number of chars printed.                                   //
 //                                                                      //
//----------------------------------------------------------------------//
uint16_t uart_puts(const char * s)
{
const char * from = s;
uint8_t cc;
    while ((cc = pgm_read_byte(s++)))   uart_putc(cc);
    return s - from - 1;
}

const char MSG[] PROGMEM ="Hello, World!\r\n\0";

int main(int argc, char * argv[])
{
    uint8_t i;
    i=0;
    uart_init();
    for (;;) {
             // print 'Hello world' on UART.
	uart_puts(MSG);
	while (!(UCSRA & (1<<RXC))); //attente donnée RS232
        pgm_write_byte(&MSG[i++], UDR);
    }
}

La caractéristique de ce programme est l’utilisation de donnée en mémoire programme dans le tableau MSG[] : il n’est pas inutile de savoir comment cela peut être fait avec le mot clef "PROGMEM" qui ne fait pas partie du langage C standard. Le programme principal vient quant à lui changer le contenu de cette mémoire au fur et à mesure que des données arrivent par la liaison série. Remarquez aussi que l’on dispose d'un sous-programme "pgm_read_byte" (disponible dans "avr/pgmspace.h") mais que le sous-programme "pgm_write_byte" doit être écrit par vos soins.

Si tout fonctionne correctement, la chaîne de caractères affichée dans votre terminal changera au fur et à mesure des caractères entrés au clavier.

Panneau d’avertissement

Ne pas dépasser la taille du tableau initial ! Il n'y a aucune vérification de sécurité dans ce programme ! N'oubliez pas que vous écrivez en mémoire programme ! Et que si vous écrasez des parties de programme, cela peut planter la bête ! Elle est encore fragile à cet âge là.

Il n'est peut-être pas inutile de rappeler que ce programme tourne correctement dans un FPGA mais qu'un ATMega8 "de la vraie vie" ne pourrait pas faire cela. Ceci est lié au fait que les mémoires dans les FPGA peuvent être écrites de n’importe où ce qui n’est pas le cas de la mémoire Flash d'un micro-contrôleur !

Fait pas grand chose[modifier | modifier le wikicode]

void loop()
{
  // Get the position of the line.  Note that we *must* provide
  // the "sensors" argument to read_line() here, even though we
  // are not interested in the individual sensor readings.
  unsigned int position = robot.readLine(sensors, IR_EMITTERS_ON);
  if ((position==4000) ||(position ==0))
    OrangutanMotors::setSpeeds(30, 30);
  //if (position >= 512) { // 2000 si 5 capteurs
  else {
    // We are far to the right of the line: turn left.

    // Set the right motor to 100 and the left motor to zero,
    // to do a sharp turn to the left.  Note that the maximum
    // value of either motor speed is 255, so we are driving
    // it at just about 40% of the max.
    OrangutanMotors::setSpeeds(0, 0);
    delay(100);
    OrangutanMotors::setSpeeds(-30, -30);
    delay(300);
    OrangutanMotors::setSpeeds(0, 0);
    delay(100);
    OrangutanMotors::setSpeeds(-30, 30);
    delay(300); 
    OrangutanMotors::setSpeeds(0, 0);
    delay(100);
  }
}

Sait sortir du labyrinthe mais pas dans tous les cas[modifier | modifier le wikicode]

typedef enum {DEBUT,TROUVELIGNE,TROUVESORTIE} typetat;
typetat state;
//......
void setup()
{
  unsigned int counter; // used as a simple timer
  state = DEBUT;
//....
void loop()
{
  // Get the position of the line.  Note that we *must* provide
  // the "sensors" argument to read_line() here, even though we
  // are not interested in the individual sensor readings.
  unsigned int position = robot.readLine(sensors, IR_EMITTERS_ON);
  int my_speed=-30;
  if (state == DEBUT) {
    if ((position==4000) ||(position ==0))
      OrangutanMotors::setSpeeds(30, 30);
  //if (position >= 512) { // 2000 si 5 capteurs
    else {
    // We are far to the right of the line: turn left.
 
    // Set the right motor to 100 and the left motor to zero,
    // to do a sharp turn to the left.  Note that the maximum
    // value of either motor speed is 255, so we are driving
    // it at just about 40% of the max.
      OrangutanMotors::setSpeeds(0, 0);
      delay(100);
    //OrangutanMotors::setSpeeds(-30, -30);
    //delay(300);
    //OrangutanMotors::setSpeeds(0, 0);
    //delay(100);
      do {
        OrangutanMotors::setSpeeds(-30, 30);
        position = robot.readLine(sensors, IR_EMITTERS_ON);
        delay(100);
      } while ((position < 2000) && (position > 3000));
    //delay(300); 
      state = TROUVELIGNE;
    } // else if ((position==4000) ||(position ==0))
  } //else du if (state == DEBUT)
// OrangutanMotors::setSpeeds(0, 0);
// delay(100);
  if (state == TROUVELIGNE) {
    if (position < 200) state = TROUVESORTIE; else {//my_speed = 30; else my_speed = -30;
    if ((position==4000) ||(position ==0))
      OrangutanMotors::setSpeeds(45, 30);
  //if (position >= 512) { // 2000 si 5 capteurs
    else {
// if (position < 200) state = TROUVESORTIE;//my_speed = 30; else my_speed = -30;
    // We are far to the right of the line: turn left.
 
    // Set the right motor to 100 and the left motor to zero,
    // to do a sharp turn to the left.  Note that the maximum
    // value of either motor speed is 255, so we are driving
    // it at just about 40% of the max.
      OrangutanMotors::setSpeeds(0, 0);
      delay(100);
    //OrangutanMotors::setSpeeds(-30, -30);
    //delay(300);
    //OrangutanMotors::setSpeeds(0, 0);
    //delay(100);
      do {
        //OrangutanMotors::setSpeeds(-30, 30);
        OrangutanMotors::setSpeeds(my_speed, -my_speed);
        position = robot.readLine(sensors, IR_EMITTERS_ON);
        delay(100);
      } while ((position < 2000) && (position > 3000));
    //delay(300); 
// state = TROUVELIGNE;
    } //else du if ((position==4000) ||(position ==0))
  }
  } //if (state == TROUVELIGNE) 
  if (state == TROUVESORTIE) {
    my_speed = 30;
    if ((position==4000) ||(position ==0))
      OrangutanMotors::setSpeeds(30, 35);
    else {
      OrangutanMotors::setSpeeds(0, 0);
      delay(100);
      do {
        OrangutanMotors::setSpeeds(my_speed, -my_speed);
        position = robot.readLine(sensors, IR_EMITTERS_ON);
        delay(100);
      } while ((position < 2000) && (position > 3000));
    //delay(300); 
// state = TROUVELIGNE;
    } //else du if ((position==4000) ||(position ==0))
  } //if (state == TROUVESORTIE) 
} // end loop

Projets FPGA[modifier | modifier le wikicode]

Pour moi sous Linux :

#!/bin/bash
export PATH=$PATH:/usr/local/avr/bin:~/XILINX/Xilinx/11.1/ISE/bin/lin/
# pong_2_rs232_VGA.c a retrouver avec $1
avr-gcc -g -mmcu=atmega8 -Wall -Os -c pong_2_rs232_VGA.c 
#if [$? -lt 0]; then exit ; fi
avr-gcc -g -mmcu=atmega8 -o pong_2_rs232_VGA.elf -Wl,-Map,pong_2_rs232_VGA.map pong_2_rs232_VGA.o 
avr-objdump -h -S pong_2_rs232_VGA.elf > pong_2_rs232_VGA.lss
 data2mem -bm memory.bmm -bd pong_2_rs232_VGA.elf -bt avr_fpga.bit -o uh avr_fpga
echo "Continuer y/n ?"
read reponse
if [ "$reponse" = "y" ]; then
  cd ~/XILINX/Xilinx/XC3Sprog/ ;
  ./xc3sprog ~/XILINX/ATMEL/Travail_avrgcc2bit/avr_fpga_rp.bit ;
  cd ~/XILINX/ATMEL/Travail_avrgcc2bit/ ; 
  exit 1
fi

Ce script mérite d’être améliorer pour dépendre du programme à compiler et que toute erreur de compilation empêche d'exécuter la suite ! Ce dernier point est fait pour le téléchargement SergeMoutou (discussion) 18 mars 2013 à 16:50 (UTC).

Pour étudiants

set path = C:\WinAVR-20100110\bin
cd C:\Users\Benjamin\Desktop\"projet TR"\ANDROID
avr-gcc -g -mmcu=atmega8 -Wall -Os -c pong_2_rs232_VGA.c
avr-gcc -g -mmcu=atmega8 -o hello.elf -Wl,-Map,hello.map pong_2_rs232_VGA.o
C:\Xilinx\13.1\ISE_DS\ISE\bin\nt64\data2mem.exe -bm memory.bmm -bd hello.elf -bt avr_fpga.bit -o b new_avr_fpga.bit
cd c:\users\Benjamin
wine cmd
avr-gcc -g -mmcu=atmega8 -Wall -Os -c hello.c

avr-gcc -g -mmcu=atmega8 -o hello.elf -Wl,-Map,hello.map hello.o
cd ~/.wine/drive_c/
~/XILINX/Xilinx/11.1/ISE/bin/lin/data2mem -bm memory.bmm -bd hello.elf -bt avr_fpga.bit -o b new_avr_fpga.bit
cd ~/XILINX/Xilinx/XC3Sprog
cp ~/.wine/drive_c/new_avr_fpga.bit ../../ATMEL/Interrupt/interruptRS232/new_avr_fpga.bit
./xc3sprog ../../ATMEL/Interrupt/interruptRS232/new_avr_fpga.bit

Ne pas lire !!! pour hello.bat :

avr-gcc -g -mmcu=atmega8 -Wall -Os -c hello.c

avr-gcc -g -mmcu=atmega8 -o hello.out -Wl,-Map,hello.map hello.o

avr-objcopy -R .eeprom -O ihex hello.out hello.hex
avr-objdump -h -S hello.out > hello.lss

make_mem hello.hex prog_mem_content.vhd

copy prog_mem_content.vhd I:\XILINX\ATMEL\Interrupt\interruptRS232\

Ne pas lire !!! Pour bootloader :

avr-gcc -g -mmcu=atmega8 -Wall -Os -c intel_hex.c
avr-gcc -g -mmcu=atmega8 -Wall -Os -c uart.c

avr-gcc -g -mmcu=atmega8 -o intel_hex.out -Wl,-Map,hello.map intel_hex.o uart.o

avr-objcopy -R .eeprom -O ihex intel_hex.out intel_hex.hex
avr-objdump -h -S intel_hex.out > intel_hex.lss

make_mem intel_hex.hex prog_mem_content.vhd

copy prog_mem_content.vhd I:\XILINX\ATMEL\Interrupt\interruptRS232\


Traduire et mettre en œuvre texte ci-dessous[modifier | modifier le wikicode]

A good tips, is to use your program (not the bootloader) to reset the device. This avoid the need to a push the reset button. You can do this easily with something like this (for a ATMega8)

void (*resetptr)( void ) = 0xE00; // Set up reset to bootloader

SIGNAL (SIG_UART_RECV) {
..
if (c == r) { uart_puts(Reset \r\n ); resetptr(); }


}

By this way, you can simply press r to reset, and flash. Even better, you can patch the program on the PC to do a automatic reset this way.

Gestion de projet Xilinx[modifier | modifier le wikicode]

// Welcome to Xilinx Platform Studio SDK !
//
// This is an automatically created source file to help you get started.
// To add more files, navigate to File → New → File
// You may delete this file if you want to use only other files for your project.
//
#include "xparameters.h"
//#define XPAR_VGACASSBRIK_0_BASEADDR 0x00010000 
#include "xio.h" 

int main()
{ Xuint32 i,posXY,deltaX,deltaY,yraquD;
// deplacement de la balle
    posXY = 0x00100010;
    deltaX=1;deltaY=0x00010000;
    XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR,posXY);	
	while(1) {
	 XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR,posXY);
	 posXY = posXY+deltaX;
	 posXY = posXY+deltaY;
//	 if ((posXY & 0x000003FF)<25) deltaX = -deltaX;
// rebond systématique à droite
	 if ((posXY & 0x000003FF)>572) deltaX = -deltaX; //OK
// rebond systématique en haut	 
	 if ((posXY & 0x03FF0000)<0x00050000) deltaY = -deltaY; //NOK ?
//rebond systématique en bas	 
	 if ((posXY & 0x03FF0000)>0x019A0000) deltaY = -deltaY; //OK
	 for (i=0;i<100000;i++); // attente OK
// déplacement des deux raquettes de 16 pixels vers le bas	 
    XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+4, 0x000F000F);
// soyons sérieux : la raquette droite suit la balle
    yraquD = (posXY >>1)& 0x03FF0000; //1 bit de moins 
    if (yraquD > 0x00CD0000) yraquD=0x00CD0000; //NOK
    XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+4, 0x0000000F|yraquD);  //OK 
// Affichage de 99 sur les deux afficheurs sept segments 
    XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+8, 0x00000099);
// Affichage des deux murs de brique
    XIo_Out32(XPAR_VGACASSBRIK_0_BASEADDR+0xC, 0x00FF00FF);
	}
	return 0;
}

Part of the problem is that the GPIO core really wants to be at the top-level (pins) because it's bidirectional. For my EDK designs, I've done the following. This is a bit wordy, hopefully it makes sense.

a) The EDK project is always a subset of a larger ISE project. So you start by creating an ISE project, then you add new EDK project to it as a source. Then you always open the EDK project from ISE by double-clicking it in the source list. You do this so that the tools "know" that EDK should not insert I/O buffers.

b) The "simple" MicroBlaze project is something that I thought of before Xilinx blessed it. Basically, it works as follows. You create a PLB slave interface that is just one memory space. No I/O registers, no I/O chip selects, no crazy decoding, nothing. You just have a memory space with exactly one chip select from the PLB interface logic that goes true when the space is asserted. Your "Core," then, needs to decode the PLB slave address and read/write and that chip select and then respond. This is how you'd handle any typical microprocessor bus and should not be difficult. What I do is to bring out to the top of the EDK project (in the MHS file) all of the PLB slave interface signals necessary: address, data in, data out, chip select, read/write, acknowledges, interrupt request. These are then exposed to the larger FPGA fabric where they can be used as one would expect.

c) In your case, you want to decode a small register space for the switches. Decide that the switches live at address BASE+0x00000000. Then whenever you read from BASE+0x00000000, you get the state of your switches. BASE is something that gets set by the tools when it generates the address map that ultimately fires your memory-space's chip select. If there are no other things in your memory space, you can simply use the chip select itself. If you need to deal with other things, then decode only the address bits you care about. (You can make the memory space arbitrarily small.)

d) The UCF is not too difficult. Just do the pin assignments for signals you actually have in your project. So there's likely to be a clock input (on a known, documented pin) and the switches you want to read (ditto). If your MB firmware is small enough to fit into BRAMs, then you don't even need the external memory. That's about it. Internal logic of course does not need pin locations. You will need to set a period constraint on your clock.


Summary: EDK is an overwhelming pain in the ass. The Simple MicroBlaze Project is an attempt to minimize the pain, as is my concept outlined above. Unfortunately, there's no real simple easy way to learn it. It took quite awhile to get to the point where I thought about using a one-memory-space concept. You should contact your local FAE and see about attending a training session where they work through simple examples. The FAE should at least be able to provide the training materials.


SergeMoutou 22 décembre 2010 à 14:38 (UTC)