chadderz121 / bakingpi-www Goto Github PK
View Code? Open in Web Editor NEWWebsite for Baking Pi: Operating Systems Development tutorial (Raspberry Pi)
License: Other
Website for Baking Pi: Operating Systems Development tutorial (Raspberry Pi)
License: Other
It might be worth adding at least a lesson on using C++ ( or C ). The former is better, but either would be better than assembly. Thankfully we can still use all the old assembly code. All we need to know if how to compile our C++ ( or C ), and execute functions defined in our assembly files. That would make things much better. Especially graphics! Oi! I would prefer drawing using C++ ( or C ) to draw since you can use more human-friendly language! Haha.
I think it wouldn't be much to add. Especially since you can use all the old code, only really a single page is needed on how to compiler the C++ ( or C ) file for the RaspberryPI and execute functions defined within the assembly files.
This is merely an idea, not a complaint.
I notice that you write text to the framebuffer by having bitmap descriptions of the letters and then drawing them pixel by pixel with the DrawPixel function.
Surely it would be faster to represent the font in the Hicolor format, and locate text by character index? You could straight buffer copy then, since you have DMA to your framebuffer.
Each approach has advantages and disadvantages, but I'm surprised that this didn't even get a mention, say as an "exercise for the reader"
Currently I'm doing lesson OK03 and at the end of step 3 "A Big Function" some useful mnemonics are suggested to fix the reset of the GPIO pins. Among these suggested mnemonics is the not
function, but I can't find it in the instruction set. The solution also uses the mnemonic MVNS
.
Therefore I would find it more helpful to use mvns
instead of not
in the text.
Okay, this was a lot more annoying than I expected it to be and really humbling on how little I apparently understand designing ARM code... but I got something working.
In the OK04 code, the system timer device is introduced.
The introduction discusses two methods to use the timer: count the delay yourself, or setup the counter and watch the status register.
The OK04 lesson then goes on to count the delay yourself leaving the counter setup and status register as an exercise for the reader.
I replace the GetTimerBase and Wait functions with a file of equates, and two new functions:
Below is an example of using the counter register and counter status for people to hopefully learn from and improve.
First, copy your ok04 folder to ok04_extension.
Next, remove all source/*.s files except gpio.s
Then create the files described below with the following content:
ok04_extension/source/systemTimer-equates.s
/******************************************************************************
* systemTimer.s
* by Alex Chadwick
* System Timer Equates
* by Exile In Paradise
* The system timer runs at 1MHz, and just counts always.
******************************************************************************/
/*
* Register equates for the System Timer
*/
.equ ST_CS, 0x20003000
.equ ST_CLO,0x20003004
.equ ST_CHI,0x20003008
.equ ST_C0, 0x2000300C
.equ ST_C1, 0x20003010
.equ ST_C2, 0x20003014
.equ ST_C3, 0x20003018
/*
* Masks for each bit of the System Timer Counter Status Register
*/
.equ MASK0,0b0000000000000001
.equ MASK1,0b0000000000000010
.equ MASK2,0b0000000000000100
.equ MASK3,0b0000000000001000
/*
* Error return values for functions
*/
.equ ERR_INPUT1_OUT_OF_RANGE, -1
.equ ERR_INPUT2_OUT_OF_RANGE, -2
.equ ERR_INPUT3_OUT_OF_RANGE, -3
.equ ERR_INPUT4_OUT_OF_RANGE, -4
ok04_extension/source/systemTimer-SetCounterDelay.s
/*****************************************************************************
* systemTimer.s
* by Alex Chadwick
* SetCounterDelay(int counter,int delay)
* by Exile In Paradise
* The system timer runs at 1MHz, and just counts always.
*****************************************************************************/
.include "systemTimer-equates.s"
/*****************************************************************************
* SetCounterDelay - sets counter to an amount to wait for
* r0 - counter to set 0 through 3 inclusive
* r1 - amount of microseconds to delay (32-bit, 1-4294967296)
* Destroys r0, r1, r2, r3
* Alters ST_CS and ST_C0, ST_C1, ST_C2, OR ST_C3 registers
* Returns nothing on success, ERR_INPUT1_OUT_OF_RANGE, ERR_INPUT2_OUT_OF_RANGE
*/
.global SetCounterDelay
SetCounterDelay:
@ first, check our inputs are in range
in_counter .req r0
in_delay .req r1
return .req r0
cmp in_counter,#3 @ r0 (counter) must be 0 to 3 inclusive
movhi return,#ERR_INPUT1_OUT_OF_RANGE
movhi pc,lr @ input 1 out of range, return from function
cmp in_delay,#0 @ r1 (delay in microsecond) must be 1 or more
moveq return,#ERR_INPUT2_OUT_OF_RANGE
moveq pc,lr @ input 2 out of range, return from function
.unreq return @ remove alias
@ now setup the function
push {lr} @ save LR onto stack for later pop to PC
counter .req r2 @ alias reg2 as "counter"
delay .req r3 @ alias reg3 as "delay"
mov counter,in_counter @ save input r0 into scratch register
mov delay,in_delay @ save input r1 into scratch register
.unreq in_counter @ remove alias
.unreq in_delay @ remove alias
@ find which bit mask to write to clear specific counter status bit
counterstatus .req r0
cs_mask .req r1
ldr counterstatus,=ST_CS @ setup r0 for Counter Status register
cmp counter,#0 @ do we need to clear counter 0?
moveq cs_mask,#MASK0 @ setup mask for bit 0
beq go_on$ @ branch to go on
cmp counter,#1 @ do we need to clear counter 1?
moveq cs_mask,#MASK1 @ setup mask for bit 1
beq go_on$ @ branch to go on
cmp counter,#2 @ do we need to clear counter 2?
moveq cs_mask,#MASK2 @ setup mask for bit 2
beq go_on$ @ branch to go on
cmp counter,#3 @ do we need to clear counter 3?
mov cs_mask,#MASK3 @ setup mask for bit 3 (no moveq for last option)
beq go_on$ @ fall through to go on
pop {pc} @ somehow we got here and should not have - bail
go_on$:
str cs_mask,[counterstatus] @ write r1 mask to ST_CS to clear counter bit
.unreq counterstatus
.unreq cs_mask
@ now to set timestamp to wait for
counterlo .req r0
counterhi .req r1
delaytime .req r1
ldr counterlo,=ST_CLO @ set r0 to counter low address
ldrd counterlo,counterhi,[counterlo] @ get current 64-bit counter value into r1/r0
add delaytime,counterlo,delay @ add the desired delay to CLO, overwrite CHI
@ FIXME need carry compare to check for 32-bit rollover
.unreq counterlo @ clean up counterlo alias
.unreq counterhi @ clean up counterhi alias
.unreq delay
@ Now setup the counter register to wait for
counterreg .req r0
loopidx .req r3
ldr counterreg,=ST_C0 @ load address of ST_C0 into register 0
mov loopidx,#0 @ setup a loop counter for 0 to 3
counterLoop$: @ figure out correct address of counter to set
cmp counter,loopidx @ does loop number match counter we want?
addlo counterreg,#4 @ add 4 to counter register address we care about
addlo loopidx,#1 @ not counter we want, add to to check next counter
blo counterLoop$ @ now loop to check again
str delaytime,[counterreg] @ store 32 bit time+delay to chosen count
@ return from function call
.unreq counterreg @ clean up counterreg alias
.unreq delaytime @ clean up delaytime alias
.unreq counter @ clean up counter alias
.unreq loopidx @ clean up alias loopidx
pop {pc} @ pop link register from stack into pc register
ok04_extension/source/systemTimer-WaitCounter.s
/*****************************************************************************
* systemTimer.s
* by Alex Chadwick
* WaitCounter(int counter)
* by Exile In Paradise
* The system timer runs at 1MHz, and just counts always.
*****************************************************************************/
.include "systemTimer-equates.s"
/*
* WaitCounter waits until the System Timer Counter Status register shows a
* preset counter has reached its timestamp and set the counter status high.
* Inputs: r0 = counter to wait for
* Returns: r0 = error if input out of range
* Outputs: none
* Uses: ST_CS
* Destroys: r0, r1, r2, r3
*/
.global WaitCounter
WaitCounter:
@ check inputs
in_counter .req r0
retval .req r0
cmp in_counter,#3 @ check which counter number we were asked to wait on
movhi retval,#ERR_INPUT1_OUT_OF_RANGE
movhi pc,lr @ return if requested counter to wait on is too high
.unreq retval
@ setup the function
push {lr} @ save the return link register on the stack
counter .req r2 @ alias register 2 as "counter"
mov counter,in_counter @ move input register 0 "counter" to alias register
.unreq in_counter
@ Start the wait polling loop
csreg .req r0
counterstatus .req r1
csmask .req r3
loop$:
ldr csreg,=ST_CS @ load counter status address into r0
ldr counterstatus,[csreg] @ load counter status value into r1
cmp counter,#0 @ check if we are waiting for counter 0
moveq csmask,#MASK0
beq test$ @ if matches, exit polling loop
cmp counter,#1 @ check if we are waiting for counter 1
moveq csmask,#MASK1
beq test$ @ if matches, exit polling loop
cmp counter,#2 @ check if we are waiting for counter 2
moveq csmask,#MASK2 @ if yes, check bitmask for counter 2 against r1
beq test$ @ if matches, exit polling loop
@cmp counter,#3 @ check if we are waiting for counter 3
moveq csmask,#MASK3 @ if yes, check bitmask for counter 3 against r1
@beq test$ @ if matches, exit polling loop
b loop$ @ no matches in counter status, go wait more
@ exit polling loop and return
test$:
tst counterstatus,csmask
beq loop$
@ if equal, timer done, exit
.unreq counter
.unreq csreg
.unreq csmask
.unreq counterstatus
pop {pc} @ return from function call
And, finally, ok04_extension/source/main.s
/*****************************************************************************
* main.s
* by Alex Chadwick
* Modified to use SetCounterDelay() and WaitCounter()
* by Exile In Paradise
* A sample assembly code implementation of the ok04 operating system, that
* simply turns the OK LED on and off repeatedly, synchronising using the
* system timer.
* Sections changed since ok03.s are marked with NEW.
*
* main.s contains the main operating system, and IVT code.
*****************************************************************************/
/*
* .globl is a directive to our assembler, that tells it to export this symbol
* to the elf file. Convention dictates that the symbol _start is used for the
* entry point, so this all has the net effect of setting the entry point here.
* Ultimately, this is useless as the elf itself is not used in the final
* result, and so the entry point really doesn't matter, but it aids clarity,
* allows simulators to run the elf, and also stops us getting a linker warning
* about having no entry point.
*/
.section .init
.globl _start
_start:
/*
* Branch to the actual main code.
*/
b main
/*
* This command tells the assembler to put this code with the rest.
*/
.section .text
/*
* main is what we shall call our main operating system method. It never
* returns, and takes no parameters.
* C++ Signature: void main(void)
*/
main:
/*
* Set the stack point to 0x8000.
*/
mov sp,#0x8000
/*
* Use our new SetGpioFunction function to set the function of GPIO port 16 (OK
* LED) to 001 (binary)
*/
mov r0,#16
mov r1,#1
bl SetGpioFunction
/*
* Use our new SetGpio function to set GPIO 16 to low, causing the LED to turn
* on.
*/
loop$:
mov r0,#16
mov r1,#0
bl SetGpio
/* NEW
* We wait using our new method. We use a value of 100000 micro seconds for the
* delay causing the light to flash 5 times per second.
*/
mov r0,#0 @ use ST_C0 counter 0
ldr r1,=100000 @ delay for 100,000 microseconds
bl SetCounterDelay
mov r0,#0 @ use ST_C0 counter 0 to wait on
bl WaitCounter
/*
* Use our new SetGpio function to set GPIO 16 to high, causing the LED to turn
* off.
*/
mov r0,#16
mov r1,#1
bl SetGpio
/* NEW
* We wait using our new method. We use a value of 100000 micro seconds for the
* delay causing the light to flash 5 times per second.
*/
mov r0,#0 @ use ST_C0 counter 0
ldr r1,=100000 @ delay for 100,000 microseconds
bl SetCounterDelay
mov r0,#0 @ use ST_C0 counter 0 to wait on
bl WaitCounter
/*
* Loop over this process forevermore
*/
b loop$
And that should be it - make, copy to your test Pi, and give it a whirl.
It should behave the same as ok04 - but in main.s you can change the r0 for counter 0 to 3, and change the delays, and the code uses the counter register requested for the delay requested.
Enjoy.
P.S. Please improve this anywhere possible - all criticism welcome in the interest of showing people better ways to get this done. I am just laying down a start here and am fully aware this code is probably horrible and atrocious and could be severely optimized. Go for it!
I think there is an off by one error in the naming of the OK LED: Since lesson OK01 the "OK" LED is decribed to be the 16th pin, but now I'm pretty sure it actually is the 17th pin, because the GPIO pin numbering starts at zero and of course by looking at all the offsets for setting the GPIO function and the output. The pin is referred to as "GPIO16" but there is also a "GPIO0" pin.
The OK01 tutorial section 2 describes the expected folder structure and content the student should use as a starting workspace.
Unfortunately, I believe the tar creation step for template.tar.gz skipped empty folders and files, causing two related issues.
First, the empty build/ subdirectory does not exist in the template.tar.gz and there is no instruction for the student to make their own. The result is that the initial compile of kernel.img at the end of the OK01 tutorial will fail due to lack of a target build directory which the Makefile expects to already exist.
Second, the source/main.s template file does not exist in the template.tar.gz, but the folder structure diagram on OK01 section two leads the student to expect one will be there to start editing with.
I do not know what would be more preferred path forward - updating template.tar.gz to include empty folders and files, or changing the OK01 section 2 description.
Loving this tutorial! Thanks so much.
If you try to run http://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/screen03.html on newer Pi linux builds (replacing kernel.img
obviously), it doesn't work. This is not because the code is wrong, it's because newer Linux distributions now use device_tree
for kernel configuration which means the bootstrap code doesn't put the configuration at 0x100 formatted as ATAGS. You can fix this by writing this to the end of your config.txt
:
# Disable device_tree so we can see the ATAGS
device_tree=
With that setting in place, this code happily runs on my Raspberry Pi Zero W with a Rasbian flash install from a couple of weeks ago, for example.
the address where the gpio pin starts on the data sheet for bcm2835 is 0x7E200000
but in the book thats 0x20200000 which confused me
I had a Raspberry Pi 2B, and I followed the baking-pi lessons to learn about the Raspberry pi, but now
I could not make the screen show the colors, it was just keep black.
Who has the right code, please give one copy or give a website link. Thanks.
My mail is [email protected]
I used Arch Linux for Raspberry Pi 2B.
Line 39 currently reads:
Table 1.1 GPIO Controller Registers
Should be:
Table 1.1 Timer Controller Registers
Hi,
In lesson OK04, the wait function implementation has the following code -
.globl GetSystemTimerBase
GetSystemTimerBase:
ldr r0,=0x20003000
mov pc,lr
.globl GetTimeStamp
GetTimeStamp:
push {lr}
bl GetSystemTimerBase
ldrd r0,r1,[r0,#4]
pop {pc}
delay .req r2
mov delay,r0
push {lr}
bl GetTimeStamp
start .req r3
mov start,r0
loop$:
bl GetTimeStamp
elapsed .req r1
sub elapsed,r0,start
cmp elapsed,delay
.unreq elapsed
bls loop$
.unreq delay
.unreq start
pop {pc}
The problem I see that after the POP {PC} instruction in the GetTimeStamp method there should have been a "wait" method declared globally. Otherwise the code wouldn't work properly as the user would have given the delay value in the R0 register which would have been over-written by the LDRD instruction.
The correct sequence should be -
.globl GetSystemTimerBase
GetSystemTimerBase:
ldr r0,=0x20003000
mov pc,lr
.globl GetTimeStamp
GetTimeStamp:
push {lr}
bl GetSystemTimerBase
ldrd r0,r1,[r0,#4]
pop {pc}
.globl Wait
Wait:
delay .req r2
mov delay,r0
push {lr}
bl GetTimeStamp
start .req r3
mov start,r0
loop$:
bl GetTimeStamp
elapsed .req r1
sub elapsed,r0,start
cmp elapsed,delay
.unreq elapsed
bls loop$
.unreq delay
.unreq start
pop {pc}
Let me know if I am missing something here.
Thanks!
Am new to ASM, has some question need enlightening:
frameBuffer.s > InitialiseFrameBuffer:
str width,[r4,#0] str height,[r4,#4] str width,[r4,#8] str height,[r4,#12]
Question: Why "STR" width and height, 2 times?
Didn't the values gets overwrite immediately?
Thanks
In lesson 6, the InitialiseFrameBuffer function using r3 to store fbInfoAddr,
fbInfoAddr .req r3
but then r3 will be used by function MailboxRead/MailboxWrite, that make the function InitialiseFrameBuffer return the wrong pointer:
mov result,fbInfoAddr
pop {pc}
.unreq result
.unreq fbInfoAddr
I followed the code in lesson 6, the image will not show due to this reason. The solution code fixed it by using r4 to store the fbInfoAddr, and push/pop r4 with pc register.
Thanks to the mighty hackers at the Pi forums, I've managed to cobble together enough information to get OK02 working.
https://www.raspberrypi.org/forums/viewtopic.php?f=72&t=114796
First, the base physical address for SoC peripherals has moved from 0x20000000 to 0x3F000000 on the Pi 2 B.
Second, the GPIO pin for the OK/ACT LED is now 47 rather than the previous 16.
In the post you will see a table showing the peripheral base and gpio pin for the recent models.
Third, the logic of 0 to enable and 1 to disable the LED seems to have been inverted somewhen recently, probably those with 40 pin GPIO header rather than 26 pin.
I am not sure I got that logic correct in my quick and dirty test... it may be backwards in the code below, but the LED still flashes grin
Fourth, there is now a kernel.img AND a kernel7.img in the boot area of the SD card for Pi 2 B.
I was able to get the OK02 working with just kernel.img in the boot area for it.
/******************************************************************************
* main.s
* by Alex Chadwick
*
* A sample assembly code implementation of the ok02 operating system, that
* simply turns the OK LED on and off repeatedly.
* Changes since OK01 are marked with NEW.
******************************************************************************/
/*
* .section is a directive to our assembler telling it to place this code first.
* .globl is a directive to our assembler, that tells it to export this symbol
* to the elf file. Convention dictates that the symbol _start is used for the
* entry point, so this all has the net effect of setting the entry point here.
* Ultimately, this is useless as the elf itself is not used in the final
* result, and so the entry point really doesn't matter, but it aids clarity,
* allows simulators to run the elf, and also stops us getting a linker warning
* about having no entry point.
*/
.section .init
.globl _start
_start:
/*
* This command loads the physical address of the GPIO region into r0.
*/
ldr r0,=0x3F200000 /* NEW */
/*
* Our register use is as follows:
* r0=0x3F200000 the address of the GPIO region.
* r1=1 << 21 a number with bits 21-23 set to 001 to put into the GPFSEL4 NEW
* function select to enable output to GPIO 47. NEW
* then
* r1=1 << 15 a number with bit 15 high, so we can communicate with GPIO 47. NEW
* r2=0x003F0000 a number that will take a noticeable duration for the processor
* to decrement to 0, allowing us to create a delay.
*/
mov r1,#1
lsl r1,#21 /* NEW GPFSEL4 bit 21 to enable GPIO47 for out */
/*
* Set the GPIO function select.
*/
str r1,[r0,#0x10] /* NEW GPFSEL4 instead */
/*
* Set the 15th bit of r1.
*/
mov r1,#1
lsl r1,#15 /* NEW we want 15th bit for GPSET1 and GPCLR0 */
/* NEW
* Label the next line loop$ for the infinite looping
*/
loop$:
/*
* Set GPIO 47 to low, causing the LED to turn on.
*/
str r1,[r0,#0x20] /* NEW GPSET1 instead of GPSET0 */
/* NEW
* Now, to create a delay, we busy the processor on a pointless quest to
* decrement the number 0x3F0000 to 0!
*/
mov r2,#0x3F0000
wait1$:
sub r2,#1
cmp r2,#0
bne wait1$
/* NEW
* Set GPIO 47 to high, causing the LED to turn off.
*/
str r1,[r0,#0x2C] /* NEW GPCLR1 instead of GPCLR0 */
/* NEW
* Wait once more.
*/
mov r2,#0x3F0000
wait2$:
sub r2,#1
cmp r2,#0
bne wait2$
/*
* Loop over this process forevermore
*/
b loop$
and the kernel.list from the assembly:
build/output.elf: file format elf32-littlearm
Disassembly of section .init:
00008000 <_start>:
8000: e59f003c ldr r0, [pc, #60] ; 8044 <wait2$+0x10>
8004: e3a01001 mov r1, #1
8008: e1a01a81 lsl r1, r1, #21
800c: e5801010 str r1, [r0, #16]
8010: e3a01001 mov r1, #1
8014: e1a01781 lsl r1, r1, #15
00008018 <loop$>:
8018: e5801020 str r1, [r0, #32]
801c: e3a0283f mov r2, #4128768 ; 0x3f0000
00008020 <wait1$>:
8020: e2422001 sub r2, r2, #1
8024: e3520000 cmp r2, #0
8028: 1afffffc bne 8020 <wait1$>
802c: e580102c str r1, [r0, #44] ; 0x2c
8030: e3a0283f mov r2, #4128768 ; 0x3f0000
00008034 <wait2$>:
8034: e2422001 sub r2, r2, #1
8038: e3520000 cmp r2, #0
803c: 1afffffc bne 8034 <wait2$>
8040: eafffff4 b 8018 <loop$>
8044: 3f200000 .word 0x3f200000
Very new to asm coding.
Puzzled why systemTimer.s Wait loop$ uses:
sub elapsed,r0,start
to delay?
Q1. Isn't GetTimeStamp 64bit, but SUB 32-bit?
Q2. Why is it ok to ignore r1 counter values and only subtract on r0?
I am going through the Baking Pi tutorial using a Raspberry Pi 2 model B and I was wondering which toolchain to use. Will the same GNU toolchain listed in the tutorial work for the ARMv7 processor in the Raspberry Pi 2 model B?
Using the Makefiles included with the tutorial, any call to "make clean" will remove the target img, the listing file, the map file, and the entire build/ subdirectory, rather than leaving the build/ subdirectory and only removing the content.
The Makefile could be modified to make the build/ folder if it doesn't exist and leave the clean step as is, or to change the clean step to only remove build/ contents rather than the entire folder tree.
Hello,
I tried to set up screen03 on RPI2, but it is not working.
Framebuffer might be initialized correctly, because the monitor is set to black. But no letter appears.
Do you have an Idea, @edargelies
Please include information on how to proceed building the OS for higher versions like Raspberry Pi 3b+. The OK01 lesson isn't working as the ACT light is not glowing on my pi
i followed all the instruction for building the operating system and setting it up on the sd card, but when i inserted the sd card into my pi 0, the program failed to execute. i think it could be an issue with the bootloader(s).
Love the lessons so far, I built OK1 successfully, copied the kernel.img to my sd card and loaded it on an Rpi zero (uses the same BCM2835). The ACT LED turns on for about half a second, then turns off. It then turns on again very briefly and back off. Then it begins flashing 7 times regularly followed by a longer pause, and continues to do this.
Any idea where I should start looking? I'm very new to this.
Thanks!
I was able to able to get the OK01
module up and running for Pi 3B+ by going through the manual.
I got to know through an online post about the GPIO Pin for Activity LED being 29.
Can I contribute and add the necessary snippets?
In Lesson 6 part 4 (My Dearest Graphics Processor), it mentions the importance of using .align 4
such that the lower 4 bits are 0s. The solution uses .align 12
as page alignment, justified for correct communication with the GPU. Is this a mistake?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.