Oblig 1 - Introduksjon til Questa og ARM assembler kompilering

M?let med denne obligen er ? bli kjent med verkt?y som skal brukes i oblig 2 og 3. Obligen best?r av to deler. Del 1 er en introduksjon til Questa, som du vil bruke for ? simulere HDL (Hardware Description Language) kode i oblig 2. I del 2 skal du bli kjent med et av to verkt?y du kan bruke for ? kompilere og kj?re ARM assembly kode, enten en Raspberry Pi, eller en emulator. Vanligvis ville alle p? kurset gjort oblig 3 p? Raspberry Pi, men ettersom det n? kan v?re vanskelig ? f? tak i Raspberry Pi er det n?dvendig med et digitalt alternativ. I ?r er det derfor satt opp et system for emulering av ARM assembler kode. Emulatoren kan brukes p? linux maskinene p? ifi, p? IFIs ssh login cluster, eller p? den virituelle maskinen Ifi Workstation.

Del 1 - Bli kjent med simuleringsverkt?y for digital design

M?let med denne oppgaven er bli kjent med hvordan vi benytter simuleringsverkt?y ved utvikling av hardware. Verkt?yet vi benytter heter Questa og er et kompilerings- og simuleringsverkt?y for hardwarespr?k (b?de VHDL og verilog). Skal man kunne analysere og konstruere digitale kretser m? man kjenne til hvordan slike verkt?y virker og bruke minst ett. Questa er en videreutvikling av Modelsim, som er mye brukt i industrien. De st?rste leverand?rene for programmerbar logikk har typisk integrerte varianter av modelsim i verkt?yene de tilbyr for utvikling med sine kretser.

F?r vi kommer til simuleringen, s? m? vi f? laget en modul som kan kompileres og simuleres.

  1. Lag en egen folder p? UIO-hjemmeomr?det ditt til bruk for denne oppgaven
  2. I den f?rste oppgaven har vi kode som skal lagres som en fil med navn halfadder.vhd. Skriv inn programmet nedenfor med et redigeringsverkt?y1 og lagre det i den nye folderen. Det anbefales ? skrive teksten manuelt inn i starten, for at oppbyggingen, n?kkelord og syntaksen skal sette seg i fingrene.

1Et hvilket som helst tekstredigeringsverkt?y kan benyttes, bare det kan lagre rene tekstfiler. Det anbefales ? bruke verkt?y som holder styr p? linjenummer, har monospace font, og kan highlighte n?kkelord i VHDL med forskjellige farger. Eksempler kan v?re Atom, Gedit, Emacs eller Notepad++. Questa har ogs? en egen editor for dette form?let.

Bilde
Figur 1 - Halvadder

library IEEE; -- bibliotek
  use IEEE.STD_LOGIC_1164.all; -- pakke

entity halfadder is
  port(
    a, b : in std_logic; -- inputs
    s : out std_logic; -- sum
    c : out std_logic -- carry
  );
end entity halfadder;

architecture dataflow of halfadder is
begin
  s <= a xor b
  c <= a and b;
end architecture dataflow;

N?r du ha lagret filen, skal du ?pne filen i Questa. Questa er installert p? linux-systemet til ifi. For ? benytte Questa, s? m? linuxmilj?et ditt settes opp slik at lisensiering kan fungere og at systemet kjenner de stiene som er n?dvendige ? kj?re programmet. Hvordan du finner Questa er beskrevet p? Questa Sim siden p? wikien til robin, http://robin.wiki.ifi.uio.no. Hvis du ikke har direkte tilgang til linuxmaskinene p? ifi, (for eksempel pga smittevernsrestriksjoner), s? kan bruke en VMwaremaskin eller ssh til login.ifi.uio.no n?rmere beskrivelse finnes p? Remote access og FPGA tools sidene. Merk at du trenger bare at én av metodene virker for deg.

Etter at systemet er satt opp kan du kj?re "vsim" i terminalen. "vsim" starter Questa sitt grafiske grensesnitt (GUI).

I Questa:

  1. Lag et nytt prosjekt i questa som f?lger: 
    1. File-> new Project… , velg folderen du lagde i 1), og gi prosjektet et navn. 
    2. Trykk ?ok?, 
    3. Velg ?Add existing files?, og legg til kildefila halfadder.vhd. Senere kan du h?yreklikke i prosjektvinduet og velge ?Add to project? for ? legge til eller opprette filer2.
  2. Kompilér filen (?Compile? / ?Compile all?) og rett eventuelle feil. Kommenter rettingen du gj?r i kildekoden.

