sleep & delay function (2)

To let the thread to suspend the exact amount of time, without being affected by thread scheduling, we can use nanospin().

int nanospin( const struct timespec *when );

 

The nanospin() function occupies the CPU for the amount of time specified by the argument when without blocking the calling thread. (The thread isn’t taken off the ready list.) The function is essentially a do…while loop.

The first time you call nanospin(), the C library invokes nanospin_calibrate() with an argument of 0 (interrupts enabled), if you haven’t already called it.

int nanospin_ns( unsigned long nsec );

The nanospin_ns() function busy-waits for the number of nanoseconds specified in nsec, without blocking the calling thread.

void nanospin_count( unsigned long count );

The nanospin_count() function busy-waits for the number of iterations specified in count. Use nanospin_ns_to_count() to turn a number of nanoseconds into an iteration count suitable for nanospin_count().

Advertisements

sleep & delay functions (1)

Quoted from QNX document.

— delay(unsigned int duration) suspends the calling thread for duration milliseconds.

— sleep(unsigned int seconds) function suspends the calling thread until the number of realtime seconds specified by the seconds argument have elapsed, or the thread receives a signal whose action is either to terminate the process or to call a signal handler.

both delay() and sleep() returns either 0 or the number of unslept time if interrupt by a signal.

— usleep(useconds_t useconds) function suspends the calling thread until useconds microseconds of realtime have elapsed, or until a signal that isn’t ignored is received.

— nanosleep( const struct timespec* rqtp, struct timespec* rmtp )  function causes the calling thread to be suspended from execution until either:

  • The time interval specified by the rqtp argument has elapsed

    Or

  • A signal is delivered to the thread, and the signal’s action is to invoke a signal-catching function or terminate the process.

usleep() and nanosleep()  returns either 0 (success) or -1 (an error occured)

 

Note:

With all the functions above, the suspension time may be greater than the requested amount, due to the nature of time measurement (see the Tick, Tock: Understanding the Neutrino Microkernel’s Concept of Time chapter of the QNX Neutrino Programmer’s Guide), or due to the scheduling of other, higher priority threads by the system.

Interrupt(8):callout for cascaded interrupts

SDMA interrupt entry is added in init_intrinfo().

 Identify SDMA interrupt source.
 *
 * Returns interrupt number in r4
 * -----------------------------------------------------------------------
 */
CALLOUT_START(interrupt_id_omap4_sdma, 0, interrupt_patch_sdma)
	/*
	 * Get the interrupt controller base address (patched)
	 */
	mov		ip,     #0x000000ff
	orr		ip, ip, #0x0000ff00
	orr		ip, ip, #0x00ff0000
	orr		ip, ip, #0xff000000

	/*
	 * Read Interrupt Mask and Status
	 */
	ldr		r3, [ip, #SDMA_IRQSTATUS]		// Status
	ldr		r2, [ip, #SDMA_IRQENABLE]		// Mask
	and		r3, r3, r2

	/*
	 * Scan for first set bit
	 */
#if 0
	mov		r4, #32
	mov		r1, #1

0:
	subs	r4, r4, #1
	blt		1f
	tst		r3, r1, lsl r4
	beq		0b
#else
	clz		r4, r3
	rsbs	r4, r4, #31
	blt		1f
	mov		r1, #1
#endif
    /*
	 * Mask the interrupt source
	 */
	mov		r1, r1, lsl r4
	bic		r2, r2, r1
	str		r2, [ip, #SDMA_IRQENABLE]
	ldr		r2, [ip, #SDMA_IRQENABLE]

	/*
	 * Clear interrupt status
	 * 09.17.2014: clearing the staus bit is moved to the eoi-callout since the status bit related
	 * to a channel can only be claered if the channel status register of the associated
	 * channel is cleared. Clearing the csr can't be done in a generic way here because the attached
	 * isterrupt service routines need to know the interrupt reason (block, fram, drop etc. ...)
	 */
	//str		r1, [ip, #SDMA_IRQSTATUS]
1:
CALLOUT_END(interrupt_id_omap4_sdma)

/*
 * -----------------------------------------------------------------------
 * Acknowledge specified SDMA interrupt
 *
 * On entry:
 *	r4 contains the interrupt number
 *	r7 contains the interrupt mask count
 * -----------------------------------------------------------------------
 */
CALLOUT_START(interrupt_eoi_omap4_sdma, 0, interrupt_patch_sdma)
	/*
	 * Get the interrupt controller base address (patched)
	 */
	mov		ip,     #0x000000ff
	orr		ip, ip, #0x0000ff00
	orr		ip, ip, #0x00ff0000
	orr		ip, ip, #0xff000000

    /*
     * Only unmask interrupt if mask count is zero
     */
	teq		r7, #0
	bne		0f
	
	/*
	 * Clear interrupt status
	 * see comment in the id-callout
	 */
	mov		r2, #1
	mov		r2, r2, lsl r4
	str		r2, [ip, #SDMA_IRQSTATUS]

	ldr		r1, [ip, #SDMA_IRQENABLE]
	orr		r1, r1, r2
	str		r1, [ip, #SDMA_IRQENABLE]

0:
CALLOUT_END(interrupt_eoi_omap4_sdma)

ARM assembly language

Parameter passing

  1. use registers for first 4 parameters (r0, r1, r2, r3);
  2. use stack beyond;
  3. return using r0.
  4. Functions can freely modify registers R0–R3 and R12. If a function needs to use R4 through R11, it is necessary to push their current register values onto the stack, use the register, and then pop the old value off the stack before returning.

Variable declaration

.balign 4      //  ensure the next address will start a 4-byte boundary.

myvar1: .word 3  // .word directive states that the assembler tool should emit the value of the argument of the directive as a 4 byte integer. the initial value of the variable is 3.

Instructions

ANDS {Rd,} Rn, Rm

ORRS {Rd,} Rn, Rm  ==> inclusive OR           Used to set bits

EORS {Rd,} Rn, Rm  ==> exclusive OR (only true if the corresponding bits differ)

BICS {Rd,} Rn, Rm  == >                           Used to clear bits

TEQ{cond} Rn, Operand2  == > same as EORs, except that the result is discarded ==> use for check if the bits of Rn is same as Operand2, result is 0 if same; result is 1 if different.

BEQ label                        ==> branch if equal to 0; used in conjunction with the previous instruction

MOV Rd, #expr

MOV Rd, Rm

MVN Rd, Rm  ==> takes the value in Rm, performs a bitwise logical NOT operation on the value, and places the result in Rd.

NEG Rd, Rm   ==> takes the value in Rm, multiplies it by –1, and places the result in Rd.

MOV r0, r0, LSL #1   ==> r0 << 1

 

arm_regs

syspage(3): callouts

In order for the Neutrino microkernel to work on all boards, all hardware-dependent operations have been factored out of the code — Known as kernel callouts.

Callouts:

  • are provided by the startup program.
  • get overwritten when the kernel starts up.
    • Startup program will copy the callouts (the code between CALLOUT_START and CALLOUT_END)from the startup program into the system page and after this, the startup memory (text and data) is freed.
  • allow you to “hook into” the kernel and gain control when a given event occurs.
    • The callouts operate in an environment similar to that of an interrupt service routine — you have a very limited stack, and you can’t invoke any kernel calls (such as mutex operations, etc.).
  • must be Position-independent
    • reason: they won’t be in the location that they were loaded in, they must be coded to be position-independent.
    • how: be coded in assembler
  • No static read/write storage
    • if needed, you can make a small storage available for it, by using the patcher routines and the 2nd parameter to CALLOUT_START. see the example below on how it works.
  • For all but two of teh routines (interrupt_id(), interrupt_eoi), the kernel invokes the callouts with the normal function-calling conventions
    • For performance reasons, the kernel intermixes id() and eoi() directly with kernel code.

Types of Callouts

  • debug interface
  • clock/timer interface
  • interrupt controller interface
    • 3 callouts for interrupt controller interface: mask(), unmask(), config().
    • 2 callouts as code stubs: id(), eoi()
    • Each group of callouts (i.e. id, eoi, mask, unmask) for each level of interrupt controller deals with a set of interrupt vectors that start at 0 (zero-based).
  • cache controller interface
  • system reset
  • power management

Callout macros, defined in “callout.ah”

rw_intr: .word 8

CALLOUT_START(interrupt_id_gic, rw_intr, patch_id)

  •   1st parameter: name of the callout routine
  • 2nd parameter: address of a 4-byte variable that contains the amount of  read/write storage the callout needs.
  • 3rd parameter: either a zero or the address of a patcher() routine.

CALLOUT_END(interrupt_id_gic)

“patching” the callout code

  • make it possible for the same callout routine to be used on the different boards, where the device might be at different locations.
  • a patcher() is invoked immediately after the callout has been copied to its final resting place.
  • make read-write storage available for the callout, when used together with 2nd parameter of CALLOUT_START.
    • “rw_intr: .word 8″ tells the startup library that the routine needs 8 bytes of read/write storage.
    • The startup library allocates space at the end of the system page and passes the offset to it as the rw_offset parameter of the patcher routine.
    • The patcher routine then modifies the initial instruction of the callout to the appropriate offset.
    • While the callout is executing, the t3 register will contain a pointer to the read/write storage.

syspage(2)-intrinfo

intrinfo is automatically filled in by init_intrinfo()

Note:

  1. struct startup_intrinfo is defined and used by startup code. the public definition is in sys/syspage.h.
  2. Each group of callouts (i.e. id, eoi, mask, unmask) for each level of interrupt controller deals with a set of interrupt vectors that start at 0 (zero-based). Interrupt vector numbers are passed without offset to the callout routines. The association between the zero-based interrupt vectors the callouts use and the system-wide interrupt vectors is configured within the startup-intrinfo structures.
  3. flags:
    • INTR_GENFLAG_LOAD_SYSPAGE 
      • Before the interrupt id or eoi code sequence is generated, a piece of code needs to be inserted to fetch the system page pointer into a register so that it’s usable within the id code sequence.
    • INTR_GENFLAG_LOAD_INTRMASK
      • Used only by EOI routines for hardware that doesn’t automatically mask at the chip level.
      • When the EOI routine is about to reenable interrupts, it should reenable only those interrupts that are actually enabled at the user level (e.g. managed by the functions InterruptMask() and InterruptUnmask()). When this flag is set, the existing interrupt mask is stored in a register for access by the EOI routine. A zero in the register indicates that the interrupt should be unmasked; a nonzero indicates it should remain masked.
struct intrinfo_entry {
	_Uint32t vector_base; // base number of the logical interrupt/vector numbers(IRQs) that programs will use
	_Uint32t num_vectors; // the number of the vectors
	_Uint32t cascade_vector;// the logical IRQ number for cascaded interrupts
	_Uint32t cpu_intr_base;
	_Uint16t cpu_intr_stride;
	_Uint16t flags;
	struct __intrgen_data	id;
	struct __intrgen_data	eoi;
	_SPFPTR(int, mask, (struct syspage_entry *, int));
	_SPFPTR(int, unmask, (struct syspage_entry *, int));
	_SPFPTR(unsigned, config, (struct syspage_entry *, struct intrinfo_entry *, int));
	_Uint32t				spare[4];
};

A piece of sample code:

// Adding main ARM GIC Controller
const static struct startup_intrinfo intrs[] =
{
    {
        .vector_base      = _NTO_INTR_CLASS_EXTERNAL, // (0x0000UL << 16)
        .num_vectors      = 32+192, // including SGIs, PPIs, SPIs
        .cascade_vector   = _NTO_INTR_SPARE, // (0x7FFFFUL << 16) | 0xFFFF
        .cpu_intr_base    = 0,
        .cpu_intr_stride  = 0,
        .flags            = 0,
        .id               = { INTR_GENFLAG_LOAD_SYSPAGE, 0, &interrupt_id_gic},
        .eoi              = { INTR_GENFLAG_LOAD_SYSPAGE | INTR_GENFLAG_LOAD_INTRMASK, 0, &interrupt_eoi_gic},
        .mask             = &interrupt_mask_gic,
        .unmask           = &interrupt_unmask_gic,
        .config           = &interrupt_config_gic,
        .patch_data       = NULL,
    }
};
// Adding System DMA interrupt cascaded into OMAP54XX_SDMA_IRQ_0 only
static struct startup_intrinfo sdmaintrs[] = 
{
    {
        .vector_base      = 256,
        .num_vectors      = 32,
        .cascade_vector   = OMAP54XX_SDMA_IRQ_0,
        .cpu_intr_base    = 0,
        .cpu_intr_stride  = 0,
        .flags            = 0,
        .id               = { 0, 0, &interrupt_id_omap4_sdma },
        .eoi              = { INTR_GENFLAG_LOAD_INTRMASK, 0, &interrupt_eoi_omap4_sdma },
        .mask             = &interrupt_mask_omap4_sdma,
        .unmask           = &interrupt_unmask_omap4_sdma,
        .config           = 0,
        .patch_data       = &sdma_base,
    },
};
init_intrinfo()
{
    initialize the interrupt controller; // See the sample code in Interrupt(2): ARM Interrupt Controller
    add_interrupt_array(intrs, sizeof(intrs));
    disable all SDMA channel interrupts, and clear all channel statuses; 
    add_interrupt_array(sdmaintrs, sizeof(sdmaintrs));
}

syspage(1)

System page: an in-memory data structure which stores the information about the system, e.g. processor type, the location and size of available system RAM.

  • the system page is initialized by the startup program.
  • the kernel as well as applications can access this information as a read-only data structure.
  • struct syspage_entry is defined in <sys/syspage.h>

struct syspage_entry {
uint16_t size;
uint16_t total_size;
uint16_t type;          // ARM, MIPS, PPC, SH4, or X86
uint16_t num_cpu;
syspage_entry_info system_private;
syspage_entry_info asinfo;
syspage_entry_info hwinfo;
syspage_entry_info cpuinfo; //  CPU type, speed, capabilities, performance, and cache sizes, etc
syspage_entry_info cacheattr;
syspage_entry_info qtime;  // get a timestamp via: SYSPAGE_ENTRY(qtime)->nsec
syspage_entry_info callout; // allow you to “hook into” the kernel and gain control when a given event occurs.
syspage_entry_info callin;
syspage_entry_info typed_strings;
syspage_entry_info strings;
syspage_entry_info intrinfo;  // information about the interrupt system
syspage_entry_info smp;
syspage_entry_info pminfo;
union {
struct x86_syspage_entry x86;
struct ppc_syspage_entry ppc;
struct mips_syspage_entry mips;
struct arm_syspage_entry arm;
struct sh_syspage_entry sh; }}