8. I/O prostor mapovaný do paměti

Osnova cvičení

  1. Semestrální projekt
  2. Zápis na I/O bránu
  3. Čtení z I/O brány
  4. Zápis řetězce na sériový port
  5. Volání služby operačního systému
Co bych si měl na cvičení zopakovat/připravit
  1. Rozumět programům z předchozích cvičení

Náplň cvičení

Cílem je z programu přistupovat na periferie procesorového zařízení.

Semestrální práce

Studenti by měli po cvičení utvořit týmy, prostudovat požadavky semestrálního projektu a připravit návrh svého projektu.

Úkoly

S problematikou se seznamte nejdříve v simulátoru QtRvSim. Jednoduchá vstupně výstupní periferie je mapovaná od adresy 0xffffc100.

Zápis hodnoty na výstupní port

  1. Napište program, který zobrazí 32-bitové číslo z registru na výstupní periferii - registr SPILED_REG_LED_LINE. Jedná se o řádku 32 LED diod na desce MZ_APO (fyzická adresa 0x43c40004) nebo ekvivalentním výstupu v simulátoru QtRvSim (přímo přístupná adresa 0xffffc104).
  2. Změňte následující program, aby vzor 101 dojel až doleva a zmizel. Program se po odjetí vzoru zastaví.

  lui t0, 0xffffc
  ori t0, t0, 0x100

  addi t1, zero, 5

  sw t1, 4(t0)
  slli t1, t1, 1
  sw t1, 4(t0)
  slli t1, t1, 1
  sw t1, 4(t0)
  slli t1, t1, 1

  ebreak

  1. Změňte program, aby vzor 101 dojel až doleva. Jakmile se rozsvítí LED nejvíce v levo, pak se vzor odrazí a bude se pohybovat vpravo.
  2. Vyzkoušejte program i pro jiné počáteční hodnoty vzoru než je 5, tedy 101. (Zkuste i vzory, které mají nastavený nejvyšší bit).
  1. Zjistěte co udělá následující program:

  lui t0, 0xffffc
  ori t0, t0, 0x100

  addi t1, zero, 0
  addi t2, zero, 0

  la s0, n
  lw s0,0(s0)

cykl1:  
  slli t1, t1, 1
  ori  t1, t1, 1
  addi t2,t2,1
  sw t1, 4(t0)
  bne s0,t2, cykl1

  ebreak
.data
n:
  .word 5

  1. Změňte program, aby zadaný počet jedniček dojel doleva a pak se vrátil vpravo a zmizel/přejel doprava. Pak opět vygenerujte daný počet jedniček.
  1. Zjistěte co dělá tento program, pomocí informací mapovaných periferiích:

  li t0, 0xffffc000   // base address into memory mapped I/O area
  addi t1, zero, 48
  addi t6, zero, 1234
  addi t2, zero, 10
loop:
  lw   t3, 0x08(t0)
  andi t3, t3, 1
  beq  t3, zero, loop  
  sw   t1, 0x0c(t0)
  addi t1, t1, 1
  addi t2, t2, -1
  bne  t2, zero, loop
  ebreak
  

  1. Načtěte počet svítících LED (jedniček) z předposledního programu z terminálu sériového portu (pouze pokud bude připraven znak).

Čtení ze vstupního portu

  1. Napište program, který do registru načte 32-bitovou hodnotu ze vstupní periferie (trojice otočných voličů). Na desce MZ_APO je stav voličů k přečtení z fyzické adresy 0x43c40024). V simulátoru QtRvSim je nastavení voličů dostupné na adrese 0xffffc124.
  2. Upravte program pro generování jedniček tak, aby počet jedniček byl natočení červeného voliče dělené 16 (instrukce srli) zvětšené o 1 (aby výsledná hodnota počtu jedniček byla v rozsahu 1 - 16).
  3. Rozšiřte program tak, že ze vstupu přečte číslo a na výstup zobrazí hodnotu z odpovídající pozice ve výše uvedeném poli. Pokud je zadáno číslo, překračující rozsah pole, zobrazí program na výstupu samé jedničky (rozsvítí všechny LED).

Čtení a zápis z programu v jazyce C