Merk: Det er normalt at det blir en del feil ved f?rste kompilering. ? kopiere koden for h?nd kan lett gi flere feil. I koden til halvadderen mangler det ett semikolon p? den tredje siste linjen. Kompilatoren b?r finne dette dersom den er kopiert inn eksakt. Ved ? dobbeltklikke p? filen med feil (markert med r?d ‘X’), eller den r?de teksten i ?transcript? vinduet f?r du frem feillisten. Noen feil kan f?re til at mange linjer i koden blir tolket som feil, uten at de n?dvendigvis er det. Hindrer en feil kompilatoren i ? jobbe videre, kommer andre feil f?rst til syne n?r man kompilerer p? ny. I starten kan det v?re lurt ? rette én feil av gangen, og heller kompilere mange ganger.

2N?r man benytter egne filer er det som regel lurt ? velge ?Reference from current location? n?r man legger til filer i et prosjekt. Uten un?dvendige duplikatfiler er sjansen mindre for at man redigerer én fil og tester en annen.

  1. N?r koden kompilerer skal du simulere: Velg Simulate-> Start simulation. I vinduet som kommer opp velger du ?work->halfadder?, og trykker p? ?Optimization Options? (Dersom din versjon av programmet har en opsjon som heter ?Enable_optimization? skal denne v?re skrudd p?)
  2. I ?Optimization Options? under fanen ?Visibility? velg “Apply full visibility to all modules (full debug mode)” og trykk OK og s? OK igjen, slik at simuleringsvinduene ?pnes (tar litt tid).
  3. Legg til signalene (a, b, s, c) fra halvadderen inn i waveform vinduet. (Velg signalene fra det m?rkebl? vinduet, og h?yreklikk med musen og velg ?add wave?)
  4. Simuler i 100 nanosekund (skriv ?run 100 ns? i transcript vinduet).
  5. Zoom ut slik at du ser hele waveformen i bildet (H?yreklikk i vinduet med waveforms og velg ?zoom full?).

I wave-vinduet, vil du n? se fire r?de streker, en for hvert signal, samt at det st?r U i kolonnen Msgs ved siden av signalnavnene. Det betyr at den gule linja (cursoren) st?r et sted der signalene er ?uninitialized? (derav U). For ? f? noe vettugt ut av et digitalt design, m? vi gi inngangssignalene en verdi.

  1. H?yreklikk p? a-signalet i wave-vinduet, velg ?Force? og sett verdien (value) til 0 De andre egenskapene i vinduet trenger du ikke endre p? n?.
  2. Gj?r det samme for B signalet 
  3. Kj?r 1 mikrosekund til (?run 1 us?), zoom fullt ut igjen og flytt cursoren til der signalene er gr?nne og les av.

P? dette tidspunktet b?r alle signalene kunne leses av som 0.

  1. Test alle mulighetene som f?lger
    1. Sett s? A til 1 og kj?r ett mikrosekund,
    2. dernest A til 0 og B til 1 og kj?r ett mikrosekund
    3. og s? begge til 1 og kj?r ett mikrosekund.
  2. Zoom ut fullt igjen, og sjekk at du f?r riktige verdier for sum (s) og carry (c).
  3. Lag en bildekopi av Waveformvinduet til rapporten:
    1. Se til at Wave vinduet er aktivt (det aktive vinduet er det du trykket p? sist)
    2. I wave-vinduet (eller den ?verste menyen til Questa), trykk File->Export->image
    3. Skift bildetype til png
    4. Lagre bildet som halfadder.png. Innleveringen i denne oppgaven skal best? av bildefilen halfadder.png.

Innleveringen i denne oppgaven skal best? av bildefilen halfadder.png.

Etterord til Del 1

Det er mange m?ter ? gj?re simuleringer med Questa. Denne oppgaven tar for seg den enkleste og mest grunnleggende m?ten ? gj?re det p?. Metoden kan benyttes for ? teste i ukesoppgaver og i den neste obligen i kurset.

I den neste obligatoriske oppgaven i IN2060 vil dere ogs? benytte testbenker3 som kan sette signaler automatisk. ? bruke testbenker er som regel raskere enn ? teste ting manuelt, men man vil ofte ha behov for begge deler, for eksempel hvis man vil se om en modul gj?r det den skal helt i starten, eller teste ?en ting til? etter at testbenkkoden er kj?rt.

N?r vi setter signaler med ?force? overskriver vi verdiene signalet hadde fra andre kilder i simulatoren. Hvis man bruker dette p? ? sette utgangssignaler fra en modul, kan det kamuflere feil som ellers ville vises i simulering. I praksis er dette sjeldent et problem, siden vi bruker testbenker aller mest, men det er greit ? v?re klar over slik at man ikke lurer seg selv.

3En testbenk kan typisk v?re en VHDL modul som er laget for ? sette signaler til en annen modul som man vil teste. Man kan ogs? lage testbenker med andre programspr?k eller script, men det er ikke pensum i IN2060

Feils?king (ved behov)

Hvis du opplever problemer med modelsim som er installert p? egen maskin, eller ikke f?r tak i det, s? pr?v questa p? en virtuell maskin eller via ssh-innlogging p? login.ifi.uio.no, f?r du sp?r gruppel?rer om hjelp. Vi som lager oppgavene kan ikke teste med versjoner du laster ned p? egen pc og som krever registrering, og vi vet at tilgangen til nedlastingsversjoner har variert mye de siste ?rene.

Hvis et menyvalg som st?r beskrevet i obligteksten er skrudd av (lysegr?tt og ikke valgbart i menyen), s? sjekk om du har riktig undervindu valgt, eller bruk undervinduets egne valg dersom det eksisterer.

Med ulike versjoner av modelsim eller questa hender det enkelte ting m? settes opp ulikt. Vi vet for eksempel at optimaiseringsinnstillingene (optimization options) har variert gjennom tidene, og ulike feil (bugs) i questa/modelsim har gjort at disse har variert en del. Hvis du ser at du kj?rer p? en eldre versjon enn 2020.4, s? kan det hende disse m? endres.

Modelsim bruker oppsettsfiler (.ini filer). Hvert prosjekt har sin egen .ini som normalt kopieres fra programmets .ini fil n?r det opprettes. Hvis man g?r inn i et gammelt prosjekt s? vil de gamle innstillingene gjerne f?lge med. Dersom et prosjekt er opprettet med feil i .ini filen, s? er det oftest greiest ? lage et nytt prosjekt med de samme kildefilene, fremfor ? fikse det gamle. Et typisk eksempel p? dette er hvis man har endret VHDL-versjon i .ini filen etter man opprettet prosjektet: Det g?r godt an ? finne prosjektets .ini fil og rette den, men det er gjerne like raskt ? lage et nytt prosjekt.

Del 2 - Bli kjent med ARM assembler kompilering

N?r vi koder et program, som skal kj?res av en datamaskin, skriver vi det ofte i et h?yniv? programmeringsspr?k. H?yniv? programmeringsspr?k er laget for ? v?re enkle ? lese og forst? for mennesker. Dette kan for eksempel v?re Python, Java eller C. Datamaskinen kan ikke direkte forst? og utf?re disse programmene. Det er fordi prosessoren(CPU) kun kan utf?re et sett instruksjoner som ble definert i hardware da den ble designet. Vi kaller disse instruksjonene for maskinkode. Dere vil l?re mer om hva maskinkode er senere i kurset, men for n? holder det ? vite all kode vi skriver m? oversettes til maskinkode for at datamaskinen skal kunne lese og utf?re den.

For noen programmeringsspr?k (f.eks. Python) vil koden oversettes til maskinkode av en interpret underveis mens den kj?rer, men for mange progammeringsspr?k m? vi f?rst oversette hele koden til maskinkode f?r vi kj?rer den. Programmet som oversetter h?yniv?kode til maskinkode kalles en kompilator. I denne obligen skal vi kompilere og kj?re et C program og et Assembler program.

Hvilket sett av maskinkodeinstruksjoner en prosessor st?tter er avhengig av arkitekturen til prosessoren. De to mest vanlige arkitekturene er X86 og ARM. Linux maskinene p? ifi har X86 arkitektur, men i dette kurset skal dere kj?re ARM maskinkode. Da m? vi enten kj?re koden p? en datamaskin som har ARM arkitektur, f.eks. en Raspberry Pi, eller bruke en emulator. ARM emulatoren later som at den utf?rer ARM maskinkode, men i bakgrunnen m? den jo kj?re maskinkode som samsvarer med arkitekturen til datamaskinen den kj?rer p?.

Denne obligen tar som utgangspunkt at emulatoren blir brukt for ? kj?re ARM kode. Dersom du har en Raspberry Pi du vil bruke i steden kan du f?lge v?r oppsettsguide, og s? g? videre til steg 4.

Vi skal n? bli kjent med emuleringsverkt?yet ved ? kompilere og kj?re et C program:

  1. For ? f? tilgang til emulatoren m? du enten v?re logget inn p? en av linux maskinene p? ifi, logge deg inn p? IFIs ssh login cluster, eller logge deg inn p? vdi ressursen IFI Workstation. Dersom du er p? en egen mac eller linux maskin kan du f?lge denne guiden for ? logge deg inn med ssh p? IFIs ssh login cluster. Dersom du er p? en egen windows maskin kan du f.eks. bruke X-Win som beskrevet i denne guiden.
  2. N?r du har kommet inn p? en av maskinene kan du kj?re f?lgende kommando for ? ?pne et milj? der der du har tilgang p? emulatoren:
in2060_arm
  1. N?r du vil lukke milj?et kan du gj?re det med f?lgende kommando:
exit
  1. Vi skal n? kompilere og kj?re C kode. Lag en mappe med navn in2060_oblig1, og naviger til mappen i terminalen. I mappen lagrer du koden nedenfor i en fil med navn helloworld.c (Inne i in2060_arm milj?et finnes kun editorene vim, nano og emacs, og du vil ikke ha tilgang p? grafisk brukergrensesnitt. Det kan derfor l?nne seg ? lagre koden i en fil f?rst, og s? ?pne milj?et etterp?.)

#include <stdio.h>

int main(int argc, char** argv) {
    printf("Hello World!\n");
    return 0;
}

  1. Vi skal n? kompilere koden. Bruk kommandoen in2060_arm for ?  ?pne milj?et som inneholder emulatoren og kompilatoren. Kompilatoren du skal bruke heter arm-linux-gnueabihf-g++. Denne kompilatoren kompilerer til ARM maskinkode.
    1. Kompiler programmet med f?lgende kommando4:
arm-linux-gnueabihf-g++ -static -o helloworld helloworld.c

4Vi inkluderer flagget -static til kompilatoren n?r vi kompilerer koden som skal emuleres. Dette er fordi ARM emulatoren vi bruker er statisk bygget.

  1. Dersom du kompilerer p? en RPi eller en annen maskin med ARM arkitektur kan du i steden bruke gcc til ? kompilere:
gcc -o helloworld helloworld.c
  1. Du vil n? ha en eksekverbar fil som heter helloworld. Dersom du er p? en maskin med ARM arkitektur (en RPi) kan du kj?re programmet med:
./helloworld
  1. N?r du pr?ver ? kj?re programmet p? denne m?ten p? en maskin med x86 arkitektur, slik som p? en ifi maskin, vil du f? en Exec format error. Denne feilmeldingen forteller deg at programmet du pr?ver ? kj?re er bygget for en annen arkitektur enn den maskinen du kj?rer p? har. Vi kj?rer derfor koden med emulatoren i steden, ved ? bruke f?lgende kommando:
qemu-arm-static helloworld
  1. Koden skal n? kj?re! Ta et skjermblide av terminalen etter at koden har kj?rt og gi det navnet helloworld_c.png. Denne bildefilen er en del av innleveringen til Del 2.

Assembler er et lavniv? programmeringsspr?k. Dette programmeringsspr?ket ligger veldig n?rt maskinkode. Vi kan nesten se p? det som en menneskeleselig versjon av maskinkode. N?r vi programmerer i Assembler har vi veldig god kontroll p? hvilke instruksjoner prosessoren utf?rer. Dette kan v?re nyttig hvis man skal lage en kompilator, en driver eller et operativsystem.

Vi g?r n? videre til neste steg hvor vi skal kompilere og kj?re et Assembler program:

  1. Lagre koden nedenfor i en fil med navn helloworld_assembler.s. Vi bruker endingen .s p? filer som inneholder Assembler kode.

.text
.global main
main:
    push {lr}    

    ldr r0, =string
    bl printf

    mov r0, $0
    pop {lr}
    bx lr

.data 
string: .asciz "Hello World!\n"

  1. For ? kompilere Assembler koden til maskinkode bruker du f?lgende kommando (vi har med opsjonen -g for ? legge til debuginformasjon til en debugger vi straks skal bruke):
    1. (Husk ? ?pne milj?et med emulatoren med kommandoen in2060_arm f?r du kompilerer!)
arm-linux-gnueabihf-g++ -static -g -o helloworld_assembler helloworld_assembler.s
  1. Dersom du bruker en RPi kan du igjen bruke gcc i steden for kompilatoren vi bruker i emuleringsmilj?et:
gcc -g -o helloworld_assembler helloworld_assembler.s
  1. Du skal n? ha en eksekverbar fil med navn helloworld_assembler. Kj?r den med en av de f?lgende kommandoene:
    1. Dersom du bruker emulatoren kj?r med:
qemu-arm-static helloworld_assembler 
  1. Dersom du er p? en RPi kj?r med:
./helloworld_assembler
  1. Dersom man f?r en feil n?r man programmerer i Assembler kan det v?re vanskelig ? finne den ettersom koden ikke er like lett ? forst? som h?yniv? programmeringsspr?k. Vi bruker derfor en debugger n?r vi skal finne feil i Assembler kode. Milj?et med emulatoren har en debugger som heter gdb. F?lg disse stegene for ? ?pne koden din i gdb med et terminalbrukergrensesnitt (tui): (Dersom du er p? RPi hopp videre til steg 13)
    1. F?rst emulerer vi koden. Vi inkluderer -g opsjonen for ? videresende debuginformasjon til en port (her port 1234). Dette gj?r at gdb kan f? tak i informasjon om kj?ringen av programmet v?rt fra emulatoren. Vi inkluderer & slik at programmet kj?rer i bakgrunnen. (Merk at du m? starte programmet ditt med emulatoren, slik som dette, p? nytt for hver gang du vil kj?re det i debuggeren)

qemu-arm-static -g 1234 ./helloworld_assembler &
  1. S? ?pner vi gdb med f?lgende kommando: (Noen ganger m? man trykke p? enter en ekstra gang for at gdb skal komme i gang skikkelig. Pass p? at det st?r (gdb) p? nederste linje i terminalen f?r du g?r videre)

gdb-multiarch -tui ./helloworld_assembler
  1. Deretter m? vi fortelle gdb hvilken port den skal lese fra (pass p? at du bruker samme port som du brukte i a):

target remote localhost:1234
  1. N? kan vi se hva som ligger i registerene med f?lgende kommando:

tui reg general
  1. Registerene er minneelementer der prosessoren har dataene den jobber p? (Du vil l?re mer om dette senere i kurset). Det er veldig nyttig ? se p? disse dataene, s? pr?v ? huske p? denne kommandoen til oblig 3 :)

  1. Dette steget beskriver hvordan vi kj?rer gdb p? RPi. Dersom du bruker emulatoren g? videre til steg 14.

  1. Vi ?pner gdb med f?lgende kommando: (Noen ganger m? man trykke p? enter en ekstra gang for at gdb skal komme i gang skikkelig. Pass p? at det st?r (gdb) p? nederste linje i terminalen f?r du g?r videre)

gdb -tui ./helloworld_assembler
  1. N? kan vi se hva som ligger i registerene med f?lgende kommando:

tui reg general
  1. Ta et skjermbilde av gdb der man kan se hva som ligger i registerene og lagre det i en fil med navn gdb_tui.png. Denne bildefilen er en del av innleveringen til Del 2.

  2. Du kan n? unders?ke hvordan gbd fungerer. Test f?lgende kommandoer:

    1. Kj?rer programmet (ps. legg inn et stoppested (breakpoint) f?r du kj?rer programmet) (pps. hvis du bruker emulatoren kj?rer allerede programmet n?r du ?pner gdbtui, p? RPi m? du starte det selv)

    2. s  Kj?rer neste instruksjon

    3. break main  Legger inn et stoppested ved main: i koden

    4. c  Kj?rer til neste stoppested, eller til slutten av koden dersom det ikke er noen stoppesteder

    5. q  Avslutter debuggingsprogrammet

  3. N?r du kj?rer koden kan du se at verdiene i registerene endrer seg.

  4. Vi skal n? gj?re en liten endring i programmet helloworld_assembler.s, f?r vi kj?rer det p? nytt. Mot slutten av assembler koden finner du f?lgende instruksjon:

mov r0, $0
  1. Denne instruksjonen flytter tallet 0 til registeret r0. Legg til ny kode rett f?r instruksjonen mov r0, $0 som i steden flytter tallet 5 til r0 og tallet 3 til r1.

  2. N?r tallene er flyttet til r0 og r1 skal vi legge dem sammen, vi kan gj?re det med instruksjonen:

add r0, r1
  1. Resultatet av addisjonen vil du kunne se i registeret r0.

  2. Kompiler den modifiserte koden, og kj?r den med debuggeren gdb-tui. G? steg for steg igjennom koden, og ta et skjermbilde av debuggeren rett etter at add instruksjonen har blitt utf?rt. (Slik at man kan se resultatet i r0)

  3. Den modifiserte assemblerfilen helloworld_assembler.s og bildefilen gdb_tui_add.png er en del av innleveringen til Del 2.

Innlevering oblig 1:

Fra Del 1:

Fra Del 2: