korena's blog

3. First signs of life - part 2

In 2. First signs of life (MoleStone 1) - PART 1 , we passed the boot BL0 bump, and successfully loaded BL1 into SRAM, we move on to setup a more productive development environment. We want to have a one click compilation for any changes we introduce to our bare metal code, so we'll go with make.

Makefile

The Makefile we need to write needs to compile, link and run a couple of scripts in order to provide a convenient one-click deployment. We'll start by a very simple Makefile.

CROSS_COMPILE = /home/korena/Development/Compilers/gcc-arm-none-eabi-4_7-2013q1/bin/arm-none-eabi-
 
AS        = $(CROSS_COMPILE)as
LD        = $(CROSS_COMPILE)ld
CC        = $(CROSS_COMPILE)gcc
AR        = $(CROSS_COMPILE)ar
NM        = $(CROSS_COMPILE)nm
STRIP     = $(CROSS_COMPILE)strip
OBJCOPY   = $(CROSS_COMPILE)objcopy
OBJDUMP   = $(CROSS_COMPILE)objdump
 
 
IMGMAKE     =imageMaker
 
BL1_BIN    = BL1.bin
BL1_ELF  = BL1.elf
OBJS      = startup.o
BL1_LDS   = linker.lds
CFLAGS =  -Os -nostdlib
 
 
all: $(BL1_BIN) $(BL1_BIN).boot
 
$(BL1_BIN):      $(OBJS)
 $(LD) -T $(BL1_LDS) -o $(BL1_ELF) -Map BL1.map $(OBJS)
 $(OBJCOPY) -O binary $(BL1_ELF) $(BL1_BIN)
 
$(BL1_BIN).boot: $(BL1_BIN) $(IMGMAKE)
 ./$(IMGMAKE) $(BL1_BIN) $(BL1_BIN).boot
  
$(IMGMAKE):  imageMaker.c
 gcc $(CFLAGS_HOST) -o $(IMGMAKE) imageMaker.c
 
fuse: $(BL1_BIN).boot
 ./fuseBin.sh
startup.o: startup.s
 $(AS) $(AFLAGS) -o startup.o startup.s
 
 
dep:
 gccmakedep $(OBJS:.o=.c)(OBJS:.o=.s)
 
clean:
 rm -rf $(OBJS) $(IMGMAKE) *.bin *.elf *.boot *.map *.o

The above Makefile provides minimal functionality, it only compiles the startup.s file and inserts the requisite BL1 headers, and then runs the fuseBin.sh introduced in the previous post on this project to give us an SD card that's ready to go. if we want to add any more source files, we'd have to come back and modify this Makefile, that's an inconvenience, but I plan to work with a single assembly file through low level initialization, so this Makefile is adequate for now, we'll have to revise it in the next milestone though, when we make the switch from assembly to C.
Another point worth considering is that it isn't a very good idea to work with the SD/MMC card at this stage, because of the rapid change that we'll make on the executable, so we might want to checkout that UART/USB booting functionality.

What the documentation says about UART/USB booting:

Unfortunately the documentation talks about a windows-only tool in the UART/USB boot section of the iROM application notes. A stream of windows based tools like the picture below is what you are expected to use, so I'll stick to my SD card, minimize fusing operations, and live with it.

DNW UART/USB Tool

<Rant>
Now, had this been a serious project (one that I am paid to work on), and a port for the Samsung provided windows tool was not already available, I probably would not have attempted to develop a kernel module AND user mode software interface that could take hours in the best case scenario and days in the worst, just to carry out such a trivial task!
I would've bought a few SD cards (just in case) and pushed through with the working code transfer setup I already have until I am at the bootloader stage.You see, If I am going to be developing kernel modules (I might in a future post), they better be for my actual end application. And this, ladies and gentlemen, is why you should be VERY careful when buying a development system, especially if you are a Linux user.

</Rant>

Taking a further step for convenience, let us write a udev rule that allows make to access the MMC card device without requiring root privileges.
in a terminal(specific to my setup)(1) :

