class DMA – pääsy RP2040:n DMA-ohjaimeen¶
DMA-luokka tarjoaa pääsyn RP2040:n suoran muistipääsyn (DMA) ohjaimeen, mikä mahdollistaa datan siirtämisen muistilohkojen ja/tai IO-rekisterien välillä. DMA-ohjaimella on omat, erilliset luku- ja kirjoitusväyläisäntäyhteydet väylärakenteeseen, ja jokainen DMA-kanava voi itsenäisesti lukea dataa yhdestä osoitteesta ja kirjoittaa sen takaisin toiseen osoitteeseen, valinnaisesti kasvattaen toista tai molempia osoittimia, mikä mahdollistaa siirtojen suorittamisen prosessorin puolesta samalla, kun prosessori suorittaa muita tehtäviä tai siirtyy virransäästötilaan. RP2040:n DMA-ohjaimessa on 12 itsenäistä DMA-kanavaa, jotka voivat toimia samanaikaisesti. RP2040:n DMA-järjestelmän täydelliset tiedot löytyvät RP2040 Datasheet -dokumentin osasta 2.5.
Esimerkkejä¶
DMA-ohjaimen yksinkertaisin käyttötapa on datan siirtäminen muistilohkosta toiseen. Tämä voidaan toteuttaa seuraavalla koodilla:
a = bytearray(32*1024)
b = bytearray(32*1024)
d = rp2.DMA()
c = d.pack_ctrl() # Just use the default control value.
# The count is in 'transfers', which defaults to four-byte words, so divide length by 4
d.config(read=a, write=b, count=len(a)//4, ctrl=c, trigger=True)
# Wait for completion
while d.active():
pass
Huomaa, että vaikka tämä esimerkki on joutotilan silmukassa odottaessaan siirron valmistumista, ohjelma voisi yhtä hyvin tehdä jotain hyödyllistä työtä tänä aikana.
Toinen, ehkä yleisempi DMA-ohjaimen käyttötapa on siirto muistin ja IO-oheislaitteen välillä. Tässä tilanteessa IO-rekisterin osoite ei muutu jokaisella siirrolla, mutta muistiosoitetta on kasvatettava. On myös tarpeen hallita siirron tahtia, jotta dataa ei kirjoiteta ennen kuin oheislaite voi ottaa sen vastaan tai lueta ennen kuin data on valmiina, ja tätä voidaan hallita DMA-kanavan ohjausrekisterin treq_sel-kentällä. Kunkin DMA-kanavan ohjausrekisterin eri kentät voidaan pakata DMA.pack_ctrl() -metodilla ja purkaa DMA.unpack_ctrl() -staattisella metodilla. Koodi, joka siirtää dataa tavutaulukosta PIO-tilakoneen TX FIFO:hon yksi tavu kerrallaan, näyttää tältä:
# pio_num is index of the PIO block being used, sm_num is the state machine in that block.
# my_state_machine is an rp2.PIO() instance.
DATA_REQUEST_INDEX = (pio_num << 3) + sm_num
src_data = bytearray(1024)
d = rp2.DMA()
# Transfer bytes, rather than words, don't increment the write address and pace the transfer.
c = d.pack_ctrl(size=0, inc_write=False, treq_sel=DATA_REQUEST_INDEX)
d.config(
read=src_data,
write=my_state_machine,
count=len(src_data),
ctrl=c,
trigger=True
)
Huomaa, että tässä esimerkissä kirjoitusosoitteeksi annettu arvo on vain PIO-tilakone, johon dataa lähetetään. Tämä toimii, koska PIO-tilakoneet tarjoavat puskuriprotokollan, mikä mahdollistaa suoran pääsyn niiden data-FIFO-rekistereihin.
Konstruktori¶
- class rp2.DMA¶
Varaa yhden DMA-ohjaimen kanavista yksinomaiseen käyttöön.
- config(read: 'int | _AnyReadableBuf | None' = None, write: 'int | _AnyWritableBuf | None' = None, count: int | None = None, ctrl: int | None = None, trigger: bool = False) None¶
Määrittää kanavan DMA-rekisterit ja valinnaisesti aloittaa siirron. Parametrit ovat:
read: Osoite, josta DMA-ohjain alkaa lukea dataa, tai objekti, joka tarjoaa luettavan datan. Se voi olla kokonaisluku tai mikä tahansa objekti, joka tukee puskuriprotokollaa.
write: Osoite, johon DMA-ohjain alkaa kirjoittaa, tai objekti, johon data kirjoitetaan. Se voi olla kokonaisluku tai mikä tahansa objekti, joka tukee puskuriprotokollaa.
count: Väyläsiirtojen lukumäärä, jotka suoritetaan ennen kuin tämä kanava pysähtyy. Huomaa, että tämä on siirtojen lukumäärä, ei tavujen lukumäärä. Jos siirrot ovat 2 tai 4 tavua leveitä, siirretyn datan kokonaismäärä (ja siten tarvittavan puskurin koko) on kerrottava vastaavasti.
ctrl: DMA-ohjausrekisterin arvo. Tämä on kokonaislukuarvo, joka tyypillisesti pakataan käyttäen
DMA.pack_ctrl().trigger: Valinnaisesti aloita siirto välittömästi.
- irq(handler: Callable[[DMA], None] | None = None, hard: bool = False) Callable¶
Palauttaa tämän DMA-kanavan IRQ-objektin ja valinnaisesti määrittää sen.
- close() None¶
Vapauttaa varauksen taustalla olevasta DMA-kanavasta ja vapauttaa keskeytyskäsittelijän.
DMA-objektia ei voi käyttää tämän toiminnon jälkeen.
- pack_ctrl(default: int | None = None, **kwargs) int¶
Pakkaa avainsana-argumenteissa annetut arvot uuden ohjausrekisteriarvon nimettyihin kenttiin. Mikä tahansa kenttä, jota ei anneta, asetetaan oletusarvoon. Oletus otetaan joko annetusta
default-arvosta tai, jos sitä ei anneta, nykyiselle kanavalle sopivasta oletuksesta; tämän asettaminenDMA.ctrl-attribuutin nykyiseen arvoon tarjoaa helpon tavan ohittaa kenttien osajoukko.Avainsana-argumenttien avaimet voivat olla mitä tahansa avaimia, jotka
DMA.unpack_ctrl()-metodi palauttaa. Kirjoitettavat arvot ovat:enable:
boolAseta kanavan käyttöön ottamiseksi (oletus:True).high_pri:
boolTee tämän kanavan väyläliikenteestä korkean prioriteetin (oletus:False).size:
intSiirron koko: 0=tavu, 1=puolisana, 2=sana (oletus: 2).inc_read:
boolKasvata lukuosoitetta jokaisen siirron jälkeen (oletus:True).inc_write:
boolKasvata kirjoitusosoitetta jokaisen siirron jälkeen (oletus:True).ring_size:
intJos nollasta poikkeava, vain yhden osoiterekisterin alimmatring_sizebittiä muuttuvat, kun osoitetta kasvatetaan, jolloin osoite kiertyy seuraavalla1 << ring_size-tavurajalla. Sitä, mikä osoite kiertyy, ohjaaring_sel-lippu. Nolla-arvo poistaa osoitteen kiertymisen käytöstä.ring_sel:
boolAseta arvoonFalse, jottaring_sizekoskee lukuosoitetta, taiTrue, jotta se koskee kirjoitusosoitetta.chain_to:
intSen kanavan kanavanumero, joka käynnistetään tämän siirron valmistuttua. Tämän arvon asettaminen tämän DMA-objektin omaan kanavanumeroon poistaa ketjutuksen käytöstä (tämä on oletus).treq_sel:
intValitse Transfer Request -signaali. Katso tarkemmat tiedot RP2040-datalehden osasta 2.5.3.irq_quiet:
boolÄlä luo keskeytystä jokaisen siirron lopussa. Keskeytyksiä luodaan sen sijaan, kun käynnistysrekisteriin kirjoitetaan nolla-arvo, mikä pysäyttää ketjutettujen siirtojen sarjan (oletus:True).bswap:
boolJos asetettu arvoon true, sanojen tai puolisanojen tavut käännetään ennen kirjoittamista (oletus:True).sniff_en:
boolAseta arvoonTrue, jotta sirun sniff-laitteisto voi käyttää dataa (oletus:False).write_err:
boolTämän asettaminen arvoonTruetyhjentää aiemmin raportoidun kirjoitusvirheen.read_err:
boolTämän asettaminen arvoonTruetyhjentää aiemmin raportoidun lukuvirheen.
Katso
CH0_CTRL_TRIG-rekisterin kuvaus RP2040-datalehden osasta 2.5.7 saadaksesi tarkemmat tiedot kaikista näistä kentistä.
- unpack_ctrl(value: int) dict¶
Purkaa DMA-kanavan ohjausrekisterin arvon sanakirjaksi, jossa on avain/arvo-parit kullekin ohjausrekisterin kentälle. value on purettava
ctrl-rekisterin arvo.Tämä metodi palauttaa arvot kaikille avaimille, jotka voidaan välittää
DMA.pack_ctrl-metodille. Lisäksi se palauttaa myös ohjausrekisterin vain luku -liput:busy, joka nousee korkeaksi, kun siirto alkaa, ja matalaksi, kun se päättyy, sekäahb_err, joka onread_err- jawrite_err-lippujen looginen OR. Nämä arvot jätetään huomiotta pakattaessa, joten ohjausrekisterin purkamisesta luotua sanakirjaa voidaan käyttää suoraan avainsana-argumentteina pakkaamiseen.
- active(value: bool | None = None, /) bool¶
Hakee tai asettaa, onko DMA-kanava tällä hetkellä käynnissä.
>>> sm.active() 0 >>> sm.active(1) >>> while sm.active(): ... pass
- read: int¶
Tämä attribuutti kuvastaa osoitetta, josta seuraava väyläsiirto lukee. Siihen voidaan kirjoittaa joko kokonaisluku tai objekti, joka tukee puskuriprotokollaa, ja näin tekeminen vaikuttaa välittömästi.
- write: int¶
Tämä attribuutti kuvastaa osoitetta, johon seuraava väyläsiirto kirjoittaa. Siihen voidaan kirjoittaa joko kokonaisluku tai objekti, joka tukee puskuriprotokollaa, ja näin tekeminen vaikuttaa välittömästi.
- count: int¶
Tämän attribuutin lukeminen palauttaa jäljellä olevien väyläsiirtojen lukumäärän nykyisessä siirtosarjassa. Tämän attribuutin kirjoittaminen asettaa siirtojen kokonaismäärän seuraavalle siirtosarjalle.
- ctrl: int¶
Tämä attribuutti kuvastaa DMA-kanavan ohjausrekisteriä. Siihen kirjoitetaan tyypillisesti kokonaisluku, joka on pakattu
DMA.pack_ctrl()-metodilla. Palautettu rekisteriarvo voidaan purkaaDMA.unpack_ctrl()-metodilla.
- channel: int¶
DMA-kanavan kanavanumero. Tämä voidaan välittää toisen kanavan
DMA.pack_ctrl()-metodinchain_to-argumenttina DMA-ketjutuksen mahdollistamiseksi.
- registers: 'memoryview'¶
Tämä attribuutti on taulukon kaltainen objekti, joka mahdollistaa suoran pääsyn DMA-kanavan rekistereihin. Indeksointi tapahtuu sanoittain, ei tavuittain, joten rekisteri-indeksit ovat rekisterin osoitesiirtymät jaettuna 4:llä. Katso rekisteritiedot RP2040-datalehdestä.
Ketjutus ja käynnistysrekisterin käyttö¶
RP2040:n DMA-ohjain tarjoaa pari edistynyttä ominaisuutta, joiden avulla yksi DMA-kanava voi käynnistää siirron toisella kanavalla. Toinen on ohjausrekisterin chain_to-arvon käyttö ja toinen on kirjoittaminen johonkin DMA-kanavan rekisteriin, jolla on käynnistysvaikutus. Yhdistettynä kykyyn, jossa yksi DMA-kanava voi kirjoittaa suoraan toisen kanavan DMA.registers-rekistereihin, tämä mahdollistaa monimutkaisten transaktioiden suorittamisen ilman mitään CPU:n väliintuloa.
Alla on esimerkki sekä ketjutuksen että rekisterin käynnistämisen käytöstä, jolla toteutetaan useiden datalohkojen kokoaminen yhteen kohteeseen. Näiden ominaisuuksien täydelliset tiedot löytyvät RP2040-datalehden osasta 2.5, ja alla oleva koodi on Pythonin tyylinen versio aliosan 2.5.6.2 esimerkistä.
from rp2 import DMA
from uctypes import addressof
from array import array
def gather_strings(string_list, buf):
# We use two DMA channels. The first sends lengths and source addresses from the gather
# list to the registers of the second. The second copies the data itself.
gather_dma = DMA()
buffer_dma = DMA()
# Pack up length/address pairs to be sent to the registers.
gather_list = array("I")
for s in string_list:
gather_list.append(len(s))
gather_list.append(addressof(s))
gather_list.append(0)
gather_list.append(0)
# When writing to the registers of the second DMA channel, we need to wrap the
# write address on an 8-byte (1<<3 bytes) boundary. We write to the ``TRANS_COUNT``
# and ``READ_ADD_TRIG`` registers in the last register alias (registers 14 and 15).
gather_ctrl = gather_dma.pack_ctrl(ring_size=3, ring_sel=True)
gather_dma.config(
read=gather_list, write=buffer_dma.registers[14:16],
count=2, ctrl=gather_ctrl
)
# When copying the data, the transfer size is single bytes, and when completed we need
# to chain back to the start another gather DMA transaction.
buffer_ctrl = buffer_dma.pack_ctrl(size=0, chain_to=gather_dma.channel)
# The read and count values will be set by the other DMA channel.
buffer_dma.config(write=buf, ctrl=buffer_ctrl)
# Set the transfer in motion.
gather_dma.active(1)
# Wait until all the register values have been sent
end_address = addressof(gather_list) + 4 * len(gather_list)
while gather_dma.read != end_address:
pass
input = ["This is ", "a ", "test", " of the scatter", " gather", " process"]
output = bytearray(64)
print(output)
gather_strings(input, output)
print(output)
Tämä esimerkki on joutotilassa odottaessaan siirron valmistumista; vaihtoehtoisesti se voisi asettaa keskeytyskäsittelijän ja palata välittömästi.