Kompletní popis periferií implementovaných v simulátoru QtRvSim naleznete na stránce třetího cvičení v odstavci periferie mapované do paměťového adresního prostoru případně přímo v souboru README.md přímo z projektu. Součástí třetího cvičení je i ukázka překladu z jazyka C do assembleru s rozborem generovaného kódu. Nalézá se tam také archiv s programem. Program naleznete in v adresáři /opt/apo/qtrvsim_binrep. Program se shodnou funkcí pro desku MZ_APO naleznete v adresáři /opt/apo/binrep/mzapo_binrep. Porovnání analýzy kódu zkompilovaného pro architekturu ARM naleznete na konci dnešního cvičení.

Zápis řetězce na sériový port

Program na výpis řetězce na sériový port pro QtRvSim

.equ SERIAL_PORT_BASE,   0xffffc000 
#base address of QtRVSim serial port

.equ SERP_RX_ST_REG,       0xffffc000  #Receiver status register
.equ SERP_RX_ST_REG_o,     0x0000      #Offset of RX_ST_REG
.equ SERP_RX_ST_REG_READY_m, 0x1 #Data byte is ready to be read
.equ SERP_RX_ST_REG_IE_m,    0x2 #Enable Rx ready interrupt

.equ SERP_RX_DATA_REG,   0xffffc004 #Received data byte in 8 LSB bits
.equ SERP_RX_DATA_REG_o,   0x0004   #Offset of RX_DATA_REG

.equ SERP_TX_ST_REG,     0xffffc008 #Transmitter status register
.equ SERP_TX_ST_REG_o,     0x0008   #Offset of TX_ST_REG
.equ SERP_TX_ST_REG_READY_m,  0x1 #Transmitter can accept next byte
.equ SERP_TX_ST_REG_IE_m,     0x2 #Enable Tx ready interrupt

.equ SERP_TX_DATA_REG,   0xffffc00c #Write word to send 8 LSB bits
.equ SERP_TX_DATA_REG_o,   0x000c   #Offset of TX_DATA_REG

write:
    li   a0, SERIAL_PORT_BASE # a0 ukazuje na zacatek pameti UART
    la   a1, text_1           # nacti ukazatel na retezec
next_char:
    lb   t1, 0(a1)            # nacti znak k odeslani
    beq  t1, zero, end_char   # 0 ukoncuje retezec
    addi a1, a1, 1            # posun ukazatel
tx_busy:
    lw   t0, SERP_TX_ST_REG_o(a0)       # zjisti statu odesilaci fronty
    andi t0, t0, SERP_TX_ST_REG_READY_m # vymaskuj bit READY
    beq  t0, zero, tx_busy    # pokud neni volno v UARTU cekej zkus to znovu
    sw   t1, SERP_TX_DATA_REG_o(a0) # je volno - zapis bajt
    j    next_char            # posli dalsi znak
end_char:
    ebreak                    # ukonci program

    .data
text_1:
    .asciz  "Hello world.\n"  # retezec ukonceny 0

Program čtení ze sériového portu pro QtRvSim

.equ SERIAL_PORT_BASE,   0xffffc000 
#base address of QtRVSim serial port

.equ SERP_RX_ST_REG,       0xffffc000  #Receiver status register
.equ SERP_RX_ST_REG_o,     0x0000      #Offset of RX_ST_REG
.equ SERP_RX_ST_REG_READY_m, 0x1 #Data byte is ready to be read
.equ SERP_RX_ST_REG_IE_m,    0x2 #Enable Rx ready interrupt

.equ SERP_RX_DATA_REG,   0xffffc004 #Received data byte in 8 LSB bits
.equ SERP_RX_DATA_REG_o,   0x0004   #Offset of RX_DATA_REG

.equ SERP_TX_ST_REG,     0xffffc008 #Transmitter status register
.equ SERP_TX_ST_REG_o,     0x0008   #Offset of TX_ST_REG
.equ SERP_TX_ST_REG_READY_m,  0x1 #Transmitter can accept next byte
.equ SERP_TX_ST_REG_IE_m,     0x2 #Enable Tx ready interrupt

.equ SERP_TX_DATA_REG,   0xffffc00c #Write word to send 8 LSB bits
.equ SERP_TX_DATA_REG_o,   0x000c   #Offset of TX_DATA_REG

gets: 
    li   a0, SERIAL_PORT_BASE # a0 ukazuje na zacatek pameti UART
    la   a1, text_1           # nacti ukazatel na buffer
    addi t2, zero, 40
