2010-09-09 8 views
5

il mio progetto di elaborazione delle immagini funziona con immagini in scala di grigi. Ho una piattaforma di processore ARM Cortex-A8. Voglio fare uso del NEON.Carica 8bit uint8_t come uint32_t?

Ho un'immagine in scala di grigi (considera l'esempio di seguito) e nel mio alogoritmo, devo aggiungere solo le colonne.

Come caricare quattro pixel a 8 bit valori in parallelo, che sono uint8_t, come quattro uint32_t in uno dei registri a 128 bit NEON? Che cosa intrinseco devo usare per fare questo?

voglio dire:

alt text

devo caricarli come 32 bit, perché se si guarda con attenzione, il momento in cui ho fare 255 + 255 è 512, che non può essere tenuto in un 8-bit Registrare.

ad es.

255 255 255 255 ......... (640 pixels) 
255 255 255 255 
255 255 255 255 
255 255 255 255 
. 
. 
. 
. 
. 
(480 pixels) 
+2

255 + 255 = 510. – kennytm

risposta

11

Ti consiglierò di dedicare un po 'di tempo a capire come funziona SIMD su ARM. Guarda:

Date un'occhiata a:

  1. http://blogs.arm.com/software-enablement/161-coding-for-neon-part-1-load-and-stores/
  2. http://blogs.arm.com/software-enablement/196-coding-for-neon-part-2-dealing-with-leftovers/
  3. http://blogs.arm.com/software-enablement/241-coding-for-neon-part-3-matrix-multiplication/
  4. http://blogs.arm.com/software-enablement/277-coding-for-neon-part-4-shifting-left-and-right/

per iniziare. È quindi possibile implementare il codice SIMD utilizzando l'assemblatore in linea o gli intrinsechi ARM corrispondenti raccomandati da Domen.

3

Se è necessario sommare fino a 480 valori a 8 bit, occorrono tecnicamente 17 bit di memoria intermedia. Tuttavia, se si eseguono le aggiunte in due fasi, vale a dire le prime 240 righe e le 240 in basso, è possibile eseguirle a 16 bit ciascuna. Quindi puoi aggiungere i risultati delle due metà per ottenere la risposta finale.

Esiste in realtà un'istruzione NEON adatta al proprio algoritmo chiamato vaddw. Aggiungerà un vettore dword a un vettore qword, con quest'ultimo contenente elementi larghi il doppio del primo. Nel tuo caso, vaddw.u8 può essere utilizzato per aggiungere 8 pixel a 8 accumulatori da 16 bit. Quindi, vaddw.u16 può essere utilizzato per aggiungere i due set di 8 accumulatori a 16 bit in un set di 8 bit a 32 bit - si noti che è necessario utilizzare l'istruzione due volte per ottenere entrambe le metà.

Se necessario, è anche possibile convertire i valori a 16 bit o 8 bit utilizzando vmovn o vqmovn.

2

Non ci sono istruzioni in grado di caricare il tuo valore a 4 8 bit in 4 registri a 32 bit.

è necessario caricarli e quindi utilizzare un vshl due volte. perché neon non può usare 32 registri dovrai lavorare su 8 pixel (e non 4)

È possibile utilizzare solo 16 bit registrati. dovrebbe essere sufficiente ...

0

Caricare i 4 byte utilizzando un'istruzione di caricamento a singola corsia (vld1 <register>[<lane>], [<address]) in un registro q, quindi utilizzare due istruzioni di movimento lungo (vmovl) per promuoverli prima a 16 e quindi a 32 bit. Il risultato dovrebbe essere qualcosa di simile (sintassi in GNU)

vld1 d0[0], [<address>] @Now d0 = (*<addr>, *<addr+1>, *<addr+2>, *<addr+3>, <junk>, ... <junk>) 
vmovl.u8 q0, d0 @Now q1 = (d0, d1) = ((uint16_t)*<addr>, ... (uint16_t)*<addr+3>, <junk>, ... <junk>) 
vmovl.u16 q0, d2 @Now d0 = ((uint32_t)*<addr>, ... (uint32_t)*<addr+3>), d1 = (<junk>, ... <junk>) 

Se è possibile garantire che <address> è di 4 byte allineati, quindi scrivere [<address>: 32] invece nelle istruzioni di carico, di salvare un ciclo o due. Se lo fai e l'indirizzo non è allineato, avrai comunque un errore.

Um, ho appena capito che vuoi usare intrinsecamente, non assemblare, quindi ecco la stessa cosa con le intrinseche.

uint32x4_t v8; // Will actually hold 4 uint8_t 
v8 = vld1_lane_u32(ptr, v8, 0); 
const uint16x4_t v16 = vget_low_u16(vmovl_u8(vreinterpret_u8_u32(v8))); 
const uint32x4_t v32 = vmovl_u16(v16);