Ponde SC Reference

Summary of the "SC" language

1. Introduction:

SC is a high-level language specifically designed for the scc-32 embedded microsequencer family. Although the language is somewhat tied to a specific hardware architecture family, most of SC grammar are generic. In fact, anybody who is familiar with C probably can easily understand a program written in SC.

2. Program Structure:

A 'main' function and any number of other functions may be in a file. A function can return a word value or can be a 'void' function. Files are compiled with the SC compiler that can produce a compiled assembly file, which can be assembled to a ROM image using an architecture specific assembler. As data structures used in a SC program are declared outside SC program, a SC program usually contain at least one inline assembly code block. When the control reaches at the end of the 'main' function, the control stalls such that it won't overrun another part of the program. This is a specific behavior of 'main' function that is declared as proc main() and the control will be returned to the caller of the function if the function is not a 'main' function.
Like "C" language, you can use both global variables and local ('auto') variables. You can only declare 'ptr' variables as global variables. The global variables in a SC program are not initialized to any known value while global variables of "C" programs are initialized to 0.

2.1 Comment:

Like C++, both /* comment_string */ and // comment_string are supported. /* comment_string */ cannot be nested.

2.2 Data type:

Unlike other high-level languages, such as "C" language, SC has only two data types, 'word' and 'ptr'.

The 'word' data type represents a data which data size is native to the target architecture. For example, the 'word' variable of the scc-32 is a 32 bit signed variable. All arithmetic operations in SC language are signed operations.
The 'ptr' data type represents an address which is used to point a data in the program memory. The 'ptr' is commonly used to access declared data structure in the program, such as a constant table. A 'word' data type can be casted to a 'ptr' data type and vice versa.
Array of 'word' data type is supported by an array operator ('[idx]') , however, the programmer is responsible to allocate the memory for such array.

2.3 A typical file structure:

#include "my_project.h"

asm {


main()                     /* main function and other functions  */
{                          /* in the same file                       */

function_1( i, j, k )      /* all parameters are word type */
                      /* function definitions can NOT be nested */
  /* no return needed at end of function when the return type is 'void' */

function_2( )
  return exp;
}                              /* as many functions as desired       */

2.4 A sample SC program:

#include "my_project.h"

    /*     inline assembly code                                              */

asm {
    jmp     _main


    .db     "0123456789abcdef"


    .db     "Hello, world\r\n\x00"


    /*     function prototype Declaration                                    */

proc main();

void printf(fmt, ...);
void outpSCI(d8);

    /*     Data (ptr) Declaration                                            */

ptr hextbl;
ptr hello;

    /*     Global variable Declaration                                       */

    /*     Program body                                                      *


printf(fmt, d1, d2, d3)     // max three arguments

3. Statements:

3.1 Variable declarations

Variables of the supported two data types, 'word' and 'ptr', must be declared.

  word xyz;            /* signed variable; size is architecture specific  */
  word ix (r16);       /* pre-assign the register mapping; this is last resort */
  ptr hextbl;

3.2 Convenient 'ptr' declaration

A special form of 'ptr' declaration that declares both a ptr variable and the data structure that is pointed by the ptr variable is newly introduced for convenience. If a string is declared, a null ('\x00') is automatically appended. In the data array format (the second example), each data entry is 'word' data type.

  // scc-32
  ptr msg = { "Hello, world!" };  /* a convenient form */
  ptr tbl = { 0xaa55ff00, 0xdeadbeef, ('a' << 24) | ('b' << 16) | ('c' << 8) | 'd'), -1 };   /* an array */

  x = tbl[0];    // x <- 0xaa55ff00

3.3 struct declaration (with initialization)

Struct declarations with initialization is very similar to ptr declaration with initialization. But each element in a data structure can be referenced by an element name rather than an index to an array. Each element of a data structure must be a word and cannot be other size or an array. The initialization list depth must match the size of the data structure. Elements cannot be left uninitialized. An array of a data structure can be declared but the depth of the initializer list must agree with the aggregated data size (structure size * array size).

  struct _pkt_services {
    word type;
    word func;
  } pkt_services[5] = { \
    (IP_PROTO_ICMP << 16) | ETHERTYPE_IP, (word)service_ip_icmp, \
    (IP_PROTO_TCP << 16) | ETHERTYPE_IP, (word)service_ip_tcp, \
    (IP_PROTO_UDP << 16) | ETHERTYPE_IP, (word)service_ip_udp, \
    ETHERTYPE_ARP, (word)service_arp, \
    ETHERTYOE_WOL, (word)service_wol };

The above data structure is translated into the following assembly code.
    .dd    67584
    .dd    _service_ip_icmp
    .dd    395264
    .dd    _service_ip_tcp
    .dd    1116160
    .dd    _service_ip_udp
    .dd    2054
    .dd    _service_arp
    .dd    2114
    .dd    _service_wol

3.4 struct declaration (without initialization)

Struct declarations without initialization is provided to declare a data structure in a volatile memory. The declared data stricture is placed in a BSS segment, which has a separate address pointer. This type of data structure cannot have an initialization list for the data structure is destined for a volatile memory. Each element of a data structure must be a word and cannot be other size or an array.

  struct _malloc_struct {
    word mptr;
    word freed;
    word allocated;
    } malloc_struct;

  struct _timer {
    word tm2trip;
    word arg;
    word cb_func;
    } timer[N_TMR];

The above volatile data structure is translated into the following assembly code.
   .ds    4
   .ds    4
   .ds    4
   .ds    4*3
   .ds    4*3
   .ds    4*3

3.5 byte struct declaration

Byte struct declarations are used to provide byte offsets of a data structure. Unlike other struct declaration, byte struct declaration won't translate into any code or data storage. A byte/hword/word or an array of byte/hword/word is permitted as an element. Each byte offset from the data structure top can be obtained using indexof operator and each element size can be obtained using sizeof operator. The size of word with scc-16 is 2 while the size of word is 4 with scc-32.

  // this file is to define byte offset to each field
  byte struct _mac_header {
    byte SFD[8];
    byte DA[6];
    byte SA[6];
    byte EtherType[2];

The following is an example in which a byte offset from a byte structure is used to access the EtherType of an Ethernet packet.
    memptr = rxbase+indexof(_mac_header.EtherType);

3.6 Function prototype declarations

Function prototypes of functions used in a SC program must be declared with function arguments and return data type.

  proc main();             /* will stall at the end */
  void printf(fmt, ...);   /* variable number of arguments */
  word func(a);            /* return a data */

3.7 Executable statements:

Statements end with a semicolon or a brace. A compound statement may be made from a sequence of statements by enclosing them in braces.

    { statement_1 ; statement_2 ; statement_3 ; }

is a statement but usually
    {              /* make code readable, one statement per line. */

3.8 Assignment statements:

  x = 10;
  x = x+1;
  x = x+20;
  x += 20;            /* same as x = x+20; add */
  x -= 20;            /* same as x = x-20; subtract */
  x *= 20;            /* same as x = x*20; multiply */
  x /= 20;            /* same as x = x/20; divide */
  x %= 20;            /* same as x = x%20; modulo */
  x <<= 2;            /* same as x = x<<2; shift left */
  x >>= 2;            /* same as x = x>>2; shift right */
  x >>>= 2;           /* same as x = x>>>2; shift right */
  x &= 20;            /* same as x = x&20; bit by bit and*/
  x ^= 20;            /* same as x = x^20; bit by bit exclusive or */
  x |= 20;            /* same as x = x|20; bit by bit or */
  x = who ? 3 : y;    /* same as if(who)x=3; else x=y; */
  y = func(x) ;       /* function call */
  x = mat[i] ;        /* subscripting one dimensional array  */

3.9 (non-void)Function call with returned value ignored:

  go_do_it();         /* actual parameters are optional              */
                      /* warning may be generated                    */

3.10 Unconditional branches:

  break;              /* immediate exit from loop or switch          */

  continue;           /* immediate jump to end of loop ( not exit )  */
                      /* also used to exit a switch in a loop        */

  return;             /* immediate exit from function, no value      */
  return exp;         /* exit from function returning value of exp   */

  goto label;         /* unconditional jump                          */

label: statement;     /* label definition                            */

3.11 Conditional branching:

  if ( condition ) statement ;  /* basic form of if statement */

  if ( condition ) statement ;
  else statement_2 ;            /* optional  else  clause */

  if ( condition_1 )
     if ( condition_2 ) statement ;
     else statement_2 ;         /* else  belongs to closest previous if */

  if ( expression )           /* condition may be any expression        */
  {                           /* zero value is false, non zero is true  */
     statement_sequence       /* { } may enclose sequence of statements */
  {                           /* it is good to use braces, safer for    */
     statement_sequence       /* updates to code later.                 */

  if-else-if ladder structure (must be space in  'else if')

  if(exp_1)             /*  the first expression that is true, e.g. exp_i */
       statement_1;     /*  causes statement_i to be executed             */
  else if(exp_2)        /*  then jump to end after statement_n            */
  else if(exp_3)
       statement_n;     /*  executed if no exp_i was true, else skipped   */

3.12 Iteration statements:

  while ( condition ) statement ; /* keep repeating statement until the */
                                  /* condition is false, zero. This is */
                                  /* classical C thinking, rather than saying*/
                                  /* keep repeating while condition is true*/

  while(1) statement;             /* infinite loop                        */
                                  /* and must have something else to      */
                                  /* terminate the loop                   */

  while ( condition ) statement_1 else statement_2 ;
                                  /* statement_2 is executed if condition is no longer true */
                                  /* statement_2 is not executed if the control exits from  */
                                  /* the loop because of a break statement                  */

  in all iteration statements,  break; causes an immediate exit from loop
                                continue; immediately proceeds to next iteration

3.13 Debug statements:

  print x;                        /* simulation only */
  halt;                           /* simulation only */

4. Function definition:

4.1 Function Prototype:

type function_name(var_1, var_2); /* fixed number of arguments */

type function_name(var_1, var_2, ...); /* variable number of arguments */
// var_3, var_4, ... may be omitted

'type' can be either 'void' or 'word'

4.2 Function Definition:

type function_name(var_1, var_2, var_3, ...)
{ function_body }

4.3 example of a function call passing a ptr type variable

asm {
.db "0123456789abcdef"

func((word)hextbl, ix); /* call, passing address of hextbl */

5. Reserved words

Like everything else, must be lower case.

term Description
asm used to insert assembly language source code
break used to exit loop and used to exit switch
continue go to bottom of loop in for, while and do loops
else executable statement, part of "if" structure
goto jump within function to a label
if executable statement
inline reserved
word basic declaration of integer
return executable statement with or without a value
void declaration of a typeless variable
while executable statement, while loop or do-while loop

6. Expressions

variable names 1 to 32 characters including alphabetic, numeric and underscore
(ex.) some_name ELSE Else z999
numbers signed integer
(ex.) 1 12345 -1
binary or octal or hexadecimal
(ex.) 01777 0x248fff 0b10001
character single character between apostrophes plus special backslash codes
(ex.) 'a' 'Z' '\n'

expressions are:

unary_operator expression
expression binary_operator expression
( expression )

7. Operators:

7.1 operator precedence and associativity:

RL ! ~ - + * & (type)
LR * / %
LR + -
LR << >> >>>
LR < <= > >=
LR == !=
LR &
LR ^
LR |
LR &&
LR ||
RL ? :
RL +-= *= /= %= »= »>= «= &= ^= |=

LR means associate left to right, RL means associate right to left

7.2 operator definitions:

( ) grouping parenthesis, function call
[ ] array indexing (accessing by 'word')
! relational not, complement, ! a yields true or false
~ bitwise not, ones complement, ~ a
- unary minus, - a
+ unary plus, + a
* indirect, the value of a pointer, * p is value at pointer p address
& the memory address, & b is the memory address of variable b
(type) a cast, explicit type conversion, (float) i, (*fun)(a,b), (int*)x
* multiply, a * b
/ divide, a / b
% modulo, a % b
+ add, a + b
- subtract, a - b
<< shift left, left operand is shifted left by right operand bits
>> shift right, left operand is shifted right by right operand bits
>>> shift right, left operand is shifted right by right operand bits
(sign preserved)
< less than, result is true or false, a < b
<= less than or equal, result is true or false, a <= b
> greater than, result is true or false, a > b
>= greater than or equal, result is true or false, a >= b
+++ ** equal, result is true or false, a == b
!= not equal, result is true or false, a != b
& bitwise and, a & b
^ bitwise exclusive or, a ^ b
| bitwise or, a | b
&& relational and, result is true or false, a < b && c >= d
@@||@@ relational or, result is true or false, a < b @@||@@ c >= d
? exp1 ? exp2 : exp3 result is exp2 if exp1 != 0, else result is exp3
= store
+= add and store
-= subtract and store
*= multiply and store
/= divide and store
%= modulo and store
<<= shift left and store
>>= shift right and store
>>>= shift right and store (sign preserved)
&= bitwise and and store
^= bitwise exclusive or and store
|= bitwise or and store

zero evaluates to false ( a null pointer has a zero value )
non zero evaluates to true


8.1 integer constants

An integer constant can be a decimal constant (base=10) or an octal constant (base=8) or a hexadecimal constant (base=16) or a binary constant (base=2).


8.2 character constants (with backslash)

These can be used as characters when enclosed in apostrophies

\a alert, audible alarm, bell, ^G
\b Backspace, ^H
\f Form feed, new page, ^L
\n New line, line feed, no carriage return, ^J
\r Carriage return, no line feed, ^M
\t Horizontal tab, tab, ^I
\v Vertical tab, ^K
\" Quote character
\' Apostrophe character
\\ Backslash character
\? Question mark character
\xhh hexadecimal number

8.3 Boolean constant

C++ style Boolean constants are supported.


9. SC language extensions:

9.1 The list of SC language extensions:

wait(n) sleep for n timer counts
eval_cond(n) test tstin[n] line
outp(port, exp) output to the port
inp(exp) input from the port
pulse(n) output a pulse to pulse[n] line
halt enter 'doze'
asm { … } inline assembly code
rot8(exp) swap upper half and lower half of the data (scc-16)
rot16(exp) swap upper half and lower half of the data (scc-32)
rol8(exp) rotate a byte to the left (scc-32)
ror8(exp) rotate a byte to the right (scc-32)
byte(exp) extract the most significant byte
scc-32 b31…b24
scc-16 b15…b8
hword(exp) extract the most significant half word
scc-32 b31…b16
__ei__() enable interrupt
__di__() disable interrupt
__nop__() insert a NOP (timing adjustment)
__push__(exp) leave the exp at the stack top
__dup__(exp) __push__(exp); __dup__(); __pop__()
__dup__() copy the stack top
__pop__() use the stack top
__discard__() discard the stack top
__discard__(exp) __push__(exp); __discard__()
repeat(n) { … } SC version of repeat; 'n' is stored in the data stack
not generic like while(cond) { … }
'n' must be a positive value
__def__(args) User definable macros
print only valid in simulation

9.2 ptr in action:

'ptr' is an object which holds an address of the instruction ROM.

asm {
.db "Hello, world\r\n"
.db 00h

ptr _hello; /* this is how to declare */
word x;

x = _hello[0]; /* x = { 'H', 'e', 'l', 'l' } if word is a 32 bit */
x = _hello[1]; /* x = { 'o', ',', ' ', 'w' } for a 32 bit */
x = (ptr)((word)_hello+1)[0];
/* x = { 'e', 'l', 'l', 'o' } as _hello+1 is the starting address */
x = (ptr)x[2]; /* 'word' variable can be casted to 'ptr' */
func((word)_hello); /* functions can only take 'word' arguments */

9.3 memory access operator ('*') in action:

SC has a memory access operator ('*') to access a memory system. As the scc-32 microsequencer doesn't have a default memory system (all variables are stored in the register file), the SC compiler provides a means to bind a user specific memory system to the memory access operator ('*'). By placing an access macro definition file (scc32.def for scc-32), the memory access operator will be compiled to the operations defined in the macro definition file. The default binding is 'ldi' for memory read macro (MLDA), 'sti' for memory write macro (MSTA). (reading from / writing to the instruction ROM) If an access macro definition file is read, a '_SCC32_DEF' macro is defined (for scc-32) such that a SC program can test if such file is read.

#ifdef _SCC32_DEF
#error This program requires "scc32.def"

word x;

x = *(MEM_BASE+0x10);
*(0x200) = x + 1;  //*(0x200) += 1; is not allowed//
//if(*(MEM_BASE+2) == 0xaa55ff00) { ...//

9.4 timer in SC

Controlling the scc-32 timer is integrated into the SC language. However, there are several things you need to pay attention due to the hardware.

//wait(10);//  doze for 10 timer ticks
//1 tick = sysFs (scc-32)//

//wait(-1);//  reset the timer without setting the TMR flag
//this is used to cancel a time out timer//

//wait(0);//  reset the timer and set the TMR flag
//the TMR flag is set within the next timer tick//

//wait(-2);//  set the timer to the maximum delay

The scc-32 microsequencer has a 16 bit timer. But those figures may vary architecture to architecture. The sysFs clock is provided from the outside the core such that it depends upon each design.

10. User definable Macros:

SC Compiler allows users to define a group of assembly code as a macro and can be used in their SC program like operators. Because calling macros won't involve function call overhead, the sequence defined in a macro is executed efficiently. Use of user definable macros is optional.
Unlike macros available in assembly languages, the SC User definable macros behave like operators. It returns a data and can take arguments. There are four user definable macros.

__def__(arg1, arg2)
__def__(arg1, arg2, arg3)

All of them have the same name, def, but they are defined separately. SC Compiler maps the correct macro definition depending upon the number of arguments. The macro definitions are defined in the macro definition file (scc32.def for example).

in SC program macro name
__def__(void) umacro
__def__(arg1) umacro_1
__def__(arg1, arg2) umacro_2
__def__(arg1, arg2, arg3) umacro_3

The given arguments are placed in the data stack and the top of the stack is the first argument in the list. The return value must be placed in the data stack. If the macro doesn't need to return a value, you still need to place something in the stack and then discard it. For such cases, alternatively you can use asm { … } to place a set of assembly code in a SC program.

in a macro definition file:

... # any sequence of assembly code
dup # place something

in your SC program:

__discard__(__def__()) // discard the return value

10.1 A simple example:

The following is an example that a defined macro does "+1".

in a macro definition file:


in your SC program:

#define inc(x) __def__(x)

word x, y;

y = inc(x);


11. Preprocessor:

11.1 Preprocessor directives:

#include "mine.h" include a file
#define TRUE 1 macro substitution, usually use capitals
#define min(a,b) (a<b)?(a):(b) macro substitution with parameters
#define abs(a) (a<0)?(-(a)):(a) macro substitution
#define note /* comment */ this comment gets inserted every time note appears */
backslash \ at end of a line means continue
#undef TRUE undefines a previously defined macroname
#error stop compiling at this point
#else else compile following code
#endif end of conditional compiling
#ifdef macroname compiles if macroname is defined
#ifndef macroname compiles if macroname is not defined
__DATE__ A string containing the date of compilation
"Mmm dd yyyy"
__TIME__ A string containing the time of compilation
__FILE__ A string containing name of the current input file
__LINE__ A decimal integer of the current input line number

11.2 Predefined macros:

The SC compiler defines the following macros if the specified conditions are met:

macro condition
_SCC32_DEF a macro definition file is read.
(ex. 'scc32.def')
RTL_SCC32_JTAGDBG An option is turned on in the core configuration file.
This case JTAGDBG option. The SC compiler lists which macros are defined.

(c) 2003-2014 Ponderosa Design

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License