WWW.DUMAIS.IO

Installing couchdb on SlackwareLast edited on Feb 26, 2012

Installation for Slackware 13.0 on a x86_64 platform

Here is a link that helped me with this: http://wiki.apache.org/couchdb/Installing_on_Slackware

I never use packages. I always compile and install from source. First you need to compile&install the dependencies. Those can be found on the page I have referenced. For couchdb 1.1.1, I had to install: icu4c 4.2.1, js 1.8.0_rc1 and Erlang R13B03. Next, you can proceed with compiling and installing couchdb. I had to execute ldconfig after installing couchdb.

Configuring

invoke "couchdb -c" to see what config file is used

# couchdb -c
/usr/local/etc/couchdb/default.ini
/usr/local/etc/couchdb/local.ini

Now we know that the configuration files are in /usr/local/etc/couchdb. Open the local.ini file because any changes made in default.ini files will be overwritten on a server upgrade. In the configuration file, locate "bind_address" and change it to the address you need your server to bind to. Use 0.0.0.0 to bind to any address.

Now you are ready to launch couchdb with "couchdb -b". I have also added "couchdb -b" in my rc.local boot script so that couchdb is loaded on a server reboot.



AP7000 and NGW100 architectureLast edited on Feb 25, 2012

Preface

The information contained on this page is a collection of my understanding of the AVR32 architecture. That means:

  • The information might not all be accurate, I don't pretend to understand the whole thing.
  • The information is not complete. I do not document everything, only the things I am interested in. I have a very good background on the x86 architecture, so things that are obvious to me are not detailed here either (I won't explain what Paging is used for).

Please feel free to contact me if you need more information.

Memory

NGW100 memory

The memory that is accessible from the CPU is as follow:

These physical address represent different memory "devices" that can be accessed by the CPU. EBI stands for "External Bus Interface" We can see that the two 16K internal sram modules can be accessed from address 0x24000000 and 0x24004000.

The NGW100 board does not have that much memory devices on it, therefore the CPU has some "slots" it cannot access since no devices are attached to it. The NGW100 contains the following:

MemoryDevice typedevice
0x0000_0000EBI SRAM CS0parallel flash(8MB)
0x1000_0000SDRAM CS1SDRAM chip(32MB)
0x2400_0000Internal SRAM0CPU Internal SRAM0(16k)
0x2400_4000Internal SRAM1CPU Internal SRAM1(16k)

We can see that it would be possible to add more memory if we were to connect more flash or SRAM chips on the EBI.

The NGW100 also contains a 8MB serial flash chip but that chip is not accessible by the EBI. It is connected to the SPI bus. NGW100 has two SPI buses, the serial flash is on bus 0 (SPI0) So in order to access that memory, one would need to write a driver. The Serial dataflash can be seen as the hard drive in that case.

Virtual memory

When accessing a memory location, the address is always a virtual address, meaning that it isn't necessarily mapped exactly at the same areas on the physical addressing. There are 2 kinds a virtual memory schemes: Segment translation and Paging. By default, after reset, segment translation is enabled.

The virtual space is divided in subsections that we will call "segments" in this document. Each segment is designed to be accessed by some privileged mode. Here is the layout

It is possible to use segmentation and paging at the same time. This is covered in the AVR32 architecture document at page 32. Using both modes results in having P1 and P2 constantly mapped to 0x00000000 and that mapping cannot change. P0 and P3 can be remapped accordind to the page translation table (TLB). Again, P4 is mapped to 0xE0000000 and is used for memory-mapped IO.

When designing a kernel, it would be a good idea use P1 since it will always map to physical location 0x00000000 regardless of the TLB. P1 is only accessible by priviledged code. So by re-mapping P0 somewhere else, physical location 0x00000000 would be accessible by P1 and P2 only so application code would not be able to access it. The choice of P1 over P2 is because P1 is cacheable. From what I understand, using cached memory would be usefull for data more than for code. Cacheable memory will increase performance if the same memmory accessed several time. By putting code that execute once, we waste cache space. The first application I did made an LED blink on the board. You could see a BIG performance increase by running the code from P1 instead of P2 because the code being executed was a loop making a led flash, so in that case, putting the code in P1 had great benefits. If you have an opinion on that, please feel free to share this with me.

Segment translation

when using segment translation, the segments P0,P1,P2,P3 are all translated to physical address 0x0000_0000. P4 is used for memory mapped IO. Therefore, accessing address 0x80000000 or 0xA0000000 or 0xC0000000 will take us to physical location 0x00000000, being the begining of the parallel flash memory on the NGW100.

Paging

Pages can be 1,4,64 or 1024K in size. A table entry defines the mapping to the physical location and the access rights associated to that chunk of memory. When remapping the memory, it is possible to have a system-wide mapping, or have a mapping for each process running on the CPU. The later is called Private Virtual Memory. This mode will be described.

The operating system must maintain a page table. In the case where 4K pages are used and a total of 32meg of memory is available, 8192 page entries would be needed. A page entry is 32bit wide.

The page lookup differs from the one from the x86 architecture because it is not automatically done. When accessing memory, the CPU determines the page number of the address according to the page size. When it has determined the page number, it looks in the TLB for a matching entry. The TLB is a table that contains 64 page entries. This table is not located in RAM but in the MMU itself. After the OS has booted and set up the paging mechanism, the TLB will be empty. When the OS will try to access a translatable location (address within P0,P3) it will iterate trough its TLB but will find no match. A TLB miss exception will then be issued. The exception handler will need to look at the TLBEAR register, which contains the virtual address that caused the fault. With that address, the handler will be able to load correct table entry in the TLB by preparing the 32bit field and loading it with the tlbw instruction. On the next memory access, the table entry will already be in the TLB and no exception will be issued. Of course, after some time, the TLB will contain 64 entries as new entries are cached in the TLB, old entries will be overwritten.

In order to load an entry in the TLB using tlbw, the TLBEHI and TLBELO registers must be set. The exception handler will copy the contents of its page table entry in TLBELO, set TLBEHI and execute tlbw to update the TLB. The index of the TLB entry is managed by hardware. The adress of the page table can be stored in the PTBR register but it does not seem to be mandatory. This seems to be provided for convenience only.

In reality, there are two TLBs: one for instruction access and one for data access. If the instruction that tries to access memory is trying is data access, the DTLB miss exception is issued, otherwise it is the ITLB exception. Depending on the exception, some fields in the TLBEHI register need to be set differently. This allows the system to have a total of 128 entries.

So basically, these are the operations that are performed:

  • Setup page table entries. A table residing in ram that contains all necessary entries to load in the TLBELO register
  • Page size is determined to be 4k
  • An application (or kernel) makes an access to memory 0x00005100
  • The page number is determined to be 5 (offset of 100)
  • The MMU searches trough the TLB for page #5
  • A TLB miss exception is issued because that page is not cached in the TLB yet
  • The exception handler loads TLBELO from the page table. It finds the correct entry because the virtual address is stored in the TLBEAR register. It knows where to find page #5.
  • Setup TLBEHI properly
  • Execute tlbw
  • The page entry is written in the TLB at an index determined by hardware.
  • Normal flow of execution is resumed and memory translation is performed, allowing the application to access the memory previously addressed.
memory protection

Even though the parallel flash memory is directly accessible trough the address space, it is not possible to write to directly. Before writing to the memory, a full sector must be erased. On the ngw100, the parallel flash is divided into 135 sectors of 32k and some are 4k.

Private virtual space

This part is not so clear in the architecture document. The main concept is that when running several process at the same time, the OS should assign a unique ID to each process. as soon as (or right before) the process has changed du to a task-switch, the process ID should be written in the ASID field in the TLBEHI register. So when an TLBMiss expcetion occur, this field is already set when loading the page into the TLB. But the important thing is that on a TLB lookup, the MMU will compare the ASID field in the TLB entries with the ASID currently loaded in TLBEHI.. It took me some time to figure that last one out. So the ASID field in TLBEHI has two purposes: 1) to inform the TLB what ASID the new page should be tagged with when using TLBW and 2) to inform the TLB what is the current ASID used by the current process. It really is the same thing though when you think about it.