korena@korena-solid:/path$ udevadm info --attribute-walk --name /dev/mmcblk0
. . .
looking at device '/devices/pci0000:00/0000:00:1c.5/0000:0b:00.0/mmc_host/mmc0/mmc0:8e09/block/mmcblk0':
    KERNEL=="mmcblk0"
. . .
looking at parent device '/devices/pci0000:00/0000:00:1c.5/0000:0b:00.0/mmc_host/mmc0/mmc0:8e09':
    KERNELS=="mmc0:8e09"
    SUBSYSTEMS=="mmc"
    DRIVERS=="mmcblk"
    ATTRS{cid}=="01504153503032477420a48e08009900"
    ATTRS{csd}=="005d01325b5a83c0b6dbffff12800000"
    ATTRS{scr}=="01a5000000a00061"
    ATTRS{date}=="09/2009"
    ATTRS{name}=="SP02G"
    ATTRS{type}=="SD"
. . .

We have the information we need to write our rule, we go on and:

korena@korena-solid:/path$ groups
korena adm cdrom sudo dip plugdev fuse lpadmin sambashare
korena@korena-solid:/path$su
korena@korena-solid:/path# echo "KERNEL==\"mmcblk0\", ATTRS{type}==\"SD\", ACTION==\"add\", GROUP=\"korena\",
MODE=\"0664\"" > /etc/udev/rules.d/98-myMMCard.rules

The above command creates a udev rule in /etc/udev/rules.d/ directory, the rule gives members of the group korena access with permissions 0664 to the device with the specified properties found from running the previous udevadm info command, these are specific to the MMC I am using, find the info specific to yours. Of course, I am a member of the korena group. We will have a somewhat extensive experience with devices, drivers and udev, when we get to the Linux kernel milestone, for now, this is all we have to do. Also, note that we did not modify the rules for the partition, which is listed as /dev/mmcblk0p1 as a result of our fdisk partitioning in the previous post, this will cause a 'permission denied' when make tries to format the partition by running the fuseBin.sh script, this is intentional, as we do not need to format the partition every time we transfer data to it. So we'll simply ignore it(2).

Initial test

Now that we have a relatively convenient project setup, lets go ahead and test it, we will be doing something a little bit more interesting than our ridiculouslySimpleStartup.s script, we will do the hello world equivalent for embedded systems, light an LED!

LED ports on the TINY210 board

This section is less about the tiny210 and more about the process of figuring out how we would go about finding information about the task we are trying to achieve. What we need to know at this point:

  1. Which pins of which port of the SoC are connected to the four user leds
  2. What logic level turns the leds on? should I give them a HIGH or a LOW?
  3. The Leds are probably controlled by the GPIO Peripheral in the SoC, so how do I control
    the GPIO?
  4. what are the peripheral registers involved in this process? and where are they (at what memory address)?

So we'll start by looking at the Mini210S User’s Manual document provided by friendlyARM, which, under the User LEDs section, tells us that the leds are ON when the corresponding ports are held LOW, and gives us the following useless piece of information:

This piece of information is useless because looking at the S5PV210 Users Manual document from Samsung, under section 1.3.6 CONNECTIVITY, it is clearly stated that this processor has GPJ0,1,2,3,4, so there is no port of the form GPIO_NUMBER as suggested by FriendlyArm's LED pin assignment, so we'll look in the schematics provided by FriendlyArm to actually know what ports of the processor drive the four LEDs.
In the schematic, we find the following :

LED assignment