next_char:
rx_not_ready:
    lw   t0, SERP_RX_ST_REG_o(a0)       # zjisti statu prijimaci fronty
    andi t0, t0, SERP_RX_ST_REG_READY_m # vymaskuj bit READY
    beq  t0, zero, rx_not_ready    # pokud neni znak UARTU cekej zkus to znovu
    lw   t1, SERP_RX_DATA_REG_o(a0) # je znak - precti ho a tim odstran
    sb   t1, 0(a1)            # uloz znak do bufferu
    addi t1, t1, -13          # test je to novy radek?
    beq  t1, zero, end_char   # ukoncuje cteni
    addi a1, a1, 1            # posun ukazatel
    addi t2, t2, -1            # kontroluj kolik muzeme nacist
    bne  t2, zero, next_char
end_char:
    ebreak                    # ukonci program
    .data
text_1:
    .word 0,0,0,0,0,0,0,0,0,0

Program pro práci s otočnými voliči v QtRvSim

#base of SPILED port region
.equ SPILED_REG_BASE,       0xffffc100

#RGB LED 1 barevne slozky – 8 bitu kazda
.equ SPILED_REG_LED_RGB1,   0xffffc110
.equ SPILED_REG_LED_RGB1_o,   0x0010

#RGB LED 2 barevne slozky – 8 bitu kazda
.equ SPILED_REG_LED_RGB2,   0xffffc114
.equ SPILED_REG_LED_RGB2_o,   0x0014

#Tri 8 bitove otocne volice
#nejvyssi bajt informace o stitsknuti
.equ SPILED_REG_KNOBS_8BIT, 0xffffc124 
.equ SPILED_REG_KNOBS_8BIT_o, 0x0024

#32 LEDek kazdy bit jedna LED dioda
.equ SPILED_REG_LED_LINE,   0xffffc104
.equ SPILED_REG_LED_LINE_o,   0x0004

    li   a0, SPILED_REG_BASE # a0 ukazuje na zacatek pameti pro I/O
    ori  t2, t2, -1
loop:
    lw   t0, SPILED_REG_KNOBS_8BIT_o(a0)   # nacti data od otocnych volicu
    sw   t0, SPILED_REG_LED_RGB1_o(a0)
    xor  t1, t0, t2
    sw   t1, SPILED_REG_LED_RGB2_o(a0)
    srli t0, t0, 24
    andi t0, t0, 4
    beq  t0, zero, loop    # pokud nebyl zmacknut cerveny volic

    ebreak                    # ukonci program

Volání služby operačního systému

Příklad použití systémového volání

template-os.S

.globl _start
.globl __start
.option norelax

// Linux kernel compatible system calls subset

.equ __NR_exit,        93  // void exit(int status)
.equ __NR_read,        63  // ssize_t read(int fd, void *buf, size_t count)
.equ __NR_write,       64  // ssize_t write(int fd, const void *buf, size_t count)
.equ __NR_close,       57  // int close(int fd)
.equ __NR_openat,      56  // int openat(int fd, const char *pathname, int flags, mode_t mode)
	// use fd = -100 for normal open behaviour. Full openat not supported.
.equ __NR_brk,         214 // void * brk(void *addr)
.equ __NR_ftruncate64, 46  // int ftruncate64(int fd, off_t length)
.equ __NR_readv,       65  // ssize_t readv(int fd, const struct iovec *iov, int iovcnt)
.equ __NR_writev,      66  // ssize_t writev(int fd, const struct iovec *iov, int iovcnt)

.text

__start:
_start:
	addi  a7, zero, __NR_write        // load syscall number
	addi  a0, zero, 1                 // load file descriptor
	addi  a1, zero, text_1            // load text address
	addi  a2, zero, text_1_e - text_1 // load text length
	ecall                             // print the text

	addi  a7, zero, __NR_exit         // load syscall numver
	addi  a0, zero, 0                 // load status argument
	ecall                             // exit

final:
	ebreak                            // request developer interaction
	jal   zero, final

.data
.org 0x400

data_1:	.word	1, 2, 3, 4

text_1:	.ascii	"Hello world.\n"	// store ASCII text, no termination
text_1_e:

Odkazy

courses/b35apo/tutorials/08/start.txt · Last modified: 2024/02/02 18:41 (external edit)