2016-12-07 2 views
2

Je souhaite envoyer une valeur de 128 bits à un FPGA, bit par bit, à partir d'un AVR. Le FPGA s'attend à ce que la transaction suivante se produise:
1) Signal RST pour effacer le registre de 128 bits du FPGA. Haut, puis bas. Un signal "CLOCK" est réglé sur bas.
2) Un bit ENABLE est réglé sur haut pour indiquer qu'un transfert est en cours.
3) Un bit INPUT est mis à la valeur de arrayOfBinaryValues ​​[i].
4a) Le signal CLOCK passe à l'état haut. Sur le bord d'attaque, la valeur de INPUT est stockée en position i sur le FPGA.
4b) Le signal CLOCK passe à l'état bas.
4c) bit INPUT réglé sur la valeur suivante arrayOfBinaryValues ​​[i]
[Répéter 4a-4c jusqu'à ce que tout le tableau est envoyé]
Convertir une chaîne de caractères en un tableau de valeurs binaires, puis transmettre le tableau bit par bit

Alors, je l'ai écrit une fonction pour réaliser ce but. Cela se passe par étapes. Étape 1, l'utilisateur entre une valeur de 32 caractères qui est stockée en tant que chaîne de caractères. Puisque ce sont des personnages, je dois les convertir en valeurs hexadécimales correspondantes:

void transmitToFPGA(unsigned char hash[32]) { 
    // convert the characters to their corresponding hex values 
    unsigned char hash_hex[32]; 
    unsigned char j = 0; 

    SET_BIT(FPGA_DDR,MD5_RST); // sets reset bit high 

    for (unsigned char i=0; i<32; i++) { 
     switch (hash[i]) { 
      case '0': hash_hex[i] = 0x00; break; 
      case '1': hash_hex[i] = 0x01; break; 
      case '2': hash_hex[i] = 0x02; break; 
      case '3': hash_hex[i] = 0x03; break; 
      case '4': hash_hex[i] = 0x04; break; 
      case '5': hash_hex[i] = 0x05; break; 
      case '6': hash_hex[i] = 0x06; break; 
      case '7': hash_hex[i] = 0x07; break; 
      case '8': hash_hex[i] = 0x08; break; 
      case '9': hash_hex[i] = 0x09; break; 
      case 'A': hash_hex[i] = 0x0a; break; 
      case 'B': hash_hex[i] = 0x0b; break; 
      case 'C': hash_hex[i] = 0x0c; break; 
      case 'D': hash_hex[i] = 0x0d; break; 
      case 'E': hash_hex[i] = 0x0e; break; 
      case 'F': hash_hex[i] = 0x0f; break; 
      default: hash_hex[i] = 0x00; break; 
     } 
    } 

Puis j'essayé de convertir les bits correspondants dans un tableau de valeurs binaires comme ceci:

unsigned char hash_bin[128]; 
    for (unsigned char i=0; i<32; i++) { 
     hash_bin[j] = hash_hex[i] & 0x01; j++; 
     hash_bin[j] = hash_hex[i] & 0x02; j++; 
     hash_bin[j] = hash_hex[i] & 0x04; j++; 
     hash_bin[j] = hash_hex[i] & 0x08; j++; 
    } 

Puis-je effectuer la transmission

// conduct transmission 
    CLR_BIT(FPGA_DDR,MD5_RST); // clear reset 
    delay_ms(1); 
    CLR_BIT(FPGA_DDR,AVR_CLK);   // AVR_CLK = 0 
    delay_ms(1); 
    CLR_BIT(FPGA_DDR,AVR_EN);   // AVR_EN = 0 
    delay_ms(1); 
    CLR_BIT(FPGA_DDR,AVR_IN);  // AVR_IN = 0 
    delay_ms(1); 
    for (unsigned char i=0; i<128; i++) { 
     CLR_BIT(FPGA_DDR,AVR_CLK);  // AVR_CLK = 0 
     delay_ms(1); 
     SET_BIT(FPGA_DDR,AVR_EN);  // AVR_EN = 1 
     delay_ms(1); 
     if (hash_bin[i] == 0) {   // AVR_IN = hash_bin[i] 
      CLR_BIT(FPGA_DDR,AVR_IN); 
     } else { 
      SET_BIT(FPGA_DDR,AVR_IN); 
     } 
     delay_ms(1); 
    t  SET_BIT(FPGA_DDR,AVR_EN);  // AVR_CLK = 1 
     delay_ms(1); 
    } 
} 

Malheureusement, cela ne semble pas fonctionner et je ne sais pas exactement pourquoi. Je soupçonne que la façon dont j'effectue la conversion ne fonctionne pas correctement. Quelqu'un a-t-il des idées?

modifier: Ceci est le module VHDL ce code communique avec:

library IEEE; 
use IEEE.STD_LOGIC_1164.ALL; 
entity SPI_Slave is 
    Port (AVR_IN : in  STD_LOGIC; 
      AVR_CLK : in STD_LOGIC; 
      RST  : in  STD_LOGIC; 
       ENABLE  : in STD_LOGIC; 
       READY   : out  STD_LOGIC; 
      HASH_OUT  : out  STD_LOGIC_VECTOR (127 downto 0) := x"00000000000000000000000000000000"); 
end SPI_Slave; 

architecture Behavioral of SPI_Slave is 

    shared variable count : integer := 0; 
    signal hash : std_logic_vector(127 downto 0); 

begin 

    PROCESS(AVR_CLK, ENABLE) 
    BEGIN 
     IF (ENABLE = '1') THEN     -- If ENABLE is HIGH 
      IF (rising_edge(AVR_CLK)) THEN -- If CLK goes HIGH 
       IF (RST = '1') THEN    -- If RST is HIGH 
        hash <= x"00000000000000000000000000000000"; -- then zero HASH_OUT and count 
        count := 0; 
        READY <= '0';    
       ELSE        -- Otherwise, if RST is LOW 
        IF (count > 126) THEN 
         hash(count) <= AVR_IN; 
         HASH_OUT <= hash (127 downto 0); 
         READY <= '1'; 
         count := count + 1; 
        ELSE 
         hash(count) <= AVR_IN; 
         count := count + 1; 
         READY <= '0'; 
        END IF; 
       END IF; 
      END IF; 
     END IF; 
    END PROCESS; 





end Behavioral; 
+1

Etes-vous sûr que votre séquence d'horloge est correcte? L'instruction indique que vous devez écrire les données et ensuite faire avancer et descendre l'horloge. –

+0

J'ai testé la séquence d'horloge dans un banc d'essai VHDL et de toutes les apparences c'était correct. Je pourrais réexaminer cet aspect si vous ne voyez aucun problème avec le C tel qu'écrit. Tous les modules personnalisés que j'ai écrits. Prolly aurait pu faire SPI, mais c'est un peu un hack et un exercice personnel. –

+0

Vous n'avez pas ramené l'horloge à la fin de la boucle mais au * départ *. Où la clause 4a) dit "la sortie est écrite ...." cela signifie que "la sortie que vous avez déjà écrite est transmise", et * pas * "c'est quand vous écrivez les données." –

Répondre

1

Si je vous, j'écrirait la fonction pour qu'il prenne les 128 bits en binaire.

De plus, je soupçonne que votre réglage de bit est incorrect (à l'écriture de cette réponse). J'ai essayé d'expliquer ma logique dans les commentaires dans le code:

#include <stdint.h> 
#include <errno.h> 

#define FPGA_LONG_DELAY() delay_ms(1) 
#define FPGA_DELAY()  delay_ms(1) 

int fpga_write_128bit(const uint8_t data[16]) 
{ 
    int i; 

    if (!data) 
     return EINVAL; 

    /* Ensure non-active state. */ 
    CLR_BIT(FPGA_DDR, AVR_EN); 
    CLR_BIT(FPGA_DDR, AVR_CLK); 
    CLR_BIT(FPGA_DDR, MD5_RST); 
    CLR_BIT(FPGA_DDR, AVR_IN); 
    FPGA_LONG_DELAY(); 

    /* Prepare for reset on rising clock edge. */ 
    SET_BIT(FPGA_DDR, AVR_EN); 
    SET_BIT(FPGA_DDR, MD5_RST); 
    FPGA_DELAY(); 

    /* Pulse clock (rising and trailing edges). */ 
    SET_BIT(FPGA_DDR, AVR_CLK); 
    FPGA_DELAY(); 
    CLR_BIT(FPGA_DDR, AVR_CLK); 

    /* Set reset low, and prepare for sending the data bits. */ 
    CLR_BIT(FPGA_DDR, MD5_RST); 

    for (i = 0; i < 128; i++) { 

     /* AVR_CLK is low and AVR_EN high at this point. */ 

     /* Set data; LSB of first byte first. */ 
     if ((data[i/8] & (1U << (i & 7)))) 
      SET_BIT(FPGA_DDR, AVR_IN); 
     else 
      CLR_BIT(FPGA_DDR, AVR_IN); 

     /* Ensure data bit state is stable before rising edge of clock. */ 
     FPGA_DELAY(); 

     /* Clock pulse (both rising and trailing edges) */ 
     SET_BIT(FPGA_DDR, AVR_CLK); 
     FPGA_DELAY(); 
     CLR_BIT(FPGA_DDR, AVR_CLK); 
    } 

    /* All bits transferred, clock is low. */ 

    CLR_BIT(FPGA_DDR, AVR_IN); /* Irrelevant, but let's leave a known state behind */ 
    CLR_BIT(FPGA_DDR, AVR_EN); 

    return 0; 
} 

Vous avez sans doute remarqué que j'ai utilisé deux macros, FPGA_LONG_DELAY() et FPGA_DELAY() là-dedans. Comme ils sont définis maintenant, l'envoi d'une seule valeur de 128 bits prend 259ms, soit un peu plus d'un quart de seconde. Vous pouvez probablement définir les deux comme des chaînes vides; sinon, au moins beaucoup plus court que 1ms.

Testez la fonction ci-dessus qui fonctionne comme vous le souhaitez.

Si vous voulez toujours une fonction qui prend le mot de 128 bits sous forme de chaîne, créez simplement une fonction wrapper qui analyse la valeur hexadécimale, puis appelle la fonction ci-dessus.

La fonction ci-dessus envoie le bit le moins significatif du premier octet en premier. Si nous considérons la chaîne hexadécimale comme un seul nombre de 128 bits, nous devons l'analyser de la droite vers la gauche, le premier caractère (le plus à droite) indiquant le quartet le moins significatif (quatre bits).

Si vous voulez que le VHDL HASH_OUT états à

HASH_OUT( 0) = 0 
HASH_OUT( 1) = 0 
HASH_OUT( 2) = 0 
HASH_OUT( 3) = 0 
HASH_OUT( 4) = 0 
HASH_OUT( 5) = 0 
HASH_OUT( 6) = 0 
HASH_OUT( 7) = 0 

HASH_OUT( 8) = 1 
HASH_OUT( 9) = 0 
HASH_OUT( 10) = 0 
HASH_OUT( 11) = 0 
HASH_OUT( 12) = 0 
HASH_OUT( 13) = 0 
HASH_OUT( 14) = 0 
HASH_OUT( 15) = 0 

HASH_OUT( 16) = 0 
HASH_OUT( 17) = 1 
HASH_OUT( 18) = 0 
HASH_OUT( 19) = 0 
HASH_OUT( 20) = 0 
HASH_OUT( 21) = 0 
HASH_OUT( 22) = 0 
HASH_OUT( 23) = 0 

HASH_OUT( 24) = 1 
HASH_OUT( 25) = 1 
HASH_OUT( 26) = 0 
HASH_OUT( 27) = 0 
HASH_OUT( 28) = 0 
HASH_OUT( 29) = 0 
HASH_OUT( 30) = 0 
HASH_OUT( 31) = 0 

et ainsi de suite, jusqu'à

HASH_OUT(120) = 1 
HASH_OUT(121) = 1 
HASH_OUT(122) = 1 
HASH_OUT(123) = 1 
HASH_OUT(124) = 0 
HASH_OUT(125) = 0 
HASH_OUT(126) = 0 
HASH_OUT(127) = 0 

vous pouvez utiliser le tableau macro et data suivant:

#define PACKBYTE(b0, b1, b2, b3, b4, b5, b6, b7) \ 
       ( (uint8_t)(!!(b0))  \ 
       | ((uint8_t)(!!(b1)) << 1) \ 
       | ((uint8_t)(!!(b2)) << 2) \ 
       | ((uint8_t)(!!(b3)) << 3) \ 
       | ((uint8_t)(!!(b4)) << 4) \ 
       | ((uint8_t)(!!(b5)) << 5) \ 
       | ((uint8_t)(!!(b6)) << 6) \ 
       | ((uint8_t)(!!(b7)) << 7)) 

const uint8_t data[16] = { 
    PACKBYTE(0, 0, 0, 0, 0, 0, 0, 0), 
    PACKBYTE(1, 0, 0, 0, 0, 0, 0, 0), 
    PACKBYTE(0, 1, 0, 0, 0, 0, 0, 0), 
    PACKBYTE(1, 1, 0, 0, 0, 0, 0, 0), 
    PACKBYTE(0, 0, 1, 0, 0, 0, 0, 0), 
    /* 10 PACKBYTE() lines omitted for brevity */ 
    PACKBYTE(1, 1, 1, 1, 0, 0, 0, 0) 
}; 

Notez que le !! dans la macro est juste deux non op érateurs. !!x évalue à 0 si x est zéro, et à 1 si x est différent de zéro. Contrairement à la normale, le bit le plus à gauche est le moins significatif, de sorte que le tableau ci-dessus a les bits dans le même ordre que le HASH_OUT dans le VHDL.

Pour la chaîne en hex la version, je vous recommande d'utiliser une fonction pour convertir le caractère en décimal:

static uint8_t hex_digit(const char c) 
{ 
    switch (c) { 
    case '0':   return 0U; 
    case '1':   return 1U; 
    case '2':   return 2U; 
    case '3':   return 3U; 
    case '4':   return 4U; 
    case '5':   return 5U; 
    case '6':   return 6U; 
    case '7':   return 7U; 
    case '8':   return 8U; 
    case '9':   return 9U; 
    case 'A': case 'a': return 10U; 
    case 'B': case 'b': return 11U; 
    case 'C': case 'c': return 12U; 
    case 'D': case 'd': return 13U; 
    case 'E': case 'e': return 14U; 
    case 'F': case 'f': return 15U; 
    default:   return 255U; /* Invalid value. */ 
    } 
} 

int fpga_write_hex_string(const char *const hex) 
{ 
    uint8_t data[16], hi, lo; 
    int  i; 

    if (!hex) 
     return EINVAL; 

    for (i = 0; i < 16; i++) { 

     /* Note: we parse the input string in pairs of 
       characters, leftmost first, so that if 
       it happens to be short, we won't try 
       to access it past its end. */ 

     hi = hex_digit(hex[2*i]); 
     if (hi > 15U) 
      return EINVAL; 

     lo = hex_digit(hex[2*i + 1]); 
     if (lo > 15U) 
      return EINVAL; 

     /* The i'th pair of hex digits form the 
      (15-i)'th byte value. */ 
     data[15 - i] = lo | (hi << 4); 
    } 

    return fpga_write_128bit(data); 
} 

Vous pouvez bien sûr simplement accepter une chaîne binaire (composée de 128 caractères de 0 ou 1, dans le même ordre que le VHDL HASH_OUT):

int fpga_write_bin_string(const char *const bin) 
{ 
    uint8_t data[16] = {0}; 
    int  i; 

    if (!bin) 
     return EINVAL; 

    for (i = 0; i < 128; i++) 
     if (bin[i] == '1') 
      data[i/8] |= 1U << (i & 7); 
     else 
     if (bin[i] != '0') 
      return EINVAL; 

    return fpga_write_128bit(data); 
} 

Je voudrais avoir l'un de ces (AVR ou ARM avec un FPGA) pour jouer avec moi-même. Donc, évidemment, tout le code ci-dessus n'est pas testé. N'hésitez pas à l'utiliser comme vous le souhaitez (c'est dans le domaine public), mais ne me blâmez pas si cela casse votre planche.