Search
Zopakujte si základy programování v jazyce C
Prostředí POSIX a program Make
Jednoduchý program 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/qtmips_binrep (také v stud-support). Program se shodnou funkcí pro desku MZ_APO naleznete v adresáři /opt/apo/binrep/mzapo_binrep (také v stud-support). Porovnání analýzy kódu zkompilovaného pro architekturu ARM naleznete na konci dnešního cvičení.
/opt/apo/qtmips_binrep
/opt/apo/binrep/mzapo_binrep
K přihlášení do operačního systému GNU/Linux běžícímu na desce je možné použít protokol SSH.
ssh -i /opt/zynq/ssh-connect/mzapo-root-key root@192.168.202.xxx
Do adresářové struktury cílového systému je do adresářů /opt/apo a /opt/zynq připojený shodný svazek/obsah jako je na hostitelském PC
/opt/apo
/opt/zynq
Nachází se zde i adresář se zdrojovými kódy programu pro test binární reprezentace
/opt/apo/binrep/print_binrep
Adresář vidí jak na host systému (PC) tak na target systému (Zynq). Ale na obou je jen read-only. Pro kompilaci je potřeba adresář zkopírovat a spustit v něm příkaz
cp -r /opt/apo/binrep/print_binrep /root cd /root/print_binrep make
Pro vynucení překompilování lze použít cíl clean pro smazání a cíl all pro opětovnou kompilaci. Cíl all je shodný s prvním uvedeným cílem, který je vybraný při volání bez parametrů.
clean
all
make clean all
Na cílovém systému je přidaná i kopie zdrojového kódu v adresáři /root/apo/binrep/print_binrep, ten je stejně jako většina systému překrytý pod overlayem, takže lze přímo používat. Pozor, po restartu systému budou veškerá data ztracena. Vše se odehrává jen v lokální RAM (tmpfs, zjednodušeně RAM disk). Jednoduchá kompilace a spuštění
/root/apo/binrep/print_binrep
cd /root/apo/binrep/print_binrep make ./print_binrep
Přímo na cílovém systému lze zdrojové kódy editovat programy
Ty jsou k dispozici i na hostitelském PC, kde jsou navíc nainstalované i editory/prostředí
#define _POSIX_C_SOURCE 200112L #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <time.h> #include <unistd.h> #include "mzapo_parlcd.h" #include "mzapo_phys.h" #include "mzapo_regs.h" int main(int argc, char *argv[]) { unsigned char *mem_base; uint32_t val_line=5; int i; printf("Hello world\n"); sleep(1); /* * Setup memory mapping which provides access to the peripheral * registers region of RGB LEDs, knobs and line of yellow LEDs. */ mem_base = map_phys_address(SPILED_REG_BASE_PHYS, SPILED_REG_SIZE, 0); /* If mapping fails exit with error code */ if (mem_base == NULL) exit(1); for (i=0; i<30; i++) { *(volatile uint32_t*)(mem_base + SPILED_REG_LED_LINE_o) = val_line; val_line<<=1; printf("LED val 0x%x\n", val_line); sleep(1); } printf("Goodbye world\n"); return 0; }
#define _POSIX_C_SOURCE 200112L #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <time.h> #include <unistd.h> #include "mzapo_parlcd.h" #include "mzapo_phys.h" #include "mzapo_regs.h" int main(int argc, char *argv[]) { unsigned char *mem_base; unsigned char *parlcd_mem_base; uint32_t val_line=5; int i,j,k; unsigned int c; printf("Hello world\n"); sleep(1); /* * Setup memory mapping which provides access to the peripheral * registers region of RGB LEDs, knobs and line of yellow LEDs. */ mem_base = map_phys_address(SPILED_REG_BASE_PHYS, SPILED_REG_SIZE, 0); /* If mapping fails exit with error code */ if (mem_base == NULL) exit(1); struct timespec loop_delay = {.tv_sec = 0, .tv_nsec = 20 * 1000 * 1000}; for (i=0; i<30; i++) { *(volatile uint32_t*)(mem_base + SPILED_REG_LED_LINE_o) = val_line; val_line<<=1; printf("LED val 0x%x\n", val_line); clock_nanosleep(CLOCK_MONOTONIC, 0, &loop_delay, NULL); } parlcd_mem_base = map_phys_address(PARLCD_REG_BASE_PHYS, PARLCD_REG_SIZE, 0); if (parlcd_mem_base == NULL) exit(1); parlcd_hx8357_init(parlcd_mem_base); parlcd_write_cmd(parlcd_mem_base, 0x2c); for (i = 0; i < 320 ; i++) { for (j = 0; j < 480 ; j++) { c = 0; parlcd_write_data(parlcd_mem_base, c); } } parlcd_write_cmd(parlcd_mem_base, 0x2c); for (i = 0; i < 320 ; i++) { for (j = 0; j < 480 ; j++) { c = ((i & 0x1f) << 11) | (j & 0x1f); parlcd_write_data(parlcd_mem_base, c); } } loop_delay.tv_sec = 0; loop_delay.tv_nsec = 200 * 1000 * 1000; for (k=0; k<60; k++) { parlcd_write_cmd(parlcd_mem_base, 0x2c); for (i = 0; i < 320 ; i++) { for (j = 0; j < 480 ; j++) { c = (((i+k) & 0x1f) << 11) | ((j+k) & 0x1f); parlcd_write_data(parlcd_mem_base, c); } } clock_nanosleep(CLOCK_MONOTONIC, 0, &loop_delay, NULL); } printf("Goodbye world\n"); return 0; }
/******************************************************************* Project main function template for MicroZed based MZ_APO board designed by Petr Porazil at PiKRON change_me.c - main file include your name there and license for distribution. Remove next text: This line should not appear in submitted work and project name should be change to match real application. If this text is there I want 10 points subtracted from final evaluation. *******************************************************************/ #define _POSIX_C_SOURCE 200112L #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <time.h> #include <unistd.h> #include "mzapo_parlcd.h" #include "mzapo_phys.h" #include "mzapo_regs.h" #include "font_types.h" unsigned short *fb; void draw_pixel(int x, int y, unsigned short color) { if (x>=0 && x<480 && y>=0 && y<320) { fb[x+480*y] = color; } } void draw_char(int x, int y, font_descriptor_t* fdes, char ch) { } int char_width(font_descriptor_t* fdes, int ch) { int width = 0; if ((ch >= fdes->firstchar) && (ch-fdes->firstchar < fdes->size)) { ch -= fdes->firstchar; if (!fdes->width) { width = fdes->maxwidth; } else { width = fdes->width[ch]; } } return width; } int main(int argc, char *argv[]) { unsigned char *mem_base; unsigned char *parlcd_mem_base; uint32_t val_line=5; int i,j,k; int ptr; unsigned int c; fb = (unsigned short *)malloc(320*480*2); printf("Hello world\n"); sleep(1); /* * Setup memory mapping which provides access to the peripheral * registers region of RGB LEDs, knobs and line of yellow LEDs. */ mem_base = map_phys_address(SPILED_REG_BASE_PHYS, SPILED_REG_SIZE, 0); /* If mapping fails exit with error code */ if (mem_base == NULL) exit(1); struct timespec loop_delay = {.tv_sec = 0, .tv_nsec = 20 * 1000 * 1000}; for (i=0; i<30; i++) { *(volatile uint32_t*)(mem_base + SPILED_REG_LED_LINE_o) = val_line; val_line<<=1; printf("LED val 0x%x\n", val_line); clock_nanosleep(CLOCK_MONOTONIC, 0, &loop_delay, NULL); } parlcd_mem_base = map_phys_address(PARLCD_REG_BASE_PHYS, PARLCD_REG_SIZE, 0); if (parlcd_mem_base == NULL) exit(1); parlcd_hx8357_init(parlcd_mem_base); parlcd_write_cmd(parlcd_mem_base, 0x2c); ptr=0; for (i = 0; i < 320 ; i++) { for (j = 0; j < 480 ; j++) { c = 0; fb[ptr]=c; parlcd_write_data(parlcd_mem_base, fb[ptr++]); } } loop_delay.tv_sec = 0; loop_delay.tv_nsec = 150 * 1000 * 1000; for (k=0; k<60; k++) { for (ptr = 0; ptr < 320*480 ; ptr++) { fb[ptr]=0u; } // pixel (x,y) -> fb[x+y*480] for (i=0; i<200; i++) { for (j=0; j<20; j++) { fb[(i+k)+j+(i+k)*480]=0x1f<<5; } } parlcd_write_cmd(parlcd_mem_base, 0x2c); for (ptr = 0; ptr < 480*320 ; ptr++) { parlcd_write_data(parlcd_mem_base, fb[ptr]); } clock_nanosleep(CLOCK_MONOTONIC, 0, &loop_delay, NULL); } int x = 10; char str[]="Goodbye world"; char *ch=str; font_descriptor_t* fdes = &font_winFreeSystem14x16; for (ptr = 0; ptr < 320*480 ; ptr++) { fb[ptr]=0u; } for (i=0; i<13; i++) { draw_char(x, 10, fdes, *ch); x+=char_width(fdes, *ch); ch++; } printf("Goodbye world\n"); return 0; }
K vývojovému kitu MZ_APO je připojený grafický displej s lokálním řadičem, který se stará o obnovování obrazu na TFT LCD display s rozměry 480 x 320 bodů.
Řadič přijímá příkazy zapisované na 16-bitovou bránu mapovanou do fyzického paměťového prostoru na adresu 0x43c00008 (PARLCD_REG_BASE_PHYS+PARLCD_REG_CMD_o).
Většina příkazů pak vyžaduje pro provedení vlastní operace zapsání dat. Data je potřeba zapsat na adresu 0x43c0000c (PARLCD_REG_BASE_PHYS+PARLCD_REG_DATA_o).
Příklad, jak zpřístupnit určitý rozsah fyzických adres pro práci z programu napsaného v jazyce C naleznete v prvním cvičení. Příklady použití mapování v aplikacích naleznete v adresářích
/opt/apo/lcd/mzapo_lcdtest
Kontrolér displeje vyžaduje konfiguraci množství parametrů pro nastavení parametrů volbu propojení vlastního LCD (“skla”) a pro nastavení napěťových úrovní a časování aby byl obraz stabilní a kontrastní.
Tuto funkci však není nutné z vašich programů provádět, protože počáteční nastavení provede již aplikace pro zobrazení IP adresy na displeji.
Pro zobrazení grafického výstupu tedy stačí vyslat příkaz 0x2c, který je následovaný požadovanými barvami jednotlivých bodů zadávaných ve formátu RGB 565 postupně řádek po řádku.
Požadovaný grafický výstup je možné připravovat přímo během vysílání dat na display (takto jsou implementované předložené testovací programy), ale pro přípravu složitějšího výstupu je mnohem vhodnější vykreslovat objekty to bloku paměti (frame-buffer) a poté vyslat dokončený obsah na display.
uint16_t *fb = (uint16_t*)malloc(sizeof(uint16_t) * width * height)
Úkoly
Pro zdatnější je pak možné přidat nastavování barvy obdélníku s využitím rotačních voličů. Případně je možné voliči měnit polohu objektů nebo jejich velikost. Režim je možné přepínat například stlačením voliče.
Kompletní popis příkazů, kterými je možné konfigurovat řadič displeje naleznete v souboru
/opt/apo/zynq/doc/mz_apo/components/HX8357-C.pdf
Simulátor obsahuje jednoduchý model grafického displaye s rozlišením 480 x 320 bodů. V okně LCD display je zobrazovaná paměť jednotlivých bodů, která začíná na adrese 0xffe00000. Body jsou uložené za sebou postupně v řádcích s tím, že každý bod je reprezentovaný 16-bitovou hodnotou stejně jako na přípravku MZ_APO. Nejvýznamnějších 5 bitů odpovídá červené složce, poté následuje 6 bitů zelené složky a nakonec 5 bitů modré složky. QtRvSim je stejně jako varianta architektury ARM použitá na kitu na MZ_APO little-endian, takže chování je podobné. Pozor, pokud při testování na simulátoru QtMips, který v standardním nastavením simuluje architekturu MIPS ve variantě big-endian. Při jiném přístupu než 16-bit unsigned integer (například po slovech, nebo bytech) je chování jeho grafické “video” paměti odlišné od přípravku MZ_APO a QtRvSim.
Existuje mnoho možností jak definovat glyfy (grafické podoby) jednotlivých znaků, číslic, symbolů. Pokročilejší metody využívající popis glyfu křivkami umožňují zobrazit znak o libovolné velikosti. Jednodušší řešení přímo definuje podobu písmene informací, které body mají být nastaveny na barvu textu. Ostatní jsou ponechány s původní barvou nebo jsou vyplněné barvou pozadí.
Příkladem definice glyfů přímo binárně v matici 14×16 bodů může být následující soubor
https://github.com/ghaerr/microwindows/blob/master/src/fonts/winFreeSystem14x16.c
z projektu Microwindows.
Pole winFreeSystem14x16_bits definuje nastavení jednotlivých bitů v matici. Typ prvků pole MWIMAGEBITS odpovídá typu uint16_t. Nejvíce významný bit (MSB, bit 15) odpovídá nejlevějšímu bodu znaku/symbolu. Postupně následují další body v řádce. Nejméně významné bity pak většinou nejsou využité, znaky fontu 14×16 jsou užší než 16 bodů. Další položka pole odpovídá dalšímu řádku.
Pole winFreeSystem14x16_offset není pro daný font používané, protože výška každého znaku/symbolu je 16 bodů, to je 16 16-bitových čísel. Přitom první grafická representace odpovídá v kódování ASCII znaku mezera (0x20, 32 dekadicky). Pole winFreeSystem14x16_width definuje šířku každého znaku v bodech. Další informace a parametry již nejsou pro implementaci jednoduchého zobrazování textů podstatné.
Soubory s dalšími definicemi fontů v různé velkosti naleznete v adresáři
/opt/apo/lcd/fonts
počítačů v laboratoři.
Největší velikost obsahuje jen číslice. Některé fonty obsahují i několik úseků z kódové tabulky Unicode a pokrývají tak kompletně potřeby českého jazyka, Ruštiny i některé arabské jazyky. Základ aplikace s již definovanými hlavičkovými soubory pro použití fontů naleznete v adresáři
/opt/apo/mzapo_template
Aplikace je též dostupná z fakultního serveru GIT https://gitlab.fel.cvut.cz/b35apo/mzapo_template.
git clone https://gitlab.fel.cvut.cz/b35apo/mzapo_template.git
Datový typ použitý pro definice fontů naleznete v souboru font_types.h.
Jednoduchý program, který kopíruje hodnotu z RGB voličů na RGB LED a řádku LED diod.
Program naleznete v adresáři /opt/apo/binrep/mzapo_binrep a na cílovém systému i v adresáři /root/apo/binrep/print_binrep.
/******************************************************************* Simple program to demostrate binary reprezentation on MicroZed based MZ_APO board designed by Petr Porazil at PiKRON mzapo_binrep.c - main and only file (C) Copyright 2004 - 2017 by Pavel Pisa e-mail: pisa@cmp.felk.cvut.cz homepage: http://cmp.felk.cvut.cz/~pisa work: http://www.pikron.com/ license: any combination GPL, LGPL, MPL or BSD licenses *******************************************************************/ #define _POSIX_C_SOURCE 200112L #include <sys/mman.h> #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <unistd.h> #include <fcntl.h> #include <malloc.h> #include <string.h> #include <byteswap.h> #include <getopt.h> #include <inttypes.h> #include <time.h> char *memdev="/dev/mem"; /* * Next macros provides location of knobs and LEDs peripherals * implemented in MZ_APO FPGA design. * * The complete list of peripheral implemented in the design * can be found on the page * https://cw.fel.cvut.cz/wiki/courses/b35apo/documentation/mz_apo/start */ /* * Base address of the region used for mapping of the knobs and LEDs * peripherals in the ARM Cortex-A9 physical memory address space. */ #define SPILED_REG_BASE_PHYS 0x43c40000 /* Valid address range for the region */ #define SPILED_REG_SIZE 0x00004000 /* * Byte offset of the register which controls individual LEDs * in the row of 32 yellow LEDs. When the corresponding bit * is set (value 1) then the LED is lit. */ #define SPILED_REG_LED_LINE_o 0x004 /* * The register to control 8 bit RGB components of brightness * of the first RGB LED */ #define SPILED_REG_LED_RGB1_o 0x010 /* * The register to control 8 bit RGB components of brightness * of the second RGB LED */ #define SPILED_REG_LED_RGB2_o 0x014 /* * The register which combines direct write to RGB signals * of the RGB LEDs, write to the keyboard scan register * and control of the two additional individual LEDs. * The direct write to RGB signals is orred with PWM * signal generated according to the values in previous * registers. */ #define SPILED_REG_LED_KBDWR_DIRECT_o 0x018 /* * Register providing access to unfiltered encoder channels * and keyboard return signals. */ #define SPILED_REG_KBDRD_KNOBS_DIRECT_o 0x020 /* * The register representing knobs positions as three * 8-bit values where each value is incremented * and decremented by the knob relative turning. */ #define SPILED_REG_KNOBS_8BIT_o 0x024 /* * The support function which returns pointer to the virtual * address at which starts remapped physical region in the * process virtual memory space. */ void *map_phys_address(off_t region_base, size_t region_size, int opt_cached) { unsigned long mem_window_size; unsigned long pagesize; unsigned char *mm; unsigned char *mem; int fd; /* * Open a device ("/dev/mem") representing physical address space * in POSIX systems */ fd = open(memdev, O_RDWR | (!opt_cached? O_SYNC: 0)); if (fd < 0) { fprintf(stderr, "cannot open %s\n", memdev); return NULL; } /* * The virtual to physical address mapping translation granularity * corresponds to memory page size. This call obtains the page * size used by running operating system at given CPU architecture. * 4kB are used by Linux running on ARM, ARM64, x86 and x86_64 systems. */ pagesize=sysconf(_SC_PAGESIZE); /* * Extend physical region start address and size to page size boundaries * to cover complete requested region. */ mem_window_size = ((region_base & (pagesize-1)) + region_size + pagesize-1) & ~(pagesize-1); /* * Map file (in our case physical memory) range at specified offset * to virtual memory ragion/area (see VMA Linux kernel structures) * of the process. */ mm = mmap(NULL, mem_window_size, PROT_WRITE|PROT_READ, MAP_SHARED, fd, region_base & ~(pagesize-1)); /* Report failure if the mmap is not allowed for given file or its region */ if (mm == MAP_FAILED) { fprintf(stderr,"mmap error\n"); return NULL; } /* * Add offset in the page to the returned pointer for non-page-aligned * requests. */ mem = mm + (region_base & (pagesize-1)); return mem; } /* * The main entry into example program */ int main(int argc, char *argv[]) { unsigned char *mem_base; /* * Setup memory mapping which provides access to the peripheral * registers region of RGB LEDs, knobs and line of yellow LEDs. */ mem_base = map_phys_address(SPILED_REG_BASE_PHYS, SPILED_REG_SIZE, 0); /* If mapping fails exit with error code */ if (mem_base == NULL) exit(1); while (1) { uint32_t rgb_knobs_value; int int_val; unsigned int uint_val; /* Initialize structure to 0 seconds and 200 milliseconds */ struct timespec loop_delay = {.tv_sec = 0, .tv_nsec = 200 * 1000 * 1000}; /* * Access register holding 8 bit relative knobs position * The type "(volatile uint32_t*)" casts address obtained * as a sum of base address and register offset to the * pointer type which target in memory type is 32-bit unsigned * integer. The "volatile" keyword ensures that compiler * cannot reuse previously read value of the location. */ rgb_knobs_value = *(volatile uint32_t*)(mem_base + SPILED_REG_KNOBS_8BIT_o); /* Store the read value to the register controlling individual LEDs */ *(volatile uint32_t*)(mem_base + SPILED_REG_LED_LINE_o) = rgb_knobs_value; /* * Store RGB knobs values to the corersponding components controlling * a color/brightness of the RGB LEDs */ *(volatile uint32_t*)(mem_base + SPILED_REG_LED_RGB1_o) = rgb_knobs_value; *(volatile uint32_t*)(mem_base + SPILED_REG_LED_RGB2_o) = rgb_knobs_value; /* Assign value read from knobs to the basic signed and unsigned types */ int_val = rgb_knobs_value; uint_val = rgb_knobs_value; /* Print values */ printf("int %10d uint 0x%08x\n", int_val, uint_val); /* * Wait for time specified by "loop_delay" variable. * Use monotonic clocks as time reference to ensure * that wait interval is not prolonged or shortened * due to real time adjustment. */ clock_nanosleep(CLOCK_MONOTONIC, 0, &loop_delay, NULL); } return 0; }
Program je zkompilovaný překladačem GCC pro architekturu ARM Cortex-A (arm-linux-gnueabihf-gcc). Pro zkrácení kódu je využitá varianta kódování instrukcí Thumb, která kombinuje instrukce kódované do 16 a 32-bitů. Pro dekódování byl použitý nástroj objdump
arm-linux-gnueabihf-objdump --source mzapo_binrep | less
Při kompilaci nebyla požadovaná žádná optimalizace, proto kód opakovaně přistupuje do lokálních proměnných a řádky v assembleru lze velmi dobře porovnávat s řádky zdrojového kódu.
Pro případ bez optimalizace
rgb_knobs_value = *(volatile uint32_t*)(mem_base + SPILED_REG_KNOBS_8BIT_o); load value of the local variable "mem_base" from the memory location relative to the register r7 106e6: 69fb ldr r3, [r7, #28] read value from the address corresponding to the sum of "mem_base" and "SPILED_REG_KNOBS_8BIT_o" peripheral register offset 106e8: 6a5b ldr r3, [r3, #36] ; 0x24 store read value to the local variable "rgb_knobs_value" 106ea: 61bb str r3, [r7, #24] *(volatile uint32_t*)(mem_base + SPILED_REG_LED_LINE_o) = rgb_knobs_value; read "mem_base" 106ec: 69fb ldr r3, [r7, #28] use short form (Thumb) instruction to add "SPILED_REG_LED_LINE_o" 106ee: 3304 adds r3, #4 read "rgb_knobs_value" local variable 106f0: 69ba ldr r2, [r7, #24] store "rgb_knobs_value" to the LED line peripheral register 106f2: 601a str r2, [r3, #0] *(volatile uint32_t*)(mem_base + SPILED_REG_LED_RGB1_o) = rgb_knobs_value; load "mem_base" 106f4: 69fb ldr r3, [r7, #28] add offset of the first RGB LED 106f6: 3310 adds r3, #16 load "rgb_knobs_value" 106f8: 69ba ldr r2, [r7, #24] store value to control color 106fa: 601a str r2, [r3, #0] *(volatile uint32_t*)(mem_base + SPILED_REG_LED_RGB2_o) = rgb_knobs_value; load "mem_base" 106fc: 69fb ldr r3, [r7, #28] add offset of the second RGB LED 106fe: 3314 adds r3, #20 load "rgb_knobs_value" 10700: 69ba ldr r2, [r7, #24] store value to control color 10702: 601a str r2, [r3, #0] int_val = rgb_knobs_value; load "rgb_knobs_value" 10704: 69bb ldr r3, [r7, #24] store value to "int_val" 10706: 617b str r3, [r7, #20] no conversion between unsigned 32-bit value and signed signed int. They are of the same size and there is no conversion between the unsigned and the second complement signed representation. The half range silently overflows/wraps. uint_val = rgb_knobs_value; load "rgb_knobs_value" 10708: 69bb ldr r3, [r7, #24] store value to "uint_val" 1070a: 613b str r3, [r7, #16] no conversion, the same size printf("int %10d uint 0x%08x\n", int_val, uint_val); the first argument register (r0 .. a0) is set to the start address of the format string 1070c: f240 70ac movw r0, #1964 ; 0x7ac 10710: f2c0 0001 movt r0, #1 load "int_val" into the second argument register (r1 .. a1) 10714: 6979 ldr r1, [r7, #20] load "uint_val" into the third argument register (r2 .. a2) 10716: 693a ldr r2, [r7, #16] call the function 10718: f7ff eea8 blx 1046c <printf@plt>
Pro případ se základní optimalizací -O1
Setup memory mapping which provides access to the peripheral registers region of RGB LEDs, knobs and line of yellow LEDs. mem_base = map_phys_address(SPILED_REG_BASE_PHYS, SPILED_REG_SIZE, 0); 106b8: 2200 movs r2, #0 106ba: f44f 4180 mov.w r1, #16384 ; 0x4000 106be: 4610 mov r0, r2 argument 0 (r0) set to physical base of the SPILED registers block. Use instruction to set upper 16 bits of the register 106c0: f2c4 30c4 movt r0, #17348 ; 0x43c4 106c4: f7ff ffa4 bl 10610 <map_phys_address> /* If mapping fails exit with error code */ if (mem_base == NULL) 106c8: b910 cbnz r0, 106d0 <main+0x1c> exit(1); 106ca: 2001 movs r0, #1 106cc: f7ff ef18 blx 10500 <exit@plt> else setup r4 to virtual base of the mapping 106d0: 4604 mov r4, r0 prepare address of text "int %10d uint 0x%08x\n" outside of the loop, the 16 lower bits the first 106dc: f240 7670 movw r6, #1904 ; 0x770 set upper 16 bits 106e0: f2c0 0601 movt r6, #1 prepare value for struct timespec loop_delay 106e4: 9500 str r5, [sp, #0] 106e6: 9701 str r7, [sp, #4] rgb_knobs_value = *(volatile uint32_t*)(mem_base + SPILED_REG_KNOBS_8BIT_o); read value from the address corresponding to the sum of "mem_base" and "SPILED_REG_KNOBS_8BIT_o" peripheral register offset 106e8: 6a61 ldr r1, [r4, #36] ; 0x24 *(volatile uint32_t*)(mem_base + SPILED_REG_LED_LINE_o) = rgb_knobs_value; store "rgb_knobs_value" to the LED line peripheral register 106ea: 6061 str r1, [r4, #4] *(volatile uint32_t*)(mem_base + SPILED_REG_LED_RGB1_o) = rgb_knobs_value; store value to color register LED1 106ec: 6121 str r1, [r4, #16] *(volatile uint32_t*)(mem_base + SPILED_REG_LED_RGB2_o) = rgb_knobs_value; store value to color register LED2 106ee: 6161 str r1, [r4, #20] printf("int %10d uint 0x%08x\n", int_val, uint_val); copy value of argument 1 (r1) even to argument 2 (r2) 106f0: 460a mov r2, r1 set argument r0 from saved register r6 106f2: 4630 mov r0, r6 106f4: f7ff eeda blx 104ac <printf@plt>