Dirty bit

The dirty bit in the page table entries is not set automatically by hardware. Instead, when the dirty bit is cleared and a store instruction is used to access that page, a DTLB modified exception is issued. It is up to the exception handler to set the bit. To do this, it must find the page entry in its table, modify the bit and load it back to the TLB using the tlbw instruction. Before executing tlbw, the MMUCR[drp] index must be set appriately in order not to have duplicate entries in the TLB The dirty bit could be used to implement swapping to disk but I do not feel that this is usefull on the NGW100. Therefore, the dirty bit should be set when filling the TLB in order to get the Modified exception off our way.

Code execution

The CPU can execute code located anywhere in the addressing space. Executing code directly from the NGW100's serial dataflash is therefore impossible since it does not reside in the addressing space. Although execution can happen anywhere, if using the SDRAM memory, one must initialize the SDRAM controller before accessing that memory space.

Upon reset, the program counter (PC) is set to 0xA000_0000 and the first instruction to be executed is at that address. Since the CPU is in segment translation mode, that address is translated to physical address 0x0000_0000 which is the address of the parallel flash in our case (see upper table). Therefore, the "boot sector" is located at the begining of the parallel flash memory

Clock speed

The CPU can run on two different clocks: OSC0 and PLL0. OSC0 is attached to the 20mhz crystal on the board. This is the default used when the CPU is reset. Using PLL0, the clock gets divided and the CPU can run at up to 150MHZ.

Increasing the clock speed allows you to increase the SDRAM refresh rate too. The sdram can run at half the speed of the master. So if running the main clock at 150MHZ, then make sure to increase the SDRAM at 75MHZ. If it is slower, it shouldn't cause any problem (I think... I did not test that).

    /* set system clock */
    mov     r0,AVR32_PM_ADDRESS
    lddpc   r2,.pllconfig
    st.w    r0[AVR32_PM_PLL0],r2

    /* wait for PLL to be locked */
1:  ld.w    r2,r0[AVR32_PM_ISR]
    bld     r2,0
    brne    1b

    /* set main clock to PLL*/
    mov     r2,2
    st.w    r0[AVR32_PM_MCCTRL],r2

    /* set full clock to PBA,PBB and CPU. HSB=CPU/2 */
    mov     r2,0x8000
    st.w    r0[AVR32_PM_CKSEL],r2

It is important to clear CKSEL because it seemed to be set by u-boot. By doing that, we are telling the CPU that PBA,PBB and the CPU should run at Frequency/1. Since the USART receives its clock from PBA, we can calculate of baud rate based on 150MHZ now. In order to have SDRAM work correctly, HBS speed is set to half the speed of the PLL. this is done by writting 1 to bit15 of AVR32_PM_CKSEL.

SDRAM

When doing standalone developpement, the SDRAM controller must be initialized before we can start to use that memory. I couldn't find much information on the subject. There is a lot of code out there (mainly for the stk1000 though) but there isn't really any information that describe why the code works that way. So I ended up looking at the u-boot source code and I did the exact same thing except I have translated in in ASM. I was unable to test it though because I am booting with u-boot for the moment, so the SDRAMC is already initialized. I don't know if it is because my code is working or if it is because it was already OK before starting to use it. My guess is that if my code would be wrong, it would have screwed up the controller. So here is a bit of code that I translated from the u-boot source code.

/************************
* SDRAM Initialization
************************/
sdram_init:
    stm     --sp,r7, lr

    // disable the pull-up resistor for the SDRAMC SFR4 in the HMATRIX controls that (datasheet, page 87)
    lddpc   r6,.hmatrix
    mov     r8,0x0002;                          // SDRAMC chip select 
    ld.w    r9,r6[AVR32_HMATRIX_SFR4]
    or      r9,r8
    st.w    r6[AVR32_HMATRIX_SFR4],r9
    mov     r8,0x0100;                          // Disable pullup
    or      r9,r8
    st.w    r6[AVR32_HMATRIX_SFR4],r9

    // initialize data bus bits 31-16
    lddpc   r6,.pioe
    mov     r8,0xFFFF;
    ld.w    r9,r6[AVR32_PIO_ASR]
    and     r9,r8
    st.w    r6[AVR32_PIO_ASR],r9
    ld.w    r9,r6[AVR32_PIO_PDR]
    and     r9,r8
    st.w    r6[AVR32_PIO_PDR],r9

    // setup sdram info
    lddpc   r6,.sdramc
    lddpc   r9,.sdraminfo
    st.w    r6[AVR32_SDRAMC_CR],r9

    // send a NOP to start clock
    lddpc   r8,.sdrambase
    mov     r9,1                    // MODE_NOP
    st.w    r6[AVR32_SDRAMC_MR],r9
    ld.w    r9,r6[AVR32_SDRAMC_MR]  // dummy read
    mov     r9,0
    st.w    r8[0],r9

    // wait a while
    mov     r9,40*200
sdramDelay:
    sub     r9,1
    brne    sdramDelay

    // Precharge All command is issued to the SDRAM
    mov     r9,2                    // MODE_PRECHARGE
    st.w    r6[AVR32_SDRAMC_MR],r9
    ld.w    r9,r6[AVR32_SDRAMC_MR]  // dummy read
    mov     r9,0
    st.w    r8[0],r9

    // Provide eight auto-refresh (CBR) cycles
    mov     r9,4                    // MODE_AUTOREFRESH
    st.w    r6[AVR32_SDRAMC_MR],r9
    ld.w    r9,r6[AVR32_SDRAMC_MR]  // dummy read
    st.w    r8[0],r9
    st.w    r8[0],r9
    st.w    r8[0],r9
    st.w    r8[0],r9
    st.w    r8[0],r9
    st.w    r8[0],r9
    st.w    r8[0],r9
    st.w    r8[0],r9

    // CAS from info struct, burst length 1, serial burst type
    mov     r9,3                    // MODE_LOAD_MR
    st.w    r6[AVR32_SDRAMC_MR],r9
    ld.w    r9,r6[AVR32_SDRAMC_MR]  // dummy read
    mov     r9,0
    st.w    r8[0x0],r9

    // A Normal Mode command is provided, 3 clocks after tMRD is met.
    mov     r9,0                    // MODE_NORMAL
    st.w    r6[AVR32_SDRAMC_MR],r9
    ld.w    r9,r6[AVR32_SDRAMC_MR]  // dummy read
    mov     r9,0
    st.w    r8[0x0],r9

    // Write refresh rate into SDRAMC refresh timer count register
    mov     r9,(156 * (SDRAM_HZ / 1000)) / 10000
    st.w    r6[AVR32_SDRAMC_TR],r9

    ldm     sp++, r7, pc



dataj: rjmp dataj   // to make sure we don't execute code

.sdramc:        .int AVR32_SDRAMC_ADDRESS
.pioe:          .int AVR32_PIOE_ADDRESS
.hmatrix:       .int AVR32_HMATRIX_ADDRESS
.sdrambase:     .int 0x10000000
.sdraminfo:     .int    ((1 << AVR32_SDRAMC_CR_NC )\
                        |(2 << AVR32_SDRAMC_CR_NR )\
                        |((1) << AVR32_SDRAMC_CR_NB )\
                        |(3 << AVR32_SDRAMC_CR_CAS )\
                        |(4 << AVR32_SDRAMC_CR_TWR )\
                        |(14 << AVR32_SDRAMC_CR_TRC )\
                        |(2 << AVR32_SDRAMC_CR_TRP )\
                        |(2 << AVR32_SDRAMC_CR_TRCD )\
                        |(14 << AVR32_SDRAMC_CR_TRAS )\
                        |(1 << AVR32_SDRAMC_CR_DBW)\
                        |(20 << AVR32_SDRAMC_CR_TXSR ))

Note that this is for the ngw100 board only. Other boards may have different SDRAM chips/clock speed/buse size and would require a different configuration. The SDRAM_HZ constant should be defined to whatever the HBS speed you are using. Usually this would be half the CPU speed, and you should have configured that when setting up the PLL. In my case, this is 75MHZ because my CPU runs at 150MHZ. for the NGW100's SDRAM here are some value that you need:

ParameterValue
Rows13bit
Cols9bit
CAS3
Banks4
DBW16bit
txsr133.3ns
tras93.1ns
trcd40ns
trc93.3ns
trp40ns
twr26.6ns

The Configuration Register of the SDRAMC must loaded with these values. See page 552 of the datasheet to determine how to load those values. The last 6 parameters in the table were taken from an example code. These timigs are in nanosecods, but the configuration register must be loaded with a number of cycles. So if you are running at 150MHZ, one cycle = 1/150M = 6.6ns. if txr is supposed to be 133.3, then this would be 20 cycles.

Interrupts

When the CPU encounters an error, it issues an exception. A table containing entry points for all exceptions handlers must be defined. This table is called the EVBA. It is very similar to the interrupt vector table on the x86 architecture. One thing that is VERY important to know is that the EVBA must start on a 8kb boundary. If it isn't, you can get a lot of strange problems. The easiest way to do this, is to define the EVBA at the begining of the code so that when booting, EVBA+0x00 will be executed. Since EVBA+0x00 is the entry for "Unrecoverable Exception", the software may take a look in the status register to see if the CPU is in Exception mode. If it isn't, then we know that this is because we are booting so we should jump to the bootstrap.

Initialising the EVBA is just a matter of writting the base address (EVBA+0x00) to the EVBA system register.

lddpc   r8,evbaconstant
mtsr    AVR32_EVBA,r8
evbaconstant: .int EvbaBaseEntryPoint

IRQ

Before going any further, make sure that INTC is enabled in PBBMASK (See Power Manager section of the datasheet). The documentation in the datasheet about INTC is not very clear. The application note AVR32101 is erroneous (errors in figure 3 and 4) and the code examples provided are very confusing.

Devices such as the USART, SPI and timer (and many others) can trigger interrupts. To do so, they are attached to the INTC with an interupt request line. If we take the timer interrupt for example, we can find in the header files that AVR32_TC0_IRQ = 704.

The INTC supports 64 interrupt groups with 32 lines in each group. Each group can be assigned a priority from 0 to 3. An IRQ belongs to group (IRQ/32) and attached to line (IRQ%32). TCO_IRQ/32 = 22, and TC_IRQ%32 = 0, so TC0_IRQ is the first line in group 22. To be able to handle those events, we would need to declare a handler right after the end of the EVBA and fill IPR22 with the relative adress of the handler (intGrp22_address - evba_base_address). We would then need to set the proper int level in IPR22 (can be whatever you like) as well. That handler will receive events for all 32 lines in the group. If only one device was setup to trigger interrupt, this is fine. But if another device was setup to trigger an interrupt in the same group, then the handler would need to look at the IRR22 register in order to determine what line has triggered that event.

evbabase:
ex0:    bral ex0Handler
ex1:    bral ex1Handler
....
ex0x100:    bral ex0x100Handler
intGroup22:
    // This handler is called because one IRQ between 704 and 735 triggered
    // check IRR to know which IRQ triggered.
    // process IRQ
    rete
    // initialize INTC
    mov     r8, (intGroup22-evbabase)                 // set offset
    sbr     r8,31                                   // set level to INT3
    sbr     r8,30                                   // set level to INT3
    lddpc   r9,.INTC
    st.b    r9[INTC_IPR22],r8
    ....

    .INTC: .int 0xFFF00400

Depending on the device that triggered the interrupt, it might be necesary to clear the interupt request. This is device dependant, so you should read on that. For example, the USART generates an interrupt but as soon as you read a byte from it, it clears its interrupt flag automatically so you don't need to do it. This might not be the case for other devices though.

Shadow Registers

Page 24 of the ap7000 datasheet shows the register set in different context. The gray squares indicate that the register is shadowed. If we take the INT3 context for example, we see that r12 is shadowed. This means that when entering the context, the CPU saves r12 in R12_INT3 and restores it when leaving the context. In that case, we don't have to push r12 onto the stack if we use it in the handler. If you take an Exception, you see that no general purpose registers are shadowed, therefore you need to push/pop them on the stack if you use them in order not to disrupt the interrupted code.

Multi-threading

Assuming that applications run in "application mode" and timer interrupt handler runs in "INT3 mode", it is very easy to do task switching. When entering INT3 mode, registers from r8 to r15 are shadowed. Changing those registers won't matter since they will be restored by hardware before going back to application mode. The STMTS instruction allows us to save the content of registers r0-r14 in memory. The good thing about this instruction is that it will save the content from the application-mode register. So if "r8" was modified in the INT3 mode handler, this value will not be saved but instead it will be the value from application mode. By saving those registers, it is possible to do safe task switching but two more registers must be saved: INT3_RAR and INT3_RSR. INT3_RSR needs to be saved because it is the content of the status flags register before swithing to INT3 mode. INT3_RAR contains the address that the handler must return to. These pretty much all the registers you need to take care of.

Serial flash

Before continuing, you should understand how SPI works.
The serial flash chip on the NGW100 is a AT45DB642D. It is connected to the SPI bus 0, CS0 of the CPU. It is supposed to be able to run at 66MHZ but atmel suggest to use it at 20MHZ. The first thing you need to do is to have a working SPI setup. I think it is pretty straight-forward to uderstand it from the datasheet but here is some code that will save you a lot of time trying to understand the framework examples:

dataflash_init:
    stm     --sp,r8,r9,lr

    lddpc   r8,.spi0

    /* reset SPI0 */
    mov     r9,0b10000000
    st.w    r8[AVR32_SPI_CR],r9

    /* set Master, use CS0, MCK = PBA/1 = 150MHZ*/
    mov     r9,0x0013
    st.w    r8[AVR32_SPI_MR],r9

    /* configure chip select */
    mov     r9,0x0809              // MCK/8 = 18.5MHZ , stay active. Atmel suggest going no more than 20MHZ
    orh     r9,0x0000
    st.w    r8[AVR32_SPI_CSR0],r9

    // Assign pins to SPI
    mov     r9,(1<<AVR32_SPI0_SCK_0_PIN)|(1<<AVR32_SPI0_MISO_0_PIN)|(1<<AVR32_SPI0_MOSI_0_PIN)|(1<<AVR32_SPI0_NPCS_0_PIN)
    lddpc   r7,.pioa

    st.w    r7[AVR32_PIO_PDR],r9        // disable GPIO control
    st.w    r7[AVR32_PIO_ASR],r9        // peripheral A
    st.w    r7[AVR32_PIO_PUDR],r9       // pull-up disable


    /* enable SPI0 */
    mov     r9,1
    st.w    r8[AVR32_SPI_CR],r9

    ldm     sp++,r8,r9,pc

.pioa: .int AVR32_PIOA_ADDRESS
.spi0:  .int 0xFFE00000
//////////////////////////////////////////////
/// write SPI
/// INPUT: r6=byte to send
/// OUTPUT: r12=byte that was shifted in during write
/////////////////////////////////////////////
writeSPI:
    stm     --sp,r7,r9,lr


    lddpc   r9,.spi0


    st.w    r9[AVR32_SPI_TDR],r6

1:  ld.w    r7,r9[AVR32_SPI_SR]
    bld     r7,AVR32_SPI_TXEMPTY_OFFSET
    brne    1b

    ld.w    r12,r9[AVR32_SPI_RDR]

    ldm     sp++,r7,r9,pc

dataflash_readmanufacturer:
    stm     --sp,r6,r8,r9,lr

    mov     r6,0x9F
    rcall   writeSPI

    rcall   writeSPI // send dummy byte
    mov     r8,r12
    rcall   debug_showRegister

    rcall   writeSPI // send dummy byte
    mov     r8,r12
    rcall   debug_showRegister

    rcall   writeSPI // send dummy byte
    mov     r8,r12
    rcall   debug_showRegister

    sbr r6,24                                   // end of transmission
    rcall   writeSPI // send dummy byte
    mov     r8,r12
    rcall   debug_showRegister

    ldm     sp++,r6,r8,r9,pc

Dataflash

Now that you can access the dataflash with the SPI bus, here is some theory about the dataflash chip itself. To use the dataflash, you simply need to send an "command" byte followed by parameters and data. I will describe the commands I am using. Before starting, it is important to understand:

  • the dataflash has two type of memory: Buffer, and Main memory. The buffer is an internal sram and the main memory is the flash itself.
  • The smallest unit that can be read/written/erase is a page, and is 1056 bytes in size.
  • There are other units (Sectors and Blocks) but I don't care much for those right now.
  • A page read is done by reading the page directly from main memory
  • A page write can be done many ways, but the way I prefer is: send bytes from MCU to dataflash's SRAM buffer, compare buffer content to the page we are about to change, if buffer is different, copy buffer to page in main memory.

When we need to send a page address to the chip, this is done by sending 3 bytes containing the page number and the offset in that page. In my case, I always use an offset of zero since I want to read the whole page. The 3 bytes need to be sent the following way:

Byte 0 (MSB..LSB)byte 1 (MSB..LSB)Byte 2 (MSB..LSB)
Page Num Bit12 Page Num Bit11 Page Num Bit10 Page Num Bit9 Page Num Bit8 Page Num Bit7 Page Num Bit6 Page Num Bit5 Page Num Bit4 Page Num Bit3 Page Num Bit2 Page Num Bit1 Page Num Bit0 Offs Bit10 Offs Bit9 Offs Bit8 Offs Bit7 Offs Bit6 Offs Bit5 Offs Bit4 Offs Bit3 Offs Bit2 Offs Bit1 Offs Bit0

Read status

By sending command 0xD7 to the chip, every subsequent dummy bytes sent to the chip will return us the status of the chip. As long as the chip is selected (CS is low), sending the dummy byte will return an updated status byte. So when polling the chip, we only need to send 0xD7 once, and send dummy bytes until the desired status is seen.
0xD7,dummy

Read page from main memory

You need to send the command byte, 3 address bytes, 4 dummy bytes and every subsequent dummy byte will return the bytes stored in the page. 0x87,addr0,addr1,addr2,dummy0,dummy1,dummy3,dummy4,data0,...,data1055

Write to buffer

This will write the your data in the internal SRAM buffer of the dataflash. You need to send the command byte, 3 address bytes, and every every bytes that need to be stored in the page.
0xD2,addr0,addr1,addr2,data0,data1,...,data1055

Compare buffer to main memory

With this command, we simply instruct the dataflash chip to compare the contents of the buffer to a page in main memory. This allows us to determine if we should erase/write the page. It would be a waste to write the page if it has not changed.

0x61,addr0,addr1,addr2
After that, we can proceed with polling the chip for the status register until the RDY flag is set

write buffer to memory with built-in erase

With this command, we instruct to dataflash to take the contents of the SRAM buffer, and copy it to the main page.
0x86,addr0,addr1,addr2
After that, we can proceed with polling the chip for the status register until the RDY flag is set

Ethernet

The AP7000 has an embeded MAC module. The PHY is handled by a second chip (DP83848I) on the NGW100. There are two RJ-45 jacks and two PHY chips on the board. Before continuing, here is some information that can be usefull for later use:

PHY IDIRQ groupRMII pinsMII PINS
ETH0125PIOC, DEV-APIOC, DEV-A
ETH1326PIOD, DEV-BPIOC, DEV-B

Buffers

I won't go into the details here since this is very well explained in the datasheet. Basically, you need to allocate space for receive buffers and send buffers. Each receive buffers are 128bytes long and TX buffers are 512 bytes long. Of course, one receive packet will span accross several receive buffers most of the time. The AP7000 must know where those buffers are located. for this, a list of descriptors must be created. This is another buffer in memory that contains a list of 64bit entries containing properties such as the location of each buffers (1 entry per buffer). The pointer to the descriptor list must then be given to the MAC module. It is the same concept as the segment descriptors on the i386. It is important to use physical addresses in descriptors and in RBQP and TBQP.

Initialization

MAC

First thing to do is to determine if you want to use RMII or MII communication. RMII requires less pins on the chip but requires a higher clock rate. In my case, I chose to use MII. For MII, all MACB related pins must be disabled in the PIO. A quick look at pages 84,85 of the datasheet will show you what pins need to be configured. Basically, you want to tell the MCU that those pins will be controlled by the MACB and that they are NOT general purpose pins. This will cause conflicts with the LCDC pins though.

After that, you need to load the MAC address, setup the descriptor tables and enable the transmitter/receiver.

PHY

By looking at my source code, you will see that the PHY initialization is very easy. This process allows you to select auto-MDX mode, select LED modes and the link speed. In my case, I only advertise 100mbps FD during auto-negotiation. The steps are: Enable auto-MDX, start auto-negotiation, and wait for link status to be up. Note that when setting auto-negotation, this will tell the chip to do auto-negotiation everytime it detects a cable plugged in. So it is not necessary to re-init the PHY everytime a cable is plugged in. You configure it once and it is good to go for as long as you leave the power on.

The PHY can be configured to generate an interrupt on an output pin when the link status changes. The AVR32 chip can detect this by triggering on a pin change interrupt. Unfortunately, the PHY INT pin is not connnected on the AVR32 on the NGW100 so it is impossible to detect this.

LCD Controler

The ap7000 has an integrated LCDC. The LCD I am using is a Hitachi TX14D12VM1CBB (TFT)

Wiring

If you look at the pinout of the ap7000, you will notice that some of the LCDC pins are duplicated. This is because each pin has a dual function and this duplication allows us to avoid possible conflicts. In my case, I want to be able to use the MACB1 device but the pins are shared with the LCDC. You can use the PIO to specify that, for example, pin PC26 be assigned to peripheral B. In that case this would be MACB1_TX_ERR. For more information on peripheral selection, see section 9.7 in the ap7000 datasheet. So if PC26 is used for MACB, then what do we do in order to use LCDC_DATA0? Lucky for us, that function is also available on PE03. Since the NGW100 uses 16 lines to access the sdram, PE0 to PE15 do not need to be assigned to peripheral A, so we can use peripheral B, the LCDC. Another major reason why we would use those pins is because they are broken out on header J7 on the NGW100.

Some people say that on the NGW100, the SD card could conflict with the LCDC. This is because the pin that triggers an interrupt when an SD card is inserted is connected to pin LCDC_PWR. That pin is supposed power the LCD. We won't use that pin and we can set it as a GPIO so that the LCDC won't use it. The VDD connections of the LCD will instead be connected on the 3.3V pin of the J7 header.

The DTMG pin on the LCD is a "Data valid" pin. It replaces HSync and VSync. This needs to be connected on the DVAL pin of the ap7000

The following is a table showing the connections between the Hitachi TX14D12VM1CBB and the J7 header on the NGW100. Note that this is of 16bit colors only.

J7 pinLCD pinLCD pin nameAP700 pin name
331,2,3,4VDD-
347,9,11,15,19,23,27,31,36,VSS-
256DTMGPE01
288DCLKPC21
533R1PE07
632R2PC31
730R3PD00
829R4PD01
328R5PE05
1226G0PE11
1325G1PE12
1424G2PD07
1522G3PD08
1621G4PD09
420G5PD06
2117B1PE17
2216B2PE18
2314B3PD16
2413B4PD17
1912B5PE15

Initialization

The LCDC uses two clock sources. The first one is the one we enable in the HSB MASK in the Power Manager This is done the same way as we do for any other devices like MACB, PBB, PBA etc..

The second one is the clock that will be used by the LCD constroller core and drive the pixel clock. This clock is refered to as the LCD Core Clock in the datasheet. If you look at the Power Manager section in the datasheet, you will notice that they talk about Generic Clocks. The LCD Core Clock is sourced by the Generic Clock #7. You need to configure the GCCTRL7 register in the Power Manager to enable it and choose whether the PLL or the OSC will be used as for this generic clock. My PLL is 150MHZ, but the datasheet mentions that "When the LCDC is being used in a system with SDRAM, the SDRAM clock frequency must be greater than the frequency of the LCDC Core Clock". Since my SDRAM runs at 75MHZ, we will use the PLL divided by 4 to get a frequency of 37.5MHZ. So my Generic Clock #7 (and this the LCD Core Clock) will run at 37.5MHZ.

You must then configure the Pixel Clock rate. This is the rate at which data will be sent to the LCD. With a resolution of 640x480 with a bit depth of 16bit and frame rate of 60hz, we would need to transfer 294912000 bits/s to the LCD. Since the LCDDC transfers 16bits at a time, we only need 18432000 pixel clocks per seconds. The LCD Core Clock is set to 37.5MHZ, so this is definitely too fast so we need to divide that rate using the CLKVAL in LCDCON1: CLKVAL = FLOOR((LCDC_Core_Clock/2*18432000)-1) = 0 Since we lost precision because we FLOOR'd the value, we get GENERIC_CLK7/((0+1)*2) = 18.75MHZ This is not exactly what we wanted but it is still in spec with the LCD datasheet.

This section is incomplete as I am still figuring it out


Asterisk Sound Injection ApplicationLast edited on Feb 25, 2012

Sound injection Application

This application, while not very usefull, is quite funny. I use it to inject sounds on an arbitrary channel, part of an active call, and will not be heard by the other peer. This will act a bit like ChanSpy, except that when pressing on a digit on your keypad, a sound file will be played on the specified channel. For example, let's say that ipphone1 is currently talking talking to ipphone2. With ipphone3, you call an extention that executes Inject(SIP/ipphone2). This will allow you to hear the conversation between ipphone1 and ipphone2. Now you would like to inject a sample that says "What is your name?" (a sound file recorded with the voice of the user using ipphone1). This can now be done by pressing a digit on your keypad. Not only they will not hear you on the other end, but the sound file that you have just injected will be heard by ipphone2 only. This could create a lot of confusion for both users.

Compiling and Installing

First, get a copy of the source code. Untar in any folder you like. With this one, the source code doesn't need to reside in the asterisk source tree, but you will need the asterisk sources on your computer. You might need to change the Makefile since the folders in there are good for my installation, but might not be good for yours. It's a one-line change anyway. Next, just type "make" to build the app, or "make load" to automatically build,install and load the module into asterisk. so basically:

wget http://www.dumaisnet.ca/files/app_inject.tar
tar -xf app_inject.tar
# you might need to modify the ASTMODULES and INCLUDES paths in Makefile
make load

Configuration

No configuration files are needed. The only thing you need to setup is the dialplan. You only need to call Inject(CHANNEL). No need to answer first, the application will take care of it.

exten => *7,1,Inject(SIP/ipphone)
exten => *7,2,Hangup()

You will need to provide sound files for when you will press digits to play the different sounds that you need. You need to copy 10 sound files in your sound folder (usualy /var/lib/asterisk/sounds). They MUST follow the name scheme "injection0.gsm", "injection1.gsm", ... "injection9.gsm". Of course, you could use .wav or .gsm. As you guessed it, pressing on "5" will play injection5.gsm. It is not necessary to provide all sound files.

Download

app_inject.tar



Asterisk Filter ApplicationLast edited on Feb 25, 2012

Filter application

I use this application to manage a blacklist of telemarketers. Althought this could be easily done with a perl script, I'd rather have a real application for it. This application will allow you to define several groups. In each group, you add a list of callerID, an optional greeting message and a context/extension pair to jump to. If the callerID of an incomming call matches a callerID defined in a group, the group's greeting message will be played and the dialplan will jump to the context/extension defined for that group. If no greeting is defined, none will be played. If no context/extension pair is defined, dialplan execution will continue normaly.

Compiling and Installing

After downloading app_filter.c, copy the file in your asterisk source three and compile it. In my example, I am assuming that your asterisk source code is in /usr/src/asterisk-1.4.21.1/apps/ and that your modules are located in /usr/lib/asterisk/modules/.

cp app_filter.c /usr/src/asterisk-1.4.21.1/apps/
cd /usr/src/asterisk-1.4.21.1
make apps
cp apps/app_filter.so /usr/lib/asterisk/modules/

next, go into the asterisk CLI and load the module using "module load app_filter.so". Of course it is also possible to have the module loaded automatically using /etc/asterisk/modules.conf. Now you are ready to use the module

Configuration

You must first create /etc/asterisk/filter.conf. Add the following:

[general]
dbtype => 0

"dbtype" must be set to 0 if the same configuration file is going to be used for defining filter groups. Set dbtype to 1 if you prefer mysql. If you are going to be using mysql, you can skip to the next section. Otherwise, here's how to do it. Note that the configuration file is read each time that Filter() is called

You can several sections, in the file. In each section, add as many callerIDs as you want using the "cid:" directive. Define an optional greeting with "greeting", context with "context" and extension to "exten" Complete example:

[general]
dbtype => 0

[test1]
cid => 1001234567
cid => 1001234566
greeting => greeting1 ; do not add the extension. This file must be present in /var/lib/asterisk/sounds

[test2]
cid => 1001234565
cid => 1001234564
greeting => not-welcome
context => telemarketer-torture
exten => s

In this example, caller with callerID 100-123-4565 will hear the "not-welcome.wav" greeting message and will be redirected to extension "s" in the "telemarketer-torture" context

Configuration with mysql

mysql support is not implemented yet

Dialplan

Here is how you should use it in the dialplan:

exten => s,1,Answer()
exten => s,2,Filter()

Download

The source code can be found here: app_filter.c



Aastra weather xml applicationLast edited on Feb 25, 2012

Weather

This application will only work for cities in Canada. The ministry of the Environment provides RSS feeds on a website for every (almost every) cities in canada. I made an application that downloads the RSS feed, parses it and then presents it in a format that is suitable for the Aastra phone. The current package has the city of Ottawa hardcoded in it but this is easily changed.

Download

weather.tar



Pages:12345678910111213