IS1500 ösningar övning CE_O7 2014 CE_O7. Programmerad in/utmatning. Serieport. Förberedelser till nios2io. 6.1. Vad är seriell kommunikation? a) Vad är skillnaden mellan seriell och parallell kommunikation? Vid seriell kommunikation överförs i varje tidsintervall en enda bit dvs om man vill överföra många bitar skickas bitarna en i taget i en lång serie. Vanliga bithastigheter är 9600 bps, 56kbit/sec eller med bredband dvs fler än cirka 2 Mbit per sekund typ. Vid parallell kommunikation har man flera ledningar parallellt och kan alltså i varje tidintervall överföra flera bitar samtidigt (parallellt). b) Vad är skillnaden mellan synkron och asynkron seriell kommunikation Vid asynkron kommunkation behöver sändare och mottagare inte ha exakt samma uppfattning om klocksignalens frekvens och fas. Det räcker om mottagarens och sändarens klockor går ungefär likadant i frekvens och fas. Sändning av ett paket av bitar, t.ex. ASCII-koden för ett tecken sköts synkront med sändarklockan. Mottagaren ska kunna upptäcka och ta emot ett paket när som helst jämfört med den egna klockan (asynkront) vilket kräver att mottagarklockan snabbt måste kunna synkroniseras i fas med det mottagna paketet och gå i rätt fart/frekvens under den tid paketet tas emot. Om paketet är kort behöver alltså mottagaren kunna upptäcka starten på varje paket och fasa in sin klocka och sen hålla hyfsat noga fart/frekvens ett tag (under hela paketets inkomst). Vid synkron kommunikation krävs att sändar- och mottagar-klocka går synkront hela tiden dvs ska ha exakt samma frekvens och hela tiden ha samma (kända) fasförskjutning. För att bibehålla synkroniseringen skickar sändaren hela tiden en följd av (paus-) bitar som mottagaren använder för att hålla sin klocka synkron med den mottagna signalen men bitarna innehåller ingen datainformation, bara klockinformation. Ett exempel på hur man kan åstadkomma detta är att sändaren hela tiden skickar en följd av ASCII-koden för SYN (0x16 = p011 0110). Mottagarens hårdvara/mjukvara ska alltså vara utformad så att den kan upptäcka att det kommer in ett bitmönster som består av en följd av SYN och kunna använda dessa bitar till att synkronisera på bitnivå (fas och frekvens) samt på byte-nivå dvs veta var gränsen finns mellan två SYN-tecken. När andra tecken börjar dyka upp ska mottagren dels kunna dela upp dem i separata 8 bitars tecken och dels kunna bibehålla både bit- och byte-synkronisering. Hur detta ska implementeras ger upphov till ett flertal intressanta frågeställningar för dem som ösningar till övning CE_O7, sida 1 (av 9) 2014-10-27
ska bygga den elektronik som måste finnas i mottagaren. Det kan påminnas om att det rimligen även bör finnas någon synkronisering på block- eller paketnivå som troligen sköts av mjukvara. Denna diskussion ligger i gränsområdet mot en kurs i Datorkommunikation och Datornät. c) Rita ett tidsdiagem som visar de logiska värde den överförda signalen antar, om man förutsätter att ett 7 bitars ASCII-tecken, t.ex. 0x26 som är koden för tecknet &, överförs med asynkron kommunikation med jämn paritet och 2 stoppbitar. Denna figur finns i föreläsningsbilderna för föreläsning 6. d) Vilken funktion har startbiten? Startbiten, dvs den första växling från 1 till 0 som upptäcks i viloläget, ska användas för att synkronisera mottagarklockan till rätt fas. Avsikten är att mottagaren i fortsättningen kunna känna av (sampla/polla) den inkommande signalen mitt i varje efterföljande klockpulsintervall för varje bit som ingår i det paket som tas emot. Om paketet är koden för ett ASCII-tecken innehåller det startbit, 7 databitar, eventuellt en paritetsbit samt stoppbit (ar) dvs cirka 10 bitar. Mottagarklockan ska alltså inte fasa ur inom 10 mottagna bitar. e) Vilken funktion har stoppbiten (stoppbitarna)? Stoppbiten (stoppbitarna) läggs in av sändaren för att garantera att det finns en minsta tid mellan två ivägsända paket så att mottagern hinner vila och göra sig beredd på nästa startbit. Det är ju av stort värde att mottagaren är helt säker på vilken flank (1 till 0) som inleder en startbit eftersom den flanken behövs för både bit- och bytesynkronisering. f) Vilken funktion har paritetsbiten? Paritetsbiten beräknas och införs av sändaren (av hårdvara eller mjukvara) för att mottagaren ska kunna utföra en rudimentär (nästan dålig men ändå någon) kontroll av att data inte har ändrats (förstörts) vid överföringen. Om mottagaren upptäcker att mottagna data har fel paritet så har ett udda antal bitar växlat värde i det paket som paritetskontrolleras. Denna information kan lämnas vidare till programvara (eller hårdvara) som vid behov kan välja att begära omsändning av det mottagna paketet. Betänk att felupptäckt (error detection) och felkontroll/korrigering (error control/correction) är helt olika kvaliteter. g) Hur stor procentuell skillnad kan man maximalt tillåta mellan sändarklocka och mottagarklocka för att kunna erhålla en fungerande kommunikation? Mottagarklockan måste hålla sig i rätt fas under hela tiden ett paket tas emot. Om vi förutsätter att vi har 1 startbit, 7 databitar, 1 paritetsbit och 1 stoppbit (10 bitar) så får mottagarklockan gå fel med högst ett halvt klockpulsintervall under dessa 10 bitar/intervall vilket betyder att en frekvensskillnad mindre än 5% är tillräcklig för att möjliggöra felfri kommunikation. (ett halvt intervall på 10 ger 1 per 20 ger 5 per 100 ger 5%) h) Vilka spänningsnivåer används vid seriell dataöverföring enligt standarden EIA RS-232C/CCITT V.24 (+- 5 till 15 Volt!!!) (Att gå mot strömmen utan motstånd ger ingen spänning sade Ohm) ösningar till övning CE_O7, sida 2 (av 9) 2014-10-27
6.2. Vad är en serieport / seriekrets Hur betraktas en serieport ur programmerarens synpunkt? Jämför med en parallellport. Hur kopplas en seriekrets till en processors bussystem? SVAR: Ur programmerarens synpunkt betraktas serieporten som en parallellport som man skriver en byte till eller läser en byte från. Porten kopplas till processorns bussystem som andra portar dvs det finns en adressavkodare som väcker kretsen till liv för vissa valda adresser. Det finns en (parallell) koppling till busens data-signaler så att det går att överföra data (t.ex. 8 bitar) parallellt mellan processor och serieport. Dessutom finns samma typ av kontrollsignaler som för andra portar dvs läs och skriv (eller kombinerad RD/WR och Data Strobe) samt möjligen IRQ (Interrupt ReQuest). Hur betraktas en seriekrets ur omgivningens synpunkt jämfört med en parallellport. Vilka signaler bör finnas mot omgivningen? SVAR: det finns bara en datasignal för sänd och en datasignal för mottag (samt jordsignal). Det måste även finnas klocksignal(er) som anger fart och fas för sända och mottagna data. Det är möjligt att man vill använda olika klocksignal för sänd och mottag (för uplink och downlink, jämför ADS som har olika bithastighet för up- och down-load) 6.3. ogiskt EKO-program (ekologiskt program?) Skriv ett program som ekar tecken på bildskärmen dvs varje tecken som skrivs på tangentbordet ska skrivas ut i motsvarande terminalfönster. Nios-II-assembler:.global main main: call in_char # invänta tecken från tangentbord mov r4, r2 # kopiera tecken call out_char # skriv ut tecken br main # snurra runt ösningsförslag i C: int in_char( void ); /* Extern funktion "importeras". */ void out_char( int ); /* Extern funktion "importeras". */ int main( void ) int c; while( 1 ) /* Oändlig slinga. */ c = in_char(); /* Vänta på tecken, läs ett. */ out_char( c ); /* Skriv tecknet till serieport. */ return( 0 ); /* to avoid "warning: control reaches end of non-void function" */ ösningar till övning CE_O7, sida 3 (av 9) 2014-10-27
Subrutinen in_char ska invänta Receiver Ready dvs att det finns ett tecken i seriekretsens mottagarregister, därefter kopiera data från mottagarregistret till register r2 och göra retur från subrutinen med det mottagna tecknet i r2. ösningsförslag i Nios-II-assembler:.equ serieport0, 0x860 # basadress symbolisk.global in_char # BOCKING I/O, inläsning av en byte till r2 in_char: movia r8, serieport0 # r8 pekar till basadress inloop: ldwio r9, 8(r8) # läs status andi r9, r9, 0x80 # maska fram RxRdy beq r9, r0, inloop # invänta värdet 1 ldwio r2, 0(r8) # läs ett word andi r2, r2, 0x7F # maska fram 7 bitar ret # och returhopp ösningsförslag i C: #define SERIEPORT0 ( (volatile unsigned int *) 0x860 ) #define RXRDYBIT (1 << 7) int in_char( void ) volatile unsigned int * tmp = SERIEPORT0; while( (tmp[2] & RXRDYBIT) == 0 ); /* vänta tills RxRdy == 1 */ return( tmp[0] & 0xff ); Subrutinen out_char ska ska invänta Transmitter Ready dvs att det går att skriva ett tecken till seriekretsens sändarregister utan att gamla förstörs, och därefter skriva data från r4 till sändarregistret. ösningsförslag i Nios-II-assembler:.equ serieport0, 0x860 # basadress symbolisk.global out_char # BOCKING I/O, skrivning av en byte från r4 till porten out_char: movia r8, SPORT # r8 pekar till basadress outloop: ldwio r9, 8(r8) # läs status andi r9, r9, 0x40 # maska fram TxRdy beq r9, r0,outloop # invänta värdet 1 andi r4, r4, 0x7F # kanske onödigt stwio r4, 4(r8) # skriv ett word/en byte ret # och returhopp ösningar till övning CE_O7, sida 4 (av 9) 2014-10-27
ösningsförslag i C: #define SERIEPORT0 ( (volatile unsigned int *) 0x860 ) #define TXRDYBIT (1 << 6) void out_char( int c ) volatile unsigned int * tmp = SERIEPORT0; while( (tmp[2] & TXRDYBIT) == 0 ); /* vänta tills TxRdy == 1 */ tmp[1] = c & 0xff; 6.4. Subrutinen prtext för utmatning av en textsträng a) Flödesschema finns inte än... b) Skriv assemblerkod för Nios II, för subrutinen prtext..global prtext prtext: push r31 mov r8, r4 # kopiera adresspekare prloop: ldb r4, 0(r8) # läs en byte beq r4, r0, prret #retur om NU push r8 # r8 kanske förstörs av OUT_CHAR call out_char # skriv ut ett tecken pop r8 addi r8, r8, 1 # peka på nästa tecken br prloop # nästa varv prret: pop r31 # reuradress på plats ret Det finns ett assemblerdirektiv.asciz för att skapa en textsträng med nulltecken som slutmarkör: message:.asciz "Hello, World!" c) Anta att ovanstående textsträng är konstant, och alltså inte ska ändras under programmets körning. Den kan då lagras antingen i kodarean () eller i dataarean (.data). Fördel och nackdel att placera i är att den inte kan ändras under exekvering, om man förutsätter att hårdvaran i processorn skyddar mot skrivning. d) Skriv programkod i C för funktionen prtext, som ska göra samma sak som assemblersubrutinen prtext enligt ovan. void prtext( char * textp ) unsigned int c; while( (c = *textp)!= 0 ) out_char( c ); textp = textp + 1; ösningar till övning CE_O7, sida 5 (av 9) 2014-10-27
6.5. Utökad Eko-logik Rita flödesshema och modifiera huvudprogram och subrutiner i tidigare uppgift så att de nya in_charx och out_charx vid anrop gör omedelbar retur om läsning respektive skrivning inte kan utföras ( misslyckas ) på grund av att kretsen inte är beredd för uppgiften (Not Ready dvs RxRdy respektive TxRdy har värdet noll). Vid returen från in_charx respektive out_charx ska innehållet i r2 ange om subrutinen har lyckats utföra sin uppgift eller inte. Retur efter att ha lyckats med uppgiften markeras med att r2 ska innehålla ett positivt värde. Misslyckat uppdrag (error) ska markeras med att r2 innehåller värdet -1 vid returen. Huvudprogrammet ska välja sin fortsatta exekvering beroende på returvärde i r2. För att skriva ett konstant värde -1 till r2 ska Du använda instruktionen movi r2, -1 Det fungerar inte med movia r2, -1 pga fel i översättarprogrammet. Observera att att dessa rutiner inte är exakt samma som i laboration nios2io..global main main: call in_charx # försök läsa tecken blt r2,r0,main # invänta lyckad läsning mov r4, r2 # kopiera tecken plain: call out_charx # försök skriva ut tecken blt r2,r0,plain # invänta lyckad skrivning br main # snurra runt Huvudprogram i C: int in_charx( void ); /* Extern funktion "importeras". */ void out_charx( int ); /* Extern funktion "importeras". */ int main( void ) int c; int retvalcheck; while( 1 ) /* Oändlig slinga. */ c = in_charx(); /* Vänta på tecken, läs ett. */ if( c >= 0 ) retvalcheck = out_charx( c ); while( retvalcheck < 0 ) retvalcheck = out_charx( c ); return( 0 ); /* to avoid "warning: control reaches end of non-void function" */ ösningar till övning CE_O7, sida 6 (av 9) 2014-10-27
Subrutinen in_charx i Nios-II-assembler:.equ serieport0, 0x860 # basadress symbolisk.global in_charx in_charx: movi r2, -1 # default error movia r8, serieport0 # r8 pekar till basadress INO: ldwio r9, 8(r8) # läs status andi r9, r9, 0b10000000 # maska fram RxRdy beq r9, r0, INRET # hopp ty ej Ready ldwio r2, 0(r8) # läs ett word andi r2, r2, 0x7F # onödigt men... positivt! INRET: ret # och returhopp Funktionen in_charx i C: #define SERIEPORT0 ( (volatile unsigned int *) 0x860 ) #define RXRDYBIT (1 << 7) int in_charx( void ) volatile unsigned int * tmp = SERIEPORT0; if( (tmp[2] & RXRDYBIT) ) return( tmp[0] & 0xff ); else return( -1 ); Subrutinen out_charx i Nios-II-assembler:.equ serieport0, 0x860 # basadress symbolisk.global out_char.equ serieport0, 0x860 # basadress symbolisk # NON-BOCKING I/O, skrivning av en byte från r4 till porten out_charx: movi r2, -1 # default error movia r8, serieport0 # r8 pekar till basadress OUTO: ldwio r9, 8(r8) # läs status andi r9, r9, 0b01000000 # maska fram TxRdy beq r9, r0,outret # hopp ty ej Ready andi r4, r4, 0x7F # kanske onödigt stwio r4, 4(r8) # skriv ett word (en byte) movi r2, 1 # positivt värde dvs lyckat OUTRET: ret # och returhopp ösningar till övning CE_O7, sida 7 (av 9) 2014-10-27
Funktionen out_charx i C: #define SERIEPORT0 ( (volatile unsigned int *) 0x860 ) #define TXRDYBIT (1 << 6) int out_charx( int c ) volatile unsigned int * tmp = SERIEPORT0; if( (tmp[2] & TXRDYBIT) ) tmp[1] = c & 0xff; return( 1 ); else return( -1 ); 6.6. Teckenvis kommunikation.global main main: foo: call in_charx # kolla om tecken från keyboard blt r2, r0, bar # hoppa om inget tecken mov r4, r2 # kopiera parameter call send_char # sänd tecken bar: call rec_charx # kolla om tecken i mottagare blt r2, r0, foo mov r4, r2 # kopiera parameter call out_char # skriv ut tecken br foo Funktionen rec_charx i C: #define SERIEPORT1 ( (volatile unsigned int *) 0x880 ) #define RXRDYBIT (1 << 7) int rec_charx( void ) volatile unsigned int * tmp = SERIEPORT1; if( (tmp[2] & RXRDYBIT) ) return( tmp[0] & 0xff ); else return( -1 ); Funktionen send_char i C: #define SERIEPORT1 ( (volatile unsigned int *) 0x880 ) #define TXRDYBIT (1 << 6) void send_char( int c ) volatile unsigned int * tmp = SERIEPORT1; while( (tmp[2] & TXRDYBIT) == 0 ); /* vänta tills TxRdy == 1 */ tmp[1] = c & 0xff; ösningar till övning CE_O7, sida 8 (av 9) 2014-10-27
6.7. Tidmätning När man skriver (vad som helst) till något Snap-Shot-register så kopieras aktuellt värde i Counter till Snap-Shot-registren vars innehåll kan läsas av med program..global getsnap getsnap: movia r2, Timer1_Base stw r0, 0x10(r2) # kopierar Counter till SnapShot ldw r4, 0x14(r2) # läs SnapShotH till r4 slli r4, r4, 16 # omplacera ldw r2, 0x10(r2) # läs SnapShot till r2 andi r2,r2,0xffff # maska för säkerhets skull or r2, r2, r4 # kombinera ihop SnapShot 32 bit ret # returnera till anroparen # Mätning av tid för subrutinen SUBRUT PUSH r16 # spara undan R16s innehåll call getsnap # läs av start-värde i Counter mov r16, r2 # spara startvärdet call SUBRUT # exekvera SUBRUTinen call getsnap # läs av slut-värde i Counter sub r4, r16, r2 # beräkna skillnad i ticks subi r4, r4, overhead # korrigering POP r16 # återställ R16s innehåll # Nu finns i r4 antal klockcykler som SUBRUT tog (nästan exakt). # Några intruktioner går åt till administration, för push/pop med mera. Värdet justeras # för detta. För korrekta resultat, gör noggrannare mätning/uppskattning av denna # overhead, värdet 17 är bara en gissning..equ overhead, 17 Kortfattad beräkning av antal extra instruktioner/ticks förutom själva SUBRUT 6 instruktioner i första Get_SnapShot efter stw till SnapShot 1 instruktion: mov r16, r2 1 instruktion call Get_SnapShot 2 instruktioner: movia innan stw till SnapShot Det blir cirka 10 instruktioner varav en del (call/ret) tar mer än 1 klockpulsintervall Kan det inträffa att mätningen ger växlande resultat, med små eller stora variationer? Kan man gardera sig mot detta? I något slags allmänt fall kan man tänka sig att Counter slår runt från bara nollor till ett nytt startvärde från period mellan de båda avläsningarna. Då får man ett mystiskt värde när man räknar ut skillnaden mellan avläsningarna. Om man tolkar värdet som SignedInt blir kan det bli en negativ tid och om man tolkar värdet som UnSigned Int blir det ett alldeles för stort värde. ösningar till övning CE_O7, sida 9 (av 9) 2014-10-27