RISC-V from scratch
https://twilco.github.io/riscv-from-scratch/2019/07/28/riscv-from-scratch-4.html
RISC-V Bytes
https://danielmangum.com/posts/risc-v-bytes-stack-use-after-return/
embedded techniques for everyone ; solve atleast one problem of the world a day ; provide the world what you do not get ; invent and service the world
https://d2pn104n81t9m2.cloudfront.net/documentation/freedom-soc/freedom-e300-platform-brief/
https://github.com/riscv/riscv-plic-spec/blob/master/riscv-plic.adoc
https://shakti.org.in/docs/plic_user_manual.pdf
https://riscv.org/wp-content/uploads/2017/05/riscv-privileged-v1.10.pdf
RISC-V Instruction-Set Cheatsheet
A very interesting guy ..
https://itnext.io/risc-v-instruction-set-cheatsheet-70961b4bbe8
The RISC-V Linux User's Manual
Interesting summary of Linux port
https://www.ocf.berkeley.edu/~qmn/linux/riscv.html
Links to Linux and FreeRTOS internals
https://forums.sifive.com/t/context-switch-on-risc-v/3624
https://riscv.org/wp-content/uploads/2018/07/Shanghai-1640_RT-ThreadinRISC-V-06029.pdf
https://cdn2.hubspot.net/hubfs/3020607/An%20Introduction%20to%20the%20RISC-V%20Architecture.pdf
மேற்கண்ட படத்தை பாருங்கள். அது ஒரு மிகவும் எளிய அடிப்படையான embedded system த்தை விளக்குகிறது.
எந்த ஒரு சிஸ்டமும் குறைந்தது கீழ்க்கண்ட பாகங்களை கொண்டிருக்கும்.
1) CPU
2) Memory
3) Interrupt Management System
4)Input Output devices
மேற்சொன்னவைகள் hardware பாகங்களே.ஏற்கனவே உங்களுக்கு தெரிந்திருக்கும்.
இப்பொழுது software பற்றி புதிதாக சில விஷயங்களை உங்களுக்கு விளக்குகிறேன்.சரி, ஒரு system த்தில் உள்ள ஒரு input device லிருந்து ஒரு input ஐ வாங்கி அதை அப்படியே ஒரு output device ல் வெளித்தள்ள வேண்டும். எப்படி உங்கள் software ஐ எழுதுவீர்கள்?
சரி, நான் சொல்வது புரிய வேண்டுமென்றால் கீழ்க்கண்டவைகளைப் பற்றி நீங்கள் அறிந்திருக்க வேண்டும்.
Power-On-Reset
ஒரு system த்தை switch-on செய்வதுதான் Power-On-Reset எனப்படும். ஒரு CPU வானது உயிரூட்டப்படும் போது, அதனுடைய Program Counter (PC) வானது, ஒரு குறிப்பிட்ட address ல் initialize செய்யப்படும். அதனால், எப்போதுமே அந்த address ல் இருந்துதான் அது தன் முதல் instruction ஐ இயக்க ஆரம்பிக்கின்றது.
முதன் முதலில் instruction இயக்கப்படும் அந்த address க்கு Reset Vector Address என்று பெயர். அப்படி என்றால் Embedded System த்தில் உங்களுடைய Program ன் entry pointஆனது, Reset Vector ல் தொடங்குகிறது. சாதாரண C Programஐ போல main()ல் அல்ல என்று புரிந்து கொள்ளுங்கள். அதாவது இங்கும் main()க்கு போக வேண்டும்தான். ஆனால், Reset Vector ல் ஆரம்பித்து main()வரைக்கும் கூட நீங்கள் தான் CPU Execution ஐ வழிநடத்தி செல்ல வேண்டும்.
கங்காதேவி வானுலகத்திலிருந்து பூமிக்கு குதிக்கும் பொழுது, அவளுடைய எல்லையற்ற வேகத்தில் பூலோகமே வெள்ளக்காடாக அழிந்து விடும் என்று அனைவரும் அஞ்சினார்களாம். அவர்களின் வேண்டுகோளுக்கிணங்க சிவபெருமான் அவளை அழகாக தன் சடைமுடியில் உள்வாங்கி வேகத்தை குறைத்து அழகான கங்கை நதியாக வழிந்தோட செய்தார்.
அதுபோலத்தான், உங்கள் program மும் Reset Vector லிருந்து main()வரை செல்வதற்கு தேவையான CPU மற்றும் memory initialization, கூடவே main()லிருந்து ஒரு C programஐ இயக்குவதற்கு தேவையான அனைத்து setup களையும் Reset Vector லிருந்து படிப்படியாக செய்து main() ஐ சென்றடைய வேண்டும்.
எனவே, உங்கள் programஐ memoryல் எங்கு load செய்தாலும் பரவாயில்லை. Reset Vector addressல் மட்டும் ஒரே ஒரு branch instructionஐ போட்டு execution உங்கள் Reset Handler க்குள் தாவும்படி பார்த்துக் கொள்ளுங்கள். எடுத்துக் காட்டாக, Reset Vector Addess ஆனது 0x00000000 என்று வைத்துக் கொள்வோம். Reset vector ல் branch Reset_Handler என்ற instruction எழுதப் பட்டிருக்க வேண்டும்.
0x00000000: b Reset_Handler (Reset Vector)
:
Reset_Handler: (Your Reset Handler)
:
CPU execution ஆனது Reset_Handler க்குள் வந்துவிட்டதல்லவா! இனி அதன் குடுமி உங்கள் கையில். மேற்சொன்னபடி, தேவையான initialization மற்றும் setup களை படிப்படியாக செய்து main()ற்கு தாவி விடுங்கள்.
Vector Table
மேற்ச்சொன்னேனல்லவா! Power-On-Reset ல் ஒரு குறிப்பிட்ட address க்கு தாவும் என்று. அது போலத்தான், மேலும் சில நிகழ்வுகளுக்கும் CPUவானது வெவ்வேறு குறிப்பிட்ட address களுக்குத் தாவும்.
முதலாவதாக, Interrupt ஐ எடுத்துக் கொள்ளுங்கள்.interrupt என்பது வெளியிலிருந்து தூண்டப்படும் ஒரு நிகழ்வு. நீங்கள் உங்கள் வீட்டில் ஒரு வேலை செய்து கொண்டிருக்கும்பொழுது யாரோ ஒருவர் calling bell ஐ அழுத்தினால், செய்யும் வேலையை அப்படியே போட்டு விட்டு வாசலுக்கு ஓடுவீர்கள் அல்லவா! அப்படித்தான். பெரும்பாலும் input devices ஆனவை, user இடமிருந்து input ஐ பெறும்போது interrupt ஐ தூண்டும்படி interrupt controller ல் இணைக்கப் பட்டிருக்கும். எடுத்துக்காட்டாக, keyboard ல் ஒரு key ஐ தட்டும்போது interrupt தூண்டப்படும். இது எப்போது தட்டுவார் என்று சொல்ல முடியாது. எப்போது வேண்டுமானாலும் நிகழலாம். இதற்கு "Asynchronous event" என்று பெயர்.
சரி, interrupt தூண்டப்படும் போது என்ன நடக்கும் என்று பார்ப்போம். CPU எந்த address இல் உங்கள் program ஐ இயக்கிக் கொண்டிருந்தாலும், அப்படியே போட்டுவிட்டு ஒரு குறிப்பிட்ட address க்கு தாவிவிடும். அதற்கு Interrupt vector என்று பெயர்.
சரி, CPU வானது interrupt vector க்கு தாவி விட்டது. யார் தூண்டினார்கள், எதற்காக தூண்டினார்கள், CPU எந்த instruction ஐ execute செய்து கொண்டிருக்கும்போது தூண்டினார்கள் என்றெல்லாம் ஆராய வேண்டுமல்லவா! அதையெல்லாம் செய்வதற்கு, Interrupt Handler ல் program எழுதி, Reset vector ஐ போலவே, interrupt vector லும் "branch Interrupt_Handler" என்ற ஒரேயொரு instruction ஐ போட்டு execution ஐ உங்கள் interrupt handler க்குள் அழைத்து செல்ல வேண்டும்.
0x00000004: b Interrupt_Handler
:
Interrupt_Handler:
:
Interrupt_Handler க்குள் என்ன செய்ய வேண்டுமென்பதை பின்னர் பார்ப்போம். Interrupt Handler லிருந்து மீண்டும் interrupt தூண்டப்பட்ட இடத்திற்கு திரும்புவது என்பது ஒரு பெரிய கலை. அதையும் பின்னர் பார்ப்போம்.
இரண்டாவதாக Exception. Exception என்றால் என்ன தெரியுமா? உங்கள் programல் செய்யக் கூடாத ஒரு வேலையை செய்கிறீர்கள் என்றால் CPU கோபித்துக்கொண்டு ஒரு குறிப்பிட்ட address க்கு தாவி விடும். அந்த address க்கு Exception Vector என்று பெயர். எடுத்துக்காட்டாக, உங்கள் program ல் Divide by zero செய்கிறீர்கள், அல்லது தவறான ஏதோ ஒரு junk data வை instruction ஆக இயக்க சொல்கிறீர்கள் என்றால் அல்லது உங்கள் instruction ல் memory யே இல்லாத ஒரு address ஐ access செய்கிறீர்கள் என்றால் அப்படியே அந்த இடத்திலிருந்து Exception vector க்கு தாவிவிடும். மேற்சொன்ன ஒவ்வொரு வகையான exception க்கும் Invalid Instruction, Data Abort என்று தனித்தனியான vector address கூட இருக்கும். இவற்றிற்கும் ஒவ்வொரு branch instruction ஐ போட்டு தான் handler க்குள் வழி நடத்திச் செல்ல வேண்டும்.
0x00000008: b DataAbort_Handler
0x0000000c: b InvalidInstrction_Handler
:
DataAbort_Handler:
:
InvalidInstrction_Handler:
:
மூன்றாவதாக, Supervisor call என்று ஒன்று உள்ளது. அது செயற்கையாக interrupt ஐ தூண்டக்கூடிய ஒரு ஸ்பெஷல் instruction ஆகும். பெரும்பாலும், CPUகளில் தொழிலாளி (non-privileged) mode மற்றும் முதலாளி (privileged) mode என்று 2 அதிகார வர்க்கங்கள் உண்டு. தொழிலாளி mode ல் முதலாளி அனுமதித்த சில மெமரி ஏரியாக்களை மட்டுமே access செய்ய முடியும். அந்த எல்லையைத் தாண்டி வேறு எதுவும் செய்ய முடியாது. இதை தாண்டி வேறு எதுவும் செய்ய வேண்டுமென்றால் "என்னிடம் கேள்! நான் செய்து கொடுக்கிறேன்!" என்று முதலாளி சொல்வது போல, என்ன Request உள்ளதோ அதை ஒரு Registerல் பதிவு செய்து விட்டு, இந்த SVC instruction ஐ நம் programல் execute செய்ய வேண்டும். இந்த SVC instruction ஐ execute செய்த அடுத்த கணமே CPU ஒரு குறிப்பிட்ட SVC Vector address க்கு தாவி விடும்.
இன்னொரு சிறப்பு என்னவென்றால், execution ஆனது SVC Vector addressக்கு தாவிய அடுத்த கணமே அது முதலாளி modeக்கு மாறி இருக்கும்.
0x00000018: b SVC_Handler
:
SVC_Handler:
<Prolog>
என்ன request என்று register ஐ பார்.
Valid Reuest ஆக இருந்தால் நிறைவேற்று.
PC backup registerலிருந்து SVC instructionஐ execute செய்த address ஐ கண்டுபிடி.
மீண்டும் SVC instruction அடுத்த instruction க்கு திரும்பு.
<Epilog >
:
மற்ற exception ஐ போலவே இந்த SVC callயும் ஒரு branch instruction மூலம் SVC Handlerக்குள் வழிநடத்தி செல்ல வேண்டும். அங்கு தொழிலாளியின் Reuest ஐ முதலாளி பரிசீலித்து பார்ப்பார். அவருக்கு தேவையானதை மட்டுமே கேட்கிறார் என்றால் அதை செய்து கொடுத்துவிட்டு மீண்டும் SVC instructionக்கு அடுத்த instructionக்கு, மற்றும் தொழிலாளிmodeக்கே திரும்பும்படி Exception Return instructionஐ execute செய்வார். அடுத்த கணமே, CPU பழைய இடத்திற்கே ஒன்றுமே தெரியாததுபோல் திரும்பியிருக்கும்.
பெரும்பாலான OS kernelகள் இந்த முதலாளி modeல் தான் இயங்கும். User program தொழிலாளி மோடில் இயங்கும். அதனால்தான் User programல் கண்ட address ஐ access செய்து சிஸ்டத்தை crash செய்ய முடிவதில்லை. உனக்கு தேவையானவைகளை OS அனுமதித்த SVC service calls மூலம் கேட்டு பெற முடியும். தவறு ஏதாவது செய்யலாம் என்று நினைத்தால் அது Exceptionக்கு தாவி, அங்கு Exception Handler ல் இவன் எங்கிருந்து இந்த வேலையைச் செய்கிறான் என்று கண்டுபிடித்து, அந்த user program ஐ மட்டும், மீண்டும் execute செய்யாத வண்ணம், அலேக்காக வெளியில் தூக்கி போட்டு விடும்.
சரி, மேற்சொன்ன எல்லா Reset, Exception மற்றும் SVC vectorகள் எல்லாவற்றையும் ஒன்றாக ஒரே இடத்தில் வரிசையாக வைத்திருப்பார்கள். அதற்குப் பெயர்தான் Vector Table. ARM CPU வில், 0x00000000 அல்லது 0xFFFFFF00 ல் இந்த vector table ஐ செட் செய்ய வேண்டும்
#includeint kbhit(void);int kbhit(void) {struct termios term, oterm;int fd = 0;int c = 0;tcgetattr(fd, &oterm);memcpy(&term, &oterm, sizeof(term));term.c_lflag = term.c_lflag & (!ICANON);term.c_cc[VMIN] = 0;term.c_cc[VTIME] = 1;tcsetattr(fd, TCSANOW, &term);c = getchar();tcsetattr(fd, TCSANOW, &oterm);if (c != -1)ungetc(c, stdin);return ((c != -1) ? 1 : 0);}
#includeint getch();int getch(){static int ch = -1, fd = 0;struct termios neu, alt;fd = fileno(stdin);tcgetattr(fd, &alt);neu = alt;neu.c_lflag &= ~(ICANON|ECHO);tcsetattr(fd, TCSANOW, &neu);ch = getchar();tcsetattr(fd, TCSANOW, &alt);return ch;}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
|
static inline int Do_SWI_9 (int arg1, struct something *arg2)
{
register long __res asm("r0");
asm volatile ("mov r0,%2\n\tmov r1,%3" : \
"=X" (*(char *)(long)arg1), "=X" (*(char *)(long)arg2) : \
"r" ((long)arg1), "r" ((long)arg2) : \
"r0", "r1");
asm volatile ("swi 9 @ %0" : "=r" (__res) : :
"r12", "r14", "cc");
return((int)__res);
}
What's different?
(1) The __res variable is now explicitly placed in r0.
(2) The swi call is marked as returning a value in __res.
(3) The "@%0" comment after swi FOOLS asm() into thinking swi really
does affect r0.
(4) The "mov %0,r0" after the swi is omitted as it's no longer
necessary.
Is this voodoo or what? But it appears to work great under level 2
optimization.
In this case, the parameres are transmitted through the stack, but I do
not need. When used inline it fits into the function.
How do I type this, an unspecified number of parameters?
_inline_ void sm(int i, int l)
{
register int p0 asm("r0") = i;
register int p1 asm("r1") = l;
asm ("swi 0x148"::"r"(p0),"r"(p1));
}
_inline_ void sm(int i,...)