So the ports in question are GPJ2_0 through GPJ2_3, that, we can accept, as it does map to a real port that exists in the S5PV210 SoC.
With this piece of information, we are ready to look into the details of controlling these processor pins, intuitively, we should expect the information needed for this task to be in one of the S5PV210 documents. why you say? Well, this sort of information generally relates to the way the vendor of the SoC has wired peripherals to the core, since the GPIO is just another peripheral, then the information we need should be in a documentation that is specific to the SoC, and not in any document that relates to the core, nor in the tiny210 board's documentation.
In the S5PV210 user manual document (S5PV210 Risc Microcontroller Rev 1.0 February 2010), a quick look at section 2.2.22 PORT GROUP GPJ2 CONTROL REGISTER tells us that the registers involved in controlling this port are:

  • (GPJ2CON, R/W, Address = 0xE020_0280)
  • (GPJ2DAT, R/W, Address = 0xE020_0284)
  • (GPJ2PUD, R/W, Address = 0xE020_0288)
  • (GPJ2DRV, R/W, Address = 0xE020_028C)
  • (GPJ2CONPDN, R/W, Address = 0xE020_0290)
  • (GPJ2PUDPDN, R/W, Address = 0xE020_0294)

In a nut shell, the GPJ2CON register controls the core functionality of GPJ2 individual physical pins, it defines whether a pin should be a digital input/output, or should be used for another purpose, such as a GPIO interrupt line. GPJ2CON is a 32 bit register, but it controls 8 physical pins, so every pin has 4 control bits within the 32 bit GPJ2CON register, if we want to control the functionality of the first physical pin of GPJ2, then we have to look into the first four bits of GPJ2CON register, and set them appropriately to achieve the desired functionality of GPJ2_0.
GPJ2DAT register has a one-to-one mapping to the physical pins of GPJ2, since we have 8 physical pins, it follows that GPJ2DAT is an eight bit register (or treated as one!), it is only involved when the corresponding pins are configured as input/output pins, the document states that: "When the port is configured as input port, the corresponding bit is the pin state. When the port is configured as output port, the pin state is the same as the corresponding bit. When the port is configured as functional pin, the undefined value will be read." I think that sums it up pretty well.
GPJ2PUD is a 16 bit register (or treated as one!), every physical pin of GPJ2 maps to 2 bits of this register. GPJ2PUD register controls the internal pull Up/Down configuration of the physical pins.
GPJ2DRV is the Port Group GPJ2 Drive Strength Control Register, it basically controls the output/input impedance of each pin that belongs to th GPJ2 group, I felt a rough post on signal impedance is due here. But we will not care for it at the moment, we will simply leave the values on this register unchanged, but it is a very important consideration when you're interfacing components in real life projects.
The rest of the registers are important in power down mode, they define the behavior of the GPIO ports when the processor is in the power down mode, we will not address this situation right now, but at some point, we might come back to them if we decide to introduce a power down mode in our Dashboard project.

Control registers' values:

  • GPJ2CON:

    We need to configure the first 4 pins of GPJ2 Port, From the S5PV210 user manual document, section 2.2.22 PORT GROUP GPJ2 CONTROL REGISTER ,we see that for GPJ2_x , where x could be a number from 0 to 7, that corresponds to a single physical pin in GPJ2, the four bits in GPJ2CON would have the following effect:

0000 = the x pin would be an Input
0001 = the x pin would be an Output
0010 = Asynchronous Modem Interface (MSM hints to qualcomm's mobile station modem)
0011 = keypad interface specific functionality
0100 = Camera interface specific functionality
0101 = MHL interface specific functionality (Mobile High-Definition Link)
0110 ~ 1110 = Reserved
1111 = GPIO interrupt functionality

since we need all the four pins connected to the LEDs to be outputs, we will set this register value to 0hxxxx1111 (0001 for each of the lower 4 pins, don't care for the rest).

  • GPJ2DAT

    The four bits corresponding to GPJ2_0,1,2,3 will be toggled, so they will toggle the LEDs at a certain frequency.

  • GPJ2PUD

    the documentation says:

00 = Pull-up/ down disabled, 01 = Pull-down enabled, 10 = Pull-up enabled, 11 = Reserved

so we'll have this register filled with 0bxxxxxxxx00000000 to disable internal pull up/down for each of the four LED pins, we will only do this if we have to, but will ignore it if we don't, cause we're lazy like that.

The startup file

Our start up file will not grow too big, but we will start introducing bits and pieces that are not very important for now, just so we would have a better base to work with through the low level initialization phase.

startup.s:

.section .text
.code 32
.global _start
 
/*useful addresses, fetched from the S5PV210 user manual*/
.equ GPIO_BASE,  0xE0200000
.equ GPJ2CON_OFFSET,  0x280   
.equ GPJ2DAT_OFFSET,   0x284
.equ GPJ2PUD_OFFSET,   0x288
.equ GPJ2DRV_SR_OFFSET,  0x28C
.equ GPJ2CONPDN_OFFSET,  0x290
.equ GPJ2PUDPDN_OFFSET,  0x294
 
 
_start:
 
 
/*configure GPJ2_0,1,2,3 as output, to drive the leds*/
 
ldr r0,=(GPIO_BASE+GPJ2CON_OFFSET)   /*load the address 0xE0200280 into r1*/
ldr r1,[r0]   /*read contents of the memory location that has it's address loaded in r0,
                into r1*/
ldr r2,=0x1111  /*use r2 as buffer for the set value, which is 0001 for each four
                 bits corresponding to the GPJ2_0,1,2,3 pins, the immediate value
                 0x1111 is stored in memory, meaning it's adding eight bytes to 
                 the overall size of the executable*/
orr r1,r1,r2  /*  sets the desired GPIOs to output, r1 = r1 | (0x1111)*/
str r1,[r0]  /*store the contents of r1 to the register of GPJ2CON*/
 
ldr r0,=(GPIO_BASE+GPJ2DAT_OFFSET) /*load the address  0xE0200284*/
ldr r1,[r0]  /*read contents of the memory location in r0 into r1*/
bic r1, r1,#0xF  /* clearing the lowest four bits, r1 &= ~(0xF), since the value
                0xF is less than 12 bits, it is accepted as an immediate value,
                   and adds nothing to the overall binary image size ...*/
str r1,[r0]  /*store the contents of r1 to the register of GPJ2DAT*/

Note that we're loading the values of the GPIO registers into r1, modifying only the bits we intend to modify, then pushing the values back to the desired registers when we're done, this might not be important now, but you ought to make a habit of it, always change only the bits you want to change. The reason for this will become clearer when we're dealing with mmu and cache controls.
It is now time to test our development setup (Note that the linker script used here is identical to the one we had in the previous post, the only difference is the name of the startup file, instead of ridiculouslySimpleStartup.S, it is now startup.s). with the SD card plugged, in a terminal:

korena@korena-solid:/path$ make fuse
. . .
./fuseBin.sh
mkfs.vfat -F 32 /dev/mmcblk0p1
mkfs.vfat 3.0.13 (30 Jun 2012)
/dev/mmcblk0p1: Permission denied
writing BL1 ...
0+1 records in
0+1 records out
84 bytes (84 B) copied, 0.0053549 s, 15.7 kB/s

The above command produces a BL1 image and fuses the MMC card. The desired result, which is lighting the four LEDs on the development module right after BL0 copies BL1 into SRAM is achieved.

Results

Before simple BL1 startup code

After simple BL1 startup code

Conclusion

Getting the system to behave as desired at this very early stage is nothing more than following documentation by the letter! And of course having a good grip on ARM assembly language.
Remember that the defining factor of whether you will have a smooth ride or a rough one is, as I keep saying, the availability of good documentation. This post concludes milestone one, to recap, we've passed the phase of BL1 loading, constructed a good development environment base, and lit 4 LEDs! Next, we'll have a few posts on low level initialization, where we look into clock, MMU, cache, DRAM, UART serial communication and other essential sub systems.


(1)  Udevadm info starts with the device specified by the devpath and then walks up the chain of parent devices. It prints for every device found, all possible attributes in the udev rules key format. A rule to match, can be composed by the attributes of the device and the attributes from one single parent device.
(2) You might find the udev rules step unnecessary, if so, then add yourself to the group that owns the sd/mmc card, and go about your business.

References

S5PV210 Risc Microcontroller Rev 1.0 February 2010 (users manual)