So, sorry if this has already been answered somewhere, I can't seem to see anything:
I have been working on a context swap, and it seems that sdcc, even with model large used, uses the local stack for the address of the return function. Because I am swapping, this is trashed, so I have been using the peephole to replace LCALL/RET with LJMP. I think that code should now work as intended, but now, when I try to compile, I get the error:
../../src/kernel/source/nrk.c:212: error 9: FATAL Compiler Internal Error in fil
e '/home/sdcc-builder/build/sdcc-build/orig/sdcc/src/mcs51/peep.c' line number '
180' : error in deadmove
Contact Author with source code
where peep.c is my peephole file. If I compile with the peep.c empty, there is no error. If I compile with the function in nrk.c removed, I just get an error 9 somewhere else.
What is "deadmove," and why is the my peephole file and the .c file interacting in the first place? Am I going about this whole thing wrong?
Compiled with version SDCC : mcs51/gbz80/z80/avr/ds390/pic16/pic14/TININative/xa51/ds400/hc08 2.8.4 #5242 (Sep 22 2008) (MINGW32)
replace {
ljmp %5
} by {
sjmp %5
} if labelInRange
____________________________________________
nrk.c________________________________________
/******************************************************************************
* Nano-RK, a real-time operating system for sensor networks.
* Copyright (C) 2007, Real-Time and Multimedia Lab, Carnegie Mellon University
* All rights reserved.
*
* This is the Open Source Version of Nano-RK included as part of a Dual
* Licensing Model. If you are unsure which license to use please refer to:
* http://www.nanork.org/nano-RK/wiki/Licensing
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.0 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contributing Authors (specific to this file):
* Anthony Rowe
* Zane Starr
* Anand Eswaren
*******************************************************************************/
// Setup a double linked list of Ready Tasks
for (i=0;i<NRK_MAX_TASKS;i++)
{
_nrk_readyQ[i].Next = &_nrk_readyQ[i+1];
_nrk_readyQ[i+1].Prev = &_nrk_readyQ[i];
}
movx a, @dptr
mov _spx,a
inc dptr
movx a, @dptr
mov __XPAGE,a
mov a, _spx
mov r0,a
subb a,#0xD
mov _spx,a
;Get the tasks PC and put it on the local stack
dec r0
movx a,@r0
push acc
dec r0
movx a,@r0
push acc
dec r0
movx a,@r0
mov psw,a
dec r0
movx a,@r0
mov r1,a
dec r0
movx a,@r0
;get r0 and put it on the local stack
push acc
dec r0
movx a,@r0
mov r7,a
dec r0
movx a,@r0
mov r6,a
dec r0
movx a,@r0
mov r5,a
dec r0
movx a,@r0
mov r4,a
dec r0
movx a,@r0
mov r3,a
dec r0
movx a,@r0
mov r2,a
dec r0
movx a,@r0
mov dph,a
dec r0
movx a,@r0
mov dpl,a
dec r0
movx a,@r0
mov b,a
dec r0
movx a,@r0
pop ar0
reti
_endasm;
}
int8_t nrk_TCB_init (nrk_task_type *Task, NRK_STK *ptos, NRK_STK *pbos, uint16_t stk_size, void *pext, uint16_t opt)
{
// Already in critical section so no needenter critical section
if(Task->Type!=IDLE_TASK)
Task->task_ID=nrk_task_init_cnt;
else Task->task_ID=NRK_IDLE_TASK_ID;
if(nrk_task_init_cnt>=NRK_MAX_TASKS) nrk_kernel_error_add(NRK_EXTRA_TASK,0);
if(Task->Type!=IDLE_TASK) nrk_task_init_cnt++;
if(nrk_task_init_cnt==NRK_IDLE_TASK_ID) nrk_task_init_cnt++;
//initialize member of TCB structure
nrk_task_TCB[Task->task_ID].OSTaskStkPtr = ptos;
nrk_task_TCB[Task->task_ID].task_prio = Task->prio;
nrk_task_TCB[Task->task_ID].task_state = SUSPENDED;
nrk_task_TCB[Task->task_ID].task_ID = Task->task_ID;
nrk_task_TCB[Task->task_ID].suspend_flag = 0;
nrk_task_TCB[Task->task_ID].period= _nrk_time_to_ticks( (void *)Task->period ); // sdcc can not do cast
nrk_task_TCB[Task->task_ID].next_wakeup= _nrk_time_to_ticks( (void*)Task->offset); // sdcc can not do cast
nrk_task_TCB[Task->task_ID].next_period= nrk_task_TCB[Task->task_ID].period+nrk_task_TCB[Task->task_ID].next_wakeup;
nrk_task_TCB[Task->task_ID].cpu_reserve= _nrk_time_to_ticks((void *)Task->cpu_reserve); // sdcc can not do cast
nrk_task_TCB[Task->task_ID].cpu_remaining = nrk_task_TCB[Task->task_ID].cpu_reserve;
nrk_task_TCB[Task->task_ID].num_periods = 1;
nrk_task_TCB[Task->task_ID].OSTCBStkBottom = pbos;
nrk_task_TCB[Task->task_ID].errno= NRK_OK;
return NRK_OK;
}
/*
* _nrk_timer_tick()
*
* This function is called by the interrupt timer0.
* It calls the scheduler.
*/
void _nrk_timer_tick(void)
{
// want to do something before the scheduler gets called?
// Go ahead and put it here...
_nrk_scheduler();
return;
}
So, sorry if this has already been answered somewhere, I can't seem to see anything:
I have been working on a context swap, and it seems that sdcc, even with model large used, uses the local stack for the address of the return function. Because I am swapping, this is trashed, so I have been using the peephole to replace LCALL/RET with LJMP. I think that code should now work as intended, but now, when I try to compile, I get the error:
sdcc --peep-file ../../src/kernel/hal/cc243X/peep.c --stack-auto --xstack --mod
el-large -L "C:\Program Files\SDCC\lib\large-stack-auto-xstack" -I. -mmcs51 --xr
am-loc 0xE000 -D NANORK -D __SDCC__ -D NODE_ADDR=0 -I . -I../../src/platform/inc
lude -I../../src/platform/cc243X/include -I../../src/radio/cc243X/include -I../.
./src/radio/cc243X/hal/cc243X -I../../src/radio/cc243X/platform/cc243X -I../../s
rc/drivers/include -I../../src/drivers/platform/cc243X/include -I../../src/kerne
l/include -I../../src/kernel/hal/include --std-sdcc89 --debug -c ../../src/ke
rnel/source/nrk.c -o ../../src/kernel/source/nrk.rel
...
../../src/kernel/source/nrk.c:212: error 9: FATAL Compiler Internal Error in fil
e '/home/sdcc-builder/build/sdcc-build/orig/sdcc/src/mcs51/peep.c' line number '
180' : error in deadmove
Contact Author with source code
where peep.c is my peephole file. If I compile with the peep.c empty, there is no error. If I compile with the function in nrk.c removed, I just get an error 9 somewhere else.
What is "deadmove," and why is the my peephole file and the .c file interacting in the first place? Am I going about this whole thing wrong?
Compiled with version SDCC : mcs51/gbz80/z80/avr/ds390/pic16/pic14/TININative/xa51/ds400/hc08 2.8.4 #5242 (Sep 22 2008) (MINGW32)
files peep.c and nrk.c below:
peep.c_____________________________________________________________
replace {
lcall %1
} by {
push acc
mov r0, _spx
mov a,pcl
add a,#0x15
movx @r0,a
inc r0
mov a,pch
movx @r0,a
inc r0
mov _spx, r0
pop acc
ljmp %1
}
replace {
reti
}by{
ri
}
replace {
ret
} by {
xch a,r1
mov r0, _spx
dec r0
movx a,@r0
push acc
dec r0
movx a,@r0
push acc
xch a,r1
rrt
}
replace {
rrt
} by {
ret
}
replace {
ri
} by {
reti
}
replace {
sjmp %1
} by {
ljmp %1
}
replace {
jz %1
} by {
jnnz (pc+3)
ljmp %1
}
replace {
jnnz (pc+3)
ljmp %5
} by {
jz %5
} if labelInRange
replace {
jnz %1
} by {
jz (pc+3)
ljmp %1
}
replace {
jz (pc+3)
ljmp %5
} by {
jnz %5
} if labelInRange
replace {
jnnz (pc+3)
} by {
jnz (pc+3)
}
replace {
jc %1
} by {
jnnc (pc+3)
ljmp %1
}
replace {
jnnc (pc+3)
ljmp %5
} by {
jc %5
} if labelInRange
replace {
jnc %1
} by {
jc (pc+3)
ljmp %1
}
replace {
jc (pc+3)
ljmp %5
} by {
jnc %5
} if labelInRange
replace {
jnnc (pc+3)
} by {
jnc (pc+3)
}
replace {
jb %1,%2
} by {
jnnb %1,(pc+3)
ljmp %2
}
replace {
jnnb %1,(pc+3)
ljmp %5
} by {
jb %1,%5
} if labelInRange
replace {
jnb %1,%2
} by {
jb %1,(pc+3)
ljmp %2
}
replace {
jb %1,(pc+3)
ljmp %5
} by {
jnb %1,%5
} if labelInRange
replace {
jnnb %1,(pc+3)
} by {
jnb %1,(pc+3)
}
replace {
cjne a,%2,%3
} by {
subb a,%2
jz (pc+3)
ljmp %3
}
replace {
cjne %1,%2,%3
} by {
mov a,%1
subb a,%2
jz (pc+3)
ljmp %3
}
replace {
ljmp %5
} by {
sjmp %5
} if labelInRange
____________________________________________
nrk.c________________________________________
/******************************************************************************
* Nano-RK, a real-time operating system for sensor networks.
* Copyright (C) 2007, Real-Time and Multimedia Lab, Carnegie Mellon University
* All rights reserved.
*
* This is the Open Source Version of Nano-RK included as part of a Dual
* Licensing Model. If you are unsure which license to use please refer to:
* http://www.nanork.org/nano-RK/wiki/Licensing
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.0 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contributing Authors (specific to this file):
* Anthony Rowe
* Zane Starr
* Anand Eswaren
*******************************************************************************/
#include <stdio.h>
#include <nrk_includes.h>
#include <ulib.h>
#include <nrk.h>
#include <nrk_task.h>
#include <nrk_idle_task.h>
#include <nrk_defs.h>
#include <nrk_cpu.h>
#include <nrk_scheduler.h>
#include <nrk_error.h>
#include <nrk_events.h>
#include <nrk_stack_check.h>
#include <nrk_status.h>
#include <nrk_watchdog.h>
#include <nrk_reserve.h>
#include <nrk_cfg.h>
#include <nrk_stats.h>
#ifdef __SDCC__
void nrk_int_disable(void) {
DISABLE_GLOBAL_INT();
}
void nrk_int_enable(void) {
ENABLE_GLOBAL_INT();
}
#else
inline void nrk_int_disable(void) {
DISABLE_GLOBAL_INT();
};
inline void nrk_int_enable(void) {
ENABLE_GLOBAL_INT();
};
#endif
uint8_t nrk_task_init_cnt;
NRK_STK *nrk_kernel_stk_ptr;
NRK_TCB nrk_task_TCB[NRK_MAX_TASKS];
NRK_STK nrk_idle_task_stk[NRK_TASK_IDLE_STK_SIZE]; /* Idle task stack */
#ifdef KERNEL_STK_ARRAY
NRK_STK nrk_kernel_stk[NRK_KERNEL_STACKSIZE];
#endif
void nrk_halt()
{
nrk_int_disable();
while(1);
}
/**
* nrk_init();
* * - Init TCBlist - linked list of empty TCBs
* - Init global variables
* - Init event list
* - Create idle task
*/
void nrk_init()
{
uint8_t i;
// unsigned char *stkc;
nrk_task_type IdleTask;
printf("Start init: %u\r\n",nrk_task_init_cnt);
nrk_wakeup_signal = nrk_signal_create();
if(nrk_wakeup_signal==NRK_ERROR) nrk_kernel_error_add(NRK_SIGNAL_CREATE_ERROR,0);
//if((volatile)TCCR1B!=0) nrk_kernel_error_add(NRK_STACK_OVERFLOW,0);
if(_nrk_startup_ok()==0) nrk_kernel_error_add(NRK_BAD_STARTUP,0);
#ifdef NRK_STARTUP_VOLTAGE_CHECK
if(nrk_voltage_status()==0) nrk_kernel_error_add(NRK_LOW_VOLTAGE,0);
#endif
#ifdef NRK_REBOOT_ON_ERROR
#ifndef NRK_WATCHDOG
while(1)
{
nrk_kprintf( PSTR("KERNEL CONFIG CONFLICT: NRK_REBOOT_ON_ERROR needs watchdog!\r\n") );
for (i = 0; i < 100; i++)
nrk_spin_wait_us (1000);
}
#endif
#endif
#ifdef NRK_WATCHDOG
if(nrk_watchdog_check()==NRK_ERROR)
{
nrk_watchdog_disable();
nrk_kernel_error_add(NRK_WATCHDOG_ERROR,0);
}
nrk_watchdog_enable();
#endif
nrk_stack_pointer_init();
/*
#ifdef KERNEL_STK_ARRAY
stkc = (uint16_t*)&nrk_kernel_stk[NRK_KERNEL_STACKSIZE-1];
nrk_kernel_stk[0]=STK_CANARY_VAL;
nrk_kernel_stk_ptr = &nrk_kernel_stk[NRK_KERNEL_STACKSIZE-1];
#else
stkc = NRK_KERNEL_STK_TOP-NRK_KERNEL_STACKSIZE;
*stkc = STK_CANARY_VAL;
stkc = NRK_KERNEL_STK_TOP;
nrk_kernel_stk_ptr = NRK_KERNEL_STK_TOP;
#endif
*stkc++ = (uint16_t)((uint16_t)_nrk_timer_tick>>8);
*stkc = (uint16_t)((uint16_t)_nrk_timer_tick&0xFF);
*/
// printf( "Init kernel_entry= %d %d\n",kernel_entry[1], kernel_entry[0] );
printf("mid init: %u\r\n",nrk_task_init_cnt);
nrk_cur_task_prio = 0;
nrk_cur_task_TCB = NULL;
nrk_high_ready_TCB = NULL;
nrk_high_ready_prio = 0;
#ifdef NRK_STATS_TRACKER
nrk_stats_reset();
#endif
#ifdef NRK_MAX_RESERVES
// Setup the reserve structures
_nrk_reserve_init();
#endif
_nrk_resource_cnt = NRK_MAX_RESOURCE_CNT;
for(i=0;i<NRK_MAX_RESOURCE_CNT;i++)
{
nrk_sem_list[i].count=-1;
nrk_sem_list[i].value=-1;
nrk_sem_list[i].resource_ceiling=-1;
//nrk_resource_count[i]=-1;
//nrk_resource_value[i]=-1;
//nrk_resource_ceiling[i]=-1;
}
for (i= 0; i<NRK_MAX_TASKS; i++)
{
nrk_task_TCB[i].task_prio = TCB_EMPTY_PRIO;
nrk_task_TCB[i].task_ID = -1;
}
// Setup a double linked list of Ready Tasks
for (i=0;i<NRK_MAX_TASKS;i++)
{
_nrk_readyQ[i].Next = &_nrk_readyQ[i+1];
_nrk_readyQ[i+1].Prev = &_nrk_readyQ[i];
}
_nrk_readyQ[0].Prev = NULL;
_nrk_readyQ[NRK_MAX_TASKS].Next = NULL;
_head_node = NULL;
_free_node = &_nrk_readyQ[0];
// SDCC can not do cast
nrk_task_set_entry_function( &IdleTask,(void *) nrk_idle_task);
nrk_task_set_stk( &IdleTask, nrk_idle_task_stk, NRK_TASK_IDLE_STK_SIZE);
nrk_idle_task_stk[0]=STK_CANARY_VAL;
//IdleTask.task_ID = NRK_IDLE_TASK_ID;
IdleTask.prio = 0;
IdleTask.offset.secs = 0;
IdleTask.offset.nano_secs = 0;
IdleTask.FirstActivation = TRUE;
IdleTask.Type = IDLE_TASK;
IdleTask.SchType = PREEMPTIVE;
nrk_activate_task(&IdleTask);
}
uint8_t* stkptr;
void nrk_start (void)
{
int8_t task_ID;
uint8_t i,j;
// NRK_STK *x;
// unsigned char *stkc;
NRK_TCB *new_nrk_TCB;
/*
- Get highest priority task from rdy list
- set cur prio and start the task
*/
// Check to make sure all tasks unique
// dump_stack_info();
task_ID = nrk_get_high_ready_task_ID();
nrk_high_ready_prio = nrk_task_TCB[task_ID].task_prio;
nrk_high_ready_TCB = nrk_cur_task_TCB = &nrk_task_TCB[task_ID];
//new_nrk_high_ready_TCB = nrk_task_TCB[task_ID];
//printf("new_nrk_high_ready_TCB %x",new_nrk_high_ready_TCB);
printf("Task %u, Ready= %u, Cur= %u,TCB= %x\r\n",task_ID,nrk_high_ready_prio,nrk_cur_task_prio,(uint16_t)nrk_high_ready_TCB);
nrk_cur_task_prio = nrk_high_ready_prio;
nrk_print_readyQ();
// nrk_stack_pointer_restore();
/*
#ifdef KERNEL_STK_ARRAY
stkc = (uint16_t*)&nrk_kernel_stk[NRK_KERNEL_STACKSIZE-1];
#else
stkc = NRK_KERNEL_STK_TOP;
#endif
*stkc++ = (uint16_t)((uint16_t)_nrk_timer_tick>>8);
*stkc = (uint16_t)((uint16_t)_nrk_timer_tick&0xFF);
//TODO: this way on msp
// *stkc++ = (uint16_t)((uint16_t)_nrk_timer_tick&0xFF);
// *stkc = (uint16_t)((uint16_t)_nrk_timer_tick>>8);
*/
// nrk_target_start();
_asm
mov _stkptr,sp
_endasm;
nrk_stack_pointer_init();
printf("1st %X %X\r\n",(uint16_t) nrk_high_ready_TCB,(*((xdata uint16_t*) nrk_high_ready_TCB)));
nrk_kprintf("XStack\r\n");
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)+2))));
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)+1))));
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)))));
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)-1))));
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)-2))));
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)-3))));
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)-4))));
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)-5))));
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)-6))));
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)-7))));
printf("%X\r\n",(*((xdata uint8_t*)(*((xdata uint16_t*) nrk_high_ready_TCB)-8))));
nrk_kprintf("Stack\r\n");
printf("%X\r\n",(*(stkptr+2)));
printf("%X\r\n",(*(stkptr+1)));
printf("%X\r\n",(*(stkptr)));
printf("%X\r\n",(*(stkptr-1)));
printf("%X\r\n",(*(stkptr-2)));
printf("%X\r\n",(*(stkptr-3)));
printf("%X\r\n",(*(stkptr-4)));
printf("%X\r\n",(*(stkptr-5)));
printf("%X\r\n",(*(stkptr-6)));
printf("%X\r\n",(*(stkptr-7)));
printf("%X\r\n",(*(stkptr-8)));
nrk_target_start();
nrk_start_high_ready_task();
// you should never get here
while(1);
}
void nrk_start_high_ready_task() __naked
{
_asm
mov dptr,#_nrk_high_ready_TCB
movx a,@dptr
mov b,a
inc dptr
movx a,@dptr
mov dph,a
mov a,b
mov dpl,a
movx a, @dptr
mov _spx,a
inc dptr
movx a, @dptr
mov __XPAGE,a
mov a, _spx
mov r0,a
subb a,#0xD
mov _spx,a
;Get the tasks PC and put it on the local stack
dec r0
movx a,@r0
push acc
dec r0
movx a,@r0
push acc
dec r0
movx a,@r0
mov psw,a
dec r0
movx a,@r0
mov r1,a
dec r0
movx a,@r0
;get r0 and put it on the local stack
push acc
dec r0
movx a,@r0
mov r7,a
dec r0
movx a,@r0
mov r6,a
dec r0
movx a,@r0
mov r5,a
dec r0
movx a,@r0
mov r4,a
dec r0
movx a,@r0
mov r3,a
dec r0
movx a,@r0
mov r2,a
dec r0
movx a,@r0
mov dph,a
dec r0
movx a,@r0
mov dpl,a
dec r0
movx a,@r0
mov b,a
dec r0
movx a,@r0
pop ar0
reti
_endasm;
}
int8_t nrk_TCB_init (nrk_task_type *Task, NRK_STK *ptos, NRK_STK *pbos, uint16_t stk_size, void *pext, uint16_t opt)
{
// Already in critical section so no needenter critical section
if(Task->Type!=IDLE_TASK)
Task->task_ID=nrk_task_init_cnt;
else Task->task_ID=NRK_IDLE_TASK_ID;
if(nrk_task_init_cnt>=NRK_MAX_TASKS) nrk_kernel_error_add(NRK_EXTRA_TASK,0);
if(Task->Type!=IDLE_TASK) nrk_task_init_cnt++;
if(nrk_task_init_cnt==NRK_IDLE_TASK_ID) nrk_task_init_cnt++;
//initialize member of TCB structure
nrk_task_TCB[Task->task_ID].OSTaskStkPtr = ptos;
nrk_task_TCB[Task->task_ID].task_prio = Task->prio;
nrk_task_TCB[Task->task_ID].task_state = SUSPENDED;
nrk_task_TCB[Task->task_ID].task_ID = Task->task_ID;
nrk_task_TCB[Task->task_ID].suspend_flag = 0;
nrk_task_TCB[Task->task_ID].period= _nrk_time_to_ticks( (void *)Task->period ); // sdcc can not do cast
nrk_task_TCB[Task->task_ID].next_wakeup= _nrk_time_to_ticks( (void*)Task->offset); // sdcc can not do cast
nrk_task_TCB[Task->task_ID].next_period= nrk_task_TCB[Task->task_ID].period+nrk_task_TCB[Task->task_ID].next_wakeup;
nrk_task_TCB[Task->task_ID].cpu_reserve= _nrk_time_to_ticks((void *)Task->cpu_reserve); // sdcc can not do cast
nrk_task_TCB[Task->task_ID].cpu_remaining = nrk_task_TCB[Task->task_ID].cpu_reserve;
nrk_task_TCB[Task->task_ID].num_periods = 1;
nrk_task_TCB[Task->task_ID].OSTCBStkBottom = pbos;
nrk_task_TCB[Task->task_ID].errno= NRK_OK;
return NRK_OK;
}
/*
* _nrk_timer_tick()
*
* This function is called by the interrupt timer0.
* It calls the scheduler.
*/
void _nrk_timer_tick(void)
{
// want to do something before the scheduler gets called?
// Go ahead and put it here...
_nrk_scheduler();
return;
}
uint16_t nrk_version (void)
{
return (NRK_VERSION);
}
___________________________________________________________