klasa DMA – pristup RP2040-ovom DMA kontroleru¶
Klasa DMA nudi pristup RP2040-ovom kontroleru izravnog pristupa memoriji (DMA), pružajući mogućnost premještanja podataka između memorijskih blokova i/ili IO registara. DMA kontroler ima vlastite, odvojene veze upravljača sabirnice za čitanje i pisanje na sabirničku strukturu, a svaki DMA kanal može neovisno čitati podatke s jedne adrese i zapisivati ih na drugu adresu, opcionalno povećavajući jedan ili oba pokazivača, što mu omogućuje izvođenje prijenosa u ime procesora dok procesor obavlja druge zadatke ili ulazi u stanje niske potrošnje. RP2040-ov DMA kontroler ima 12 neovisnih DMA kanala koji mogu raditi istovremeno. Za potpune pojedinosti o RP2040-ovom DMA sustavu pogledajte odjeljak 2.5 dokumenta RP2040 Datasheet.
Primjeri¶
Najjednostavnija upotreba DMA kontrolera je premještanje podataka iz jednog memorijskog bloka u drugi. To se može postići sljedećim kodom:
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
Imajte na umu da, iako ovaj primjer ostaje u praznoj petlji dok čeka da se prijenos dovrši, program bi u tom vremenu mogao jednako tako obavljati neki koristan posao.
Druga, možda češća upotreba DMA kontrolera je prijenos između memorije i IO periferije. U toj situaciji adresa IO registra se ne mijenja za svaki prijenos, ali memorijska adresa se mora povećavati. Također je potrebno kontrolirati tempo prijenosa kako se podaci ne bi zapisivali prije nego što ih periferija može prihvatiti ili čitali prije nego što su podaci spremni, a to se može kontrolirati poljem treq_sel kontrolnog registra DMA kanala. Različita polja kontrolnog registra za svaki DMA kanal mogu se pakirati metodom DMA.pack_ctrl() i raspakirati statičkom metodom DMA.unpack_ctrl(). Kod za prijenos podataka iz niza bajtova u TX FIFO PIO state machine, jedan po jedan bajt, izgleda ovako:
# 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
)
Imajte na umu da je u ovom primjeru vrijednost dana za adresu pisanja jednostavno PIO state machine kojemu šaljemo podatke. Ovo funkcionira jer PIO state machine predstavljaju protokol međuspremnika, omogućujući izravan pristup svojim FIFO registrima podataka.
Konstruktor¶
- class rp2.DMA¶
Zauzima jedan od kanala DMA kontrolera za isključivu upotrebu.
- config(read: 'int | _AnyReadableBuf | None' = None, write: 'int | _AnyWritableBuf | None' = None, count: int | None = None, ctrl: int | None = None, trigger: bool = False) None¶
Konfigurira DMA registre za kanal i opcionalno pokreće prijenos. Parametri su:
read: Adresa s koje će DMA kontroler početi čitati podatke ili objekt koji će pružati podatke za čitanje. Može biti cijeli broj ili bilo koji objekt koji podržava protokol međuspremnika.
write: Adresa na koju će DMA kontroler početi zapisivati ili objekt u koji će se podaci zapisivati. Može biti cijeli broj ili bilo koji objekt koji podržava protokol međuspremnika.
count: Broj sabirničkih prijenosa koji će se izvršiti prije nego što se ovaj kanal zaustavi. Imajte na umu da je ovo broj prijenosa, a ne broj bajtova. Ako su prijenosi široki 2 ili 4 bajta, tada se ukupna količina premještenih podataka (a time i veličina potrebnog međuspremnika) mora prema tome pomnožiti.
ctrl: Vrijednost za DMA kontrolni registar. Ovo je cjelobrojna vrijednost koja se obično pakira pomoću
DMA.pack_ctrl().trigger: Opcionalno odmah započinje prijenos.
- irq(handler: Callable[[DMA], None] | None = None, hard: bool = False) Callable¶
Vraća IRQ objekt za ovaj DMA kanal i opcionalno ga konfigurira.
- close() None¶
Oslobađa zauzeće temeljnog DMA kanala i oslobađa rukovatelj prekida. Objekt
DMAne može se koristiti nakon ove operacije.
- pack_ctrl(default: int | None = None, **kwargs) int¶
Pakira vrijednosti navedene u imenovanim argumentima u imenovana polja nove vrijednosti kontrolnog registra. Svako polje koje nije navedeno postavit će se na zadanu vrijednost. Zadana vrijednost uzet će se ili iz navedene vrijednosti
defaultili, ako ona nije dana, iz zadane vrijednosti prikladne za trenutni kanal; postavljanje ovoga na trenutnu vrijednost atributaDMA.ctrlpruža jednostavan način za nadjačavanje podskupa polja.Ključevi za imenovane argumente mogu biti bilo koji ključ koji vraća metoda
DMA.unpack_ctrl(). Zapisive vrijednosti su:enable:
boolPostavite za omogućavanje kanala (zadano:True).high_pri:
boolUčinite sabirnički promet ovog kanala visokim prioritetom (zadano:False).size:
intVeličina prijenosa: 0=bajt, 1=polovica riječi, 2=riječ (zadano: 2).inc_read:
boolPovećava adresu čitanja nakon svakog prijenosa (zadano:True).inc_write:
boolPovećava adresu pisanja nakon svakog prijenosa (zadano:True).ring_size:
intAko je različit od nule, samo donjiring_sizebitovi jednog adresnog registra mijenjat će se kada se adresa povećava, uzrokujući da se adresa omota na sljedećoj granici od1 << ring_sizebajtova. Koja se adresa omota kontrolira zastavicaring_sel. Vrijednost nula onemogućuje omatanje adresa.ring_sel:
boolPostavite naFalsekako bi sering_sizeprimijenio na adresu čitanja ili naTruekako bi se primijenio na adresu pisanja.chain_to:
intBroj kanala za kanal koji se pokreće nakon što se ovaj prijenos dovrši. Postavljanje ove vrijednosti na vlastiti broj kanala ovog DMA objekta onemogućuje ulančavanje (to je zadano).treq_sel:
intOdabire signal zahtjeva za prijenos (Transfer Request). Za pojedinosti pogledajte odjeljak 2.5.3 u RP2040 dokumentaciji.irq_quiet:
boolNe generira prekid na kraju svakog prijenosa. Prekidi će se umjesto toga generirati kada se vrijednost nula zapiše u okidački registar, što će zaustaviti niz ulančanih prijenosa (zadano:True).bswap:
boolAko je postavljeno na true, bajtovi u riječima ili poluriječima bit će obrnuti prije pisanja (zadano:True).sniff_en:
boolPostavite naTruekako biste dopustili pristup podacima sniff hardveru čipa (zadano:False).write_err:
boolPostavljanje ovoga naTrueizbrisat će prethodno prijavljenu pogrešku pisanja.read_err:
boolPostavljanje ovoga naTrueizbrisat će prethodno prijavljenu pogrešku čitanja.
Za pojedinosti o svim ovim poljima pogledajte opis registra
CH0_CTRL_TRIGu odjeljku 2.5.7 RP2040 dokumentacije.
- unpack_ctrl(value: int) dict¶
Raspakirava vrijednost za kontrolni registar DMA kanala u rječnik s parovima ključ/vrijednost za svako od polja u kontrolnom registru. value je vrijednost registra
ctrlkoju treba raspakirati.Ova metoda vraća vrijednosti za sve ključeve koji se mogu proslijediti u
DMA.pack_ctrl. Osim toga, vratit će i zastavice samo za čitanje u kontrolnom registru:busy, koja prelazi u visoko stanje kada prijenos započne i u nisko stanje kada završi, teahb_err, koja je logičko ILI zastavicaread_erriwrite_err. Te se vrijednosti zanemaruju pri pakiranju, tako da se rječnik stvoren raspakiranjem kontrolnog registra može izravno koristiti kao imenovani argumenti za pakiranje.
- active(value: bool | None = None, /) bool¶
Dohvaća ili postavlja je li DMA kanal trenutno pokrenut.
>>> sm.active() 0 >>> sm.active(1) >>> while sm.active(): ... pass
- read: int¶
Ovaj atribut odražava adresu s koje će sljedeći sabirnički prijenos čitati. Može se zapisati cijelim brojem ili objektom koji podržava protokol međuspremnika, a to ima trenutni učinak.
- write: int¶
Ovaj atribut odražava adresu na koju će sljedeći sabirnički prijenos zapisivati. Može se zapisati cijelim brojem ili objektom koji podržava protokol međuspremnika, a to ima trenutni učinak.
- count: int¶
Čitanje ovog atributa vratit će broj preostalih sabirničkih prijenosa u trenutnom nizu prijenosa. Pisanje ovog atributa postavlja ukupni broj prijenosa za sljedeći niz prijenosa.
- ctrl: int¶
Ovaj atribut odražava kontrolni registar DMA kanala. Obično se zapisuje cijelim brojem zapakiranim metodom
DMA.pack_ctrl(). Vraćena vrijednost registra može se raspakirati metodomDMA.unpack_ctrl().
- channel: int¶
Broj kanala DMA kanala. Ovo se može proslijediti u argument
chain_tometodeDMA.pack_ctrl()na drugom kanalu kako bi se omogućilo DMA ulančavanje.
- registers: 'memoryview'¶
Ovaj atribut je objekt nalik nizu koji omogućuje izravan pristup registrima DMA kanala. Indeksiranje je po riječi, a ne po bajtu, tako da su indeksi registara odmaci adresa registara podijeljeni s 4. Za pojedinosti o registrima pogledajte RP2040 dokumentaciju.
Ulančavanje i pristup okidačkim registrima¶
DMA kontroler u RP2040 nudi nekoliko naprednih značajki koje omogućuju jednom DMA kanalu da pokrene prijenos na drugom kanalu. Jedna je upotreba vrijednosti chain_to u kontrolnom registru, a druga je pisanje u jedan od registara DMA kanala koji ima okidački učinak. U kombinaciji s mogućnošću da jedan DMA kanal zapisuje izravno u DMA.registers drugog kanala, ovo omogućuje izvođenje složenih transakcija bez ikakve intervencije procesora.
Ispod je primjer korištenja i ulančavanja i okidanja registara za implementaciju prikupljanja više blokova podataka u jedno odredište. Potpune pojedinosti o tim značajkama mogu se pronaći u odjeljku 2.5 RP2040 dokumentacije, a kod ispod je Pythonska verzija primjera iz pododjeljka 2.5.6.2.
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)
Ovaj primjer miruje dok čeka da se prijenos dovrši; alternativno bi mogao postaviti rukovatelj prekida i odmah se vratiti.