The Cint language

Cint accepts most of C’s expression and statement syntax, but it is an interpreter with its own rules, not a C compiler. Programs that compile with gcc often need small changes. This page lists what Cint accepts on this build, what it does not, and how to print values. How to run programs is in Cint: the C interpreter.

Program structure

A Cint file is a sequence of top-level statements: declarations, function definitions and ordinary statements such as calls. They run in order as the file is read, so a function must be defined before the statement that calls it. There is no main; call your function explicitly at the end:

cint_reset();

int show_unit(int unit)
{
    bcm_info_t info;
    int rv;

    rv = bcm_info_get(unit, &info);
    if (rv != BCM_E_NONE) {
        printf("bcm_info_get: %s\n", bcm_errmsg(rv));
        return rv;
    }
    print info;
    return 0;
}

show_unit(0);
printf("DONE\n");

Comments are /* … */ (which may nest) and // ….

Types and declarations

  • C types: char, short, int, long, long long, their unsigned forms, and double.

  • SDK integer types: uint8, uint16, uint32, int8, int16, int32 and their _t forms, and every SDK typedef such as bcm_port_t, bcm_vlan_t, bcm_if_t or bcm_gport_t.

  • uint64, the SDK’s 64-bit counter type. It is not an ordinary integer: use the COMPILER_64_* helpers or $name printing (below).

  • SDK value types with their own printed form: bcm_mac_t (MAC address), bcm_ip_t (IPv4 address), bcm_ip6_t (IPv6 address), bcm_pbmp_t (port bitmap).

  • Every SDK structure (bcm_port_info_t, bcm_l3_route_t …) and your own struct definitions. After struct pair { int key; int value; }; the tag pair is itself a type name: declare variables as pair p;. Writing struct pair p; is a syntax error (unexpected TYPE_NAME, expecting IDENTIFIER or '{').

  • Arrays (with initialisers such as int a[4] = { 1, 2, 3, 4 };), pointers, and strings (char *s = "text";).

A declaration is a statement and may appear anywhere in a block, but not inside the parentheses of a for. Declare the loop variable first:

int i;
for (i = 0; i < 4; i++) {
    printf("%d\n", i);
}

Initialise SDK structures with their _init function (bcm_l3_egress_t_init(&egr), bcm_port_config_t_init(&cfg)) before use, as in C.

What is not supported

C feature

In Cint

Casts such as (int) x or (uint32) v

Syntax error. Assign to a variable of the wanted type instead (int n = v;). Only (void *) is accepted.

#define, #if, #ifdef

Not supported; #include is the only directive. The SDK’s BCM_* constants are already defined; use variables or enum for your own.

Declarations in for ( … )

Syntax error; declare the variable before the loop.

struct tag var; for a structure defined in Cint

Syntax error; write tag var;.

goto and labels, bit-fields, K&R function definitions

Not supported.

extern variables

Rejected with 'extern' variables are not supported. An extern function prototype is accepted but cannot make an unregistered function callable.

The C library (atoi, strcpy, malloc …)

Not available. Use the sal_* functions (sal_strcmp, sal_strlen, sal_memset, sal_alloc …) or write the code in Cint.

printf("%d", <enum value>)

Rejected; see Printing.

A cast is reported at the cast, and the rest of the file does not run; the line NEG_CAST after in this program is never printed:

Hardware-validated

drivshell> cint /root/negative_cast.c
NEG_CAST before
nc_y = (int) nc_x;
        ^^^ syntax error, unexpected INT [/root/negative_cast.c:12]
nc_y = (int) nc_x;
           ^ syntax error, unexpected ')' [/root/negative_cast.c:12]
** warning: statement with no effect

Functions

Define functions as in C, with a return type and typed parameters. A function may take at most 16 parameters. Parameters behave as in C: pass &var where the SDK expects a pointer for an output value.

Calls are checked against the registered prototype: the number of arguments and each argument’s type must match, or the call fails with wrong number of parameters or arg <n> … was wrong type. Use cint list function <name>; to see a prototype.

Some SDK macros are available as functions: BCM_FAILURE(rv), BCM_SUCCESS(rv), BCM_PBMP_MEMBER(&pbm, port), BCM_PBMP_PORT_ADD(pbm, port), BCM_PBMP_COUNT(pbm, count), BCM_GPORT_IS_TRUNK(gport), BCM_GPORT_LOCAL_GET(gport), BCM_L3_ITF_VAL_GET(intf) and others. BCM_IF_ERROR_RETURN(expr) returns from the current function when expr is negative.

Callbacks

SDK functions that take a callback, such as the *_traverse functions, accept the name of a Cint function. The Cint function must have exactly the callback’s prototype; cint list fp <name>; shows it. For example, the route-table walk:

int route_cb(int unit, int index, bcm_l3_route_t *info, void *user_data)
{
    bcm_ip_t net = info->l3a_subnet;
    printf("%d: ${net} intf %d\n", index, info->l3a_intf);
    return BCM_E_NONE;
}

bcm_l3_route_traverse(0, 0, 0, 19, route_cb, NULL);

Since Cint has no casts, user_data cannot be turned back into a typed pointer. Pass NULL and keep counters or results in global variables. While the callback runs, the SDK holds the lock of the table being walked, so syncd cannot update that table until the walk finishes. Keep callbacks short and limit how much they print.

Iterators

BCM_PBMP_ITER(pbm, port) { … } runs the block once for every port in a port bitmap, as in C:

bcm_port_config_t cfg;
bcm_pbmp_t pbm;
bcm_port_t port;

bcm_port_config_t_init(&cfg);
bcm_port_config_get(0, &cfg);
pbm = cfg.port;
BCM_PBMP_ITER(pbm, port) {
    printf("port %d\n", port);
}

BCM_RX_REASON_ITER and BCM_PKT_DROP_REASON_ITER work the same way for reason sets.

Control flow

if/else, while, do … while, for, switch/case/ default, break, continue, return and the ?: operator work as in C. Arithmetic, comparison, logical, bitwise and assignment operators (+=, <<= …) are supported, as are &, *, ., -> and array subscripts. sizeof takes a variable or expression, such as sizeof(info); sizeof of a type name, such as sizeof(int), fails at run time with invalid operand(s) to 'sizeof'. Cint checks pointers and array bounds when they are used and reports attempt to dereference NULL pointer or array subscript out of range instead of crashing. Pointers returned by the SDK are not checked beyond NULL.

Self-check

language_tour.c tries the features on this page one by one and prints LANG <name> ok or LANG <name> FAIL for each, followed by DONE language_tour. On Trident 3 its sizeof(int) check fails, and the run also shows what a run-time error does: the rest of lt_main() is skipped (no further LANG lines), while the top-level statements after the call still run:

language_tour.c
/*
 * language_tour.c - exercise the parts of the C subset described in the
 * Cint language page.  Read-only: calls no SDK function that changes state.
 * Run:  cint /root/language_tour.c
 * Every check prints "LANG <name> ok" or "LANG <name> FAIL".
 */
cint_reset();

struct lt_pair {
    int key;
    int value;
};

int lt_check(char *name, int ok)
{
    if (ok) {
        printf("LANG %s ok\n", name);
    } else {
        printf("LANG %s FAIL\n", name);
    }
    return ok;
}

int lt_square(int x)
{
    return x * x;
}

int lt_sum(int *values, int count)
{
    int i;
    int total = 0;

    for (i = 0; i < count; i++) {
        total += values[i];
    }
    return total;
}

void lt_set(int *p, int v)
{
    *p = v;
}

int lt_main()
{
    int a[4] = { 1, 2, 3, 4 };
    int n = 0;
    int i;
    int x = 0;
    int kind;
    char buf[64];
    char *s = "text";
    lt_pair pair;
    uint32 big = 0x80000000;
    uint64 wide;
    bcm_switch_control_t ctl;

    lt_check("function-call", lt_square(7) == 49);
    lt_check("array-arg", lt_sum(a, 4) == 10);
    lt_set(&x, 42);
    lt_check("pointer-out", x == 42);

    pair.key = 1;
    pair.value = 2;
    lt_check("struct", pair.key + pair.value == 3);
    print pair;

    i = 0;
    while (i < 3) {
        i++;
    }
    do {
        n++;
    } while (n < 2);
    lt_check("while-do", i == 3 && n == 2);

    switch (i) {
    case 3:
        kind = 1;
        break;
    default:
        kind = 0;
        break;
    }
    lt_check("switch", kind == 1);
    lt_check("ternary", (i > 2 ? 5 : 6) == 5);
    lt_check("sizeof", sizeof(int) == 4);
    lt_check("hex-uint32", (big >> 31) == 1);

    sprintf(buf, "%s-%d", s, 7);
    lt_check("sprintf", sal_strcmp(buf, "text-7") == 0);

    ctl = bcmSwitchL3EgressMode;
    kind = ctl;
    printf("enum via int: %d\n", kind);
    printf("enum via $: ${ctl}\n");

    COMPILER_64_SET(&wide, 1, 2);
    printf("uint64 via $: ${wide}\n");
    printf("uint64 halves: hi %u lo %u\n", COMPILER_64_HI(&wide), COMPILER_64_LO(&wide));

    lt_check("defined-bcm_port_info_get", cint_defined("bcm_port_info_get") == 1);
    printf("cint_defined(soc_mem_search) = %d\n", cint_defined("soc_mem_search"));
    printf("cint_defined(soc_reg_t) = %d\n", cint_defined("soc_reg_t"));
    return 0;
}

lt_main();
print bcm_info_t;
printf("DONE language_tour\n");

Hardware-validated

drivshell> cint /root/language_tour.c
LANG function-call ok
LANG array-arg ok
LANG pointer-out ok
LANG struct ok
lt_pair pair = {
    int key = 1 (0x1)
    int value = 2 (0x2)
}
LANG while-do ok
LANG switch ok
LANG ternary ok
** error: invalid operand(s) to 'sizeof'
** error: invalid operand(s) to '=='
** error: error evaluating argument 2 to function 'lt_check'
struct bcm_info_t {
    uint32 vendor;
    uint32 device;
    uint32 revision;
    uint32 capability;
    int num_pipes;
    int num_pp_pipes;
    int num_buffers;
    int num_instances_per_pp_pipe;
    uint32 package_rev_id;
    uint32 interposer_rev_id;
    int dies_num;
    uint32[16] die_dev_id;
    uint32[16] die_rev_id;
}
size is 172 bytes
DONE language_tour

Enums and constants

SDK enum values (bcmSwitchL3EgressMode, snmpIfHCInOctets, BCM_E_NOT_FOUND) and constants (BCM_L3_IP6, BCM_L2_STATIC) are known by name and can be used in expressions, comparisons and as arguments. Enum numbers differ between SDK releases; always use the name.

An enum value is not an int for printf. To print its number, assign it to an int first:

int n;
n = bcmSwitchL3EgressMode;
printf("bcmSwitchL3EgressMode = %d\n", n);

print bcm_switch_control_t; lists every name and number of an enum.

Passing the enum directly is a run-time error; the statement prints nothing more, and the program continues:

Hardware-validated

drivshell> cint /root/negative_enum_printf.c
NEG_ENUM before
NEG_ENUM direct ** error: printf expected integer type but received bcm_switch_control_t

NEG_ENUM via int 230
DONE negative_enum_printf

Printing

print <expression>;

Prints the type, a name and the value. A variable prints under its own name, an expression as $$. Structures print every member, nested, with integers in decimal and hexadecimal. print "text"; prints the text and a newline. print <type>; prints a structure’s layout or an enum’s values.

printf("format", …)

C-style formatting. The format must be a string literal. %d %i %u %x %X %o %c need an integer argument, %s a string or char array, %f %e %g a double, %p a pointer. Width, precision, - and l/ll work as in C.

$name or ${name} inside a printf format

Prints the variable name in its natural form: an enum as its name, a bcm_ip_t as 10.0.0.1, a bcm_mac_t as 00:1A:2B:3C:4D:5E (upper-case hexadecimal), a bcm_pbmp_t as a list of ports, a uint64 as {0x<high> 0x<low>}. A plain $name ends at a space, :, ., (, ), ' or end of line; use ${name} otherwise. Because of this, a printf format cannot contain a literal $ followed by text.

sprintf(buf, "format", …), snprintf(buf, size, "format", …)

Format into a char array.

The cint reference shows print of a bcm_info_t captured on an older SONiC build; Cint recipes shows the same structure on this build, which has more members, including arrays: an array member prints as unsigned int[16] die_dev_id = { … } with one line per element.

Broadcom’s Trident 3 examples (OpenBCM sdk-6.5.27 CINT interpreter sources (src/appl/cint, src/appl/diag/cint) and Trident 3 CINT examples with logs (src/examples/xgs/TD3)) print the result of a function call with print; a function declared to return bcm_error_t shows its value by name. The last lines of one such run:

Unvalidated

BCM.0> init all;cint lsr_php.c
...
execute(): Start
...
bcm_error_t $$ = BCM_E_NONE (0)
execute(): End

64-bit counters

bcm_stat_get and similar functions return uint64 values. Print them with $name (hexadecimal high and low words) or split them:

uint64 v;
bcm_stat_get(0, 1, snmpIfHCInOctets, &v);
printf("in octets ${v}  low word %u\n", COMPILER_64_LO(&v));

COMPILER_64_HI(&v) and COMPILER_64_LO(&v) return the two 32-bit halves; COMPILER_64_SET, COMPILER_64_ADD_64, COMPILER_64_SUB_64 and COMPILER_64_IS_ZERO do arithmetic.

Return codes

SDK functions return an int: BCM_E_NONE (0) for success, a negative BCM_E_… code for failure. bcm_errmsg(rv) returns the text. Compare against the names, not the numbers.

Code

Value

bcm_errmsg text

BCM_E_NONE

0

Ok

BCM_E_INTERNAL

−1

Internal error

BCM_E_MEMORY

−2

Out of memory

BCM_E_UNIT

−3

Invalid unit

BCM_E_PARAM

−4

Invalid parameter

BCM_E_EMPTY

−5

Table empty

BCM_E_FULL

−6

Table full

BCM_E_NOT_FOUND

−7

Entry not found

BCM_E_EXISTS

−8

Entry exists

BCM_E_TIMEOUT

−9

Operation timed out

BCM_E_BUSY

−10

Operation still running

BCM_E_FAIL

−11

Operation failed

BCM_E_DISABLED

−12

Operation disabled

BCM_E_BADID

−13

Invalid identifier

BCM_E_RESOURCE

−14

No resources for operation

BCM_E_CONFIG

−15

Invalid configuration

BCM_E_UNAVAIL

−16

Feature unavailable

BCM_E_INIT

−17

Feature not initialized

BCM_E_PORT

−18

Invalid port

BCM_E_UNAVAIL usually means the API is not supported on Trident 3, not that something is broken.