TMS320C28x Assembly Language
Tools
v17.6.0.STS User's Guide
SPRU513N - REVISED JUNE 2017
A CRC Reference Implementation
This appendix contains source code in C for a reference implementation of a CRC calculation routine that is compatible with the linker-generated CRC tables. This code is found in the file labeled ref_crc.c.
This appendix also contains source code for a simple example application using linker-generated CRC tables and copy tables. The application contains several tasks which share a common run area. Linker-generated copy tables move the tasks from their load addresses to the run address. The application also uses the reference CRC calculation routine to compute CRC values which are compared against the linker-generated values.
This code is for reference only, and no warranty is made as to suitability for any purpose.
A.1 Compilation Instructions
- Run a stand-alone test of the reference implementation of CRC computation.
- For C2000 on Linux:
- CRC-32-PRIME: 4beab53b
- CRC-8-PRIME: 70
- CRC16_802_15_4: 1bd3
- For GCC on Linux:
- CRC-32-PRIME: 4beab53b
- CRC-8-PRIME: 70
- CRC16_802_15_4: 1bd3
cl2000 -D=_RUN_MAIN ref_crc.c -z -o ref_crc_c2000 -l lnk2800_ml.cmd <Linking>
Run ref_crc_c2000 with an appropriate simulator:
gcc -D_RUN_MAIN -o ref_crc_gcc ref_crc.c ./ref_crc_gcc
Run ref_crc_gcc with an appropriate simulator:
- Run a simple example program using copy tables and CRC tables.
- For C2000 on Linux:
- CRC-32-PRIME: 4beab53b
- CRC-8-PRIME: 70
- CRC16_802_15_4: 1bd3
cl2000 -c *.c cl2000 -z -lex1.cmd <Linking>
Run ex1.out with an appropriate simulator:
A.2 Reference CRC Calculation Routine
Example A-1 Reference Implementation of a CRC Calculation Function: ref_crc.c
/*****************************************************************************/
/* Reference implementation of a CRC calculation function */
/* */
/* gen_crc is the interface function which should be called from the */
/* application. There is also a stand-alone test mode that can be used */
/* if _RUN_MAIN is defined. */
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/* This file does NOT implement a general-purpose CRC function. */
/* Specifically, it does not handle parameterization by initial value, bit */
/* reflection, or final XOR value. This implementation is intended only to */
/* implement the CRC funtions used by the linker for C28x CRC tables. The */
/* algorithms used by the linker are selected to match the CRC algorithms in */
/* the PRIME and IEEE 802.15.4-2006 standards, which use the polynomials */
/* supported by the C28x VCU hardware. To understand CRCs in general, */
/* especially what other parameters exist, see: */
/* */
/* "A Painless Guide To CRC Error Detection Algorithms" likely at: */
/* http://www.ross.net/crc/download/crc_v3.txt */
/* Author : Ross Williams (ross@guest.adelaide.edu.au.). */
/* Date : 3 June 1993. */
/* Status : Public domain (C code). */
/*---------------------------------------------------------------------------*/
#include <stdio.h>#include <stdlib.h>#include <limits.h>/*---------------------------------------------------------------------------*/
/* These are the CRC algorithms supported by the linker, which match the */
/* polynomials supported in C28x VCU hardware, which match the PRIME and */
/* IEEE 802.15.4-2006 standards. These must match the values in crc_tbl.h. */
/*---------------------------------------------------------------------------*/
#define CRC32_PRIME 0
#define CRC16_802_15_4 1
#define CRC16_ALT 2
#define CRC8_PRIME 3
typedef struct crc_config_t
{
int id;
int degree;
unsigned long poly;
} crc_config_t;
const crc_config_t crc_config[] = { { CRC32_PRIME, 32, 0x04c11db7 },
{ CRC16_802_15_4, 16, 0x1021 },
{ CRC16_ALT, 16, 0x8005 },
{ CRC8_PRIME, 8, 0x07 } };
unsigned long crc_table[256] = { 0 };
const crc_config_t *find_config(int id) {
size_t i;
for (i = 0; i < sizeof(crc_config) / sizeof(*crc_config); i++)
if (crc_config[i].id == id)
return &crc_config[i];
fprintf(stderr, "invalid config id %d\n", id);
exit(EXIT_FAILURE);
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Table-driven version */
/*---------------------------------------------------------------------------*/
unsigned long generate_mask(int degree)
{
unsigned long half = (1ul << (degree / 2)) - 1;
return half << (degree / 2) | half;
}
void generate_crc_table(const crc_config_t *config)
{
int i, j;
unsigned long bit, crc;
unsigned long high_bit = (1ul << (config->degree - 1));
unsigned long mask = generate_mask(config->degree);
for (i = 0; i < 256; i++)
{
crc = (unsigned long)i << config->degree - 8;
for (j = 0; j < 8; j++)
{
bit = crc & high_bit;
crc <<= 1;
if (bit) crc^= config->poly;
}
crc_table[i] = crc & mask;
}
}
/*****************************************************************************/
/* gen_crc - Return the CRC value for the data using the given CRC algorithm */
/* int id : identifies the CRC algorithm */
/* char *data : the data */
/* size_t len : the size of the data */
/*****************************************************************************/
unsigned long gen_crc(int id, const unsigned char *data, size_t len)
{
/*-----------------------------------------------------------------------*/
/* Note: this is not a general-purpose CRC function. It does not handle */
/* parameterization by initial value, bit reflection, or final XOR */
/* value. This CRC function is specialized to the CRC algorithms in the */
/* linker used for C28x CRC tables. */
/*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* This CRC function is not intended to be optimal; it is written such */
/* that it works and generates the same result on all 8-bit and 16-bit */
/* targets, including C28x, other TI DSPs, and typical desktops. */
/*-----------------------------------------------------------------------*/
const crc_config_t *config = find_config(id);
unsigned long crc = 0;
unsigned long mask = generate_mask(config->degree);
size_t i;
generate_crc_table(config);
for (i = 0; i < len; i++)
{
unsigned int datum = data[i];
/*--------------------------------------------------------------------*/
/* This loop handles 16-bit chars when we compile on 16-bit machines. */
/*--------------------------------------------------------------------*/
int n;
for (n = 0; n < (CHAR_BIT / 8); n++)
{
/*----------------------------------------------------------------*/
/* For 16-bit machines, we need to feed the octets in an */
/* arbitrary order. For C2000, the arbitrary order we choose is */
/* to feed the LEAST significant octet of char 0 first. The */
/* first octet fed to the CRC is the LEAST-significant octet of */
/* char 0; the second octet is the MOST-significant octet of char */
/* 0. See the "Special Note regarding 16-bit char" in the */
/* Assembly Language Tools User's Guide. */
/*----------------------------------------------------------------*/
#if __TMS320C28XX__
/*----------------------------------------------------------------*/
/* Using __byte is not necessary; we use it here to illustrate */
/* how it relates to octet order. */
/*----------------------------------------------------------------*/
unsigned long octet = __byte((int*)&datum, n);
#else
unsigned long octet = ((datum >> (8 * n)) & 0xff);
#endif
unsigned long term1 = (crc << 8);
int idx = ((crc >> (config->degree - 8)) & 0xff) ^ octet;
crc = term1 ^ crc_table[idx];
}
}
return crc & mask;
}
#ifdef _RUN_MAIN
/*****************************************************************************/
/* main - If requested, compute the CRC of test data using each algorithm. */
/*****************************************************************************/
int main(void)
{
#if CHAR_BIT == 16
const unsigned char data[] = { 'a', 'b', 'c', 'd' };
#elif CHAR_BIT == 8
/*-----------------------------------------------------------------------*/
/* This represents "abcd" as it would appear in C2000 memory if we view */
/* C2000 memory as octets, least-significant octet first; see "a special */
/* note regarding 16-bit char" in Assembly Language Tools User's Guide. */
/*-----------------------------------------------------------------------*/
const unsigned char data[] = { 'a', 0, 'b', 0, 'c', 0, 'd', 0 };
#endif
/* CRC_8_PRIME: 0x70 */
/* CRC_16_802: 0x1bd3 */
/* CRC_32_PRIME: 0x4beab53b */
const unsigned char *p = (const unsigned char *)data;
unsigned long crc;
crc = gen_crc(CRC32_PRIME, p, sizeof data);
printf("CRC_32_PRIME: %08lx\n", crc);
crc = gen_crc(CRC8_PRIME, p, sizeof data);
printf("CRC_8_PRIME: %02lx\n", crc);
crc = gen_crc(CRC16_802_15_4, p, sizeof data);
printf("CRC16_802_15_4: %04lx\n", crc);
return 0;
}
#endif
A.3 Linker-Generated Copy Tables and CRC Tables
Three tasks exist in separate load areas. As each is needed, it is copied into the common run area and executed. A separate copy table is generated for each task (see table() operator in ex1.cmd). CRC values for the task functions are verified as well. See Example A-7 for the crc_table() operator calls.
Example A-2 Main Routine for Example Application: main.c
#include <stdio.h>#include <cpy_tbl.h>#include <crc_tbl.h>extern COPY_TABLE task1_ctbl;
extern COPY_TABLE task2_ctbl;
extern COPY_TABLE task3_ctbl;
extern CRC_TABLE task1_crctbl;
extern CRC_TABLE union_crctbl;
/****************************************************************************/
/* copy_in - provided by the RTS library to copy code from its load */
/* address to its run address. */
/* my_check_CRC - verify that the CRC values stored in the given table */
/* match the computed value at run time, using load address. */
/* taskX - perform a simple task. These routines share the same run */
/* address. */
/****************************************************************************/
extern void copy_in(COPY_TABLE *tp);
extern unsigned int my_check_CRC(CRC_TABLE *tp);
extern void task1(void);
extern void task2(void);
extern void task3(void);
int x = 0;
main()
{
unsigned int ret_val = 0;
unsigned int CRC_ok = 1;
printf("Start task copy test with CRC checking.\n");
printf("Check CRC of task1 section.\n");
ret_val = my_check_CRC(&task1_crctbl);
if (ret_val == 1)
printf("\nPASSED: CRCs for task1_crc_tbl match.\n");
else
{
CRC_ok = 0;
printf("\nFAILED: CRCs for task1_crc_tbl do NOT match.\n");
}
/*************************************************************************/
/* Copy task1 into the run area and execute it. */
/*************************************************************************/
copy_in(&task1_ctbl);
task1();
printf("Check CRC of UNION.\n");
if ((ret_val = my_check_CRC(&union_crctbl)) == 1)
printf("\nPASSED: CRCs for union_crc_tbl match.\n");
else
{
CRC_ok = 0;
printf("\nFAILED: CRCs for union_crc_tbl do NOT match.\n");
}
copy_in(&task2_ctbl);
task2();
copy_in(&task3_ctbl);
task3();
printf("Copy table and CRC tasks %s!!\n",
((CRC_ok == 1 && x == 6)) ? "PASSED" : "FAILED");
}
Example A-3 Checking CRC Values: check_crc.c
#include <stdio.h>#include "crc_tbl.h"/****************************************************************************/
/* gen_crc() - computes the CRC value of data using the CRC algorithm ID */
/* specified. Found in ref_crc.c */
/****************************************************************************/
unsigned long gen_crc(int id, const unsigned char *data, size_t len);
/****************************************************************************/
/* my_check_CRC() - verify the CRC values for all records stored in the */
/* given CRC table. Print diagnostic information also. */
/****************************************************************************/
unsigned int my_check_CRC(CRC_TABLE *tp)
{
int i;
unsigned int ret_val = 1;
uint32_t my_crc;
printf("\n\tTABLE INFO: rec size=%d, num_rec=%d.",
tp->rec_size, tp->num_recs);
for (i = 0; i < tp->num_recs; i++)
{
CRC_RECORD crc_rec = tp->recs[i];
/**************************************************/
/* COMPUTE CRC OF DATA STARTING AT crc_rec.addr */
/* FOR crc_rec.size UNITS. USE */
/* crc_rec.crc_alg_ID to select algorithm. */
/* COMPARE COMPUTED VALUE TO crc_rec.crc_value. */
/**************************************************/
my_crc = gen_crc(crc_rec.crc_alg_ID, (unsigned char *)crc_rec.addr,
crc_rec.size);
printf("\n\tCRC record: page=%x, alg=%x, addr = %lx, size=%lx, " "\n\t\tcrc=%lx, my_crc=%lx.",
crc_rec.page_id, crc_rec.crc_alg_ID,
crc_rec.addr, crc_rec.size, crc_rec.crc_value, my_crc);
if (my_crc == crc_rec.crc_value)
printf("\n\tCRCs match for record %d.\n", i);
else
{
ret_val = 0;
printf("\n\tCRCs DO NOT match for record %d.\n", i);
}
}
return ret_val;
}
Example A-4 Task1 Routine: task1.c
#include <stdio.h>extern int x;
#pragma CODE_SECTION(task1, ".task1_scn")
void task1(void) { printf("hit task1, x is %d\n", x); x += 1; }
Example A-5 Task2 Routine: task2.c
#include <stdio.h>extern int x;
#pragma CODE_SECTION(task2, ".task2_scn")
void task2(void) { printf("hit task2, x is %d\n", x); x += 2; }
Example A-6 Task3 Routine: task3.c
#include <stdio.h>extern int x;
#pragma CODE_SECTION(task3, ".task3_scn")
void task3(void) { printf("hit task3, x is %d\n", x); x += 3; }
Example A-7 Example 1 Command File: ex1.cmd
/****************************************************************************/
/* Linker Generated Copy Tables - Example #1 */
/* */
/* 3 separate tasks are loaded into 3 separate areas of target memory. */
/* They all must be run in a common area of memory (overlay). Before */
/* each task is run, it is copied into its run space using a linker */
/* generated copy table for each task. */
/* */
/* Two linker generated CRC tables are created. One is for .task1_scn */
/* and the other is for the UNION. The UNION table will contain CRC */
/* records for .task1_scn, .task2_scn and .task3_scn. */
/****************************************************************************/
-c
-x
ex1.obj
task1.obj
task2.obj
task3.obj
check_crc.obj
ref_crc.obj
-o ex1.out
-m ex1.map
-stack 0x1000
-heap 0x800
MEMORY
{
PAGE 0 : RESET(R): origin = 0x000000, length = 0x00002
VECTORS(R) : origin = 0x000002, length = 0x003FE
PROG(R) : origin = 0x3f0000, length = 0x10000
PAGE 1 : RAM1 (RW) : origin = 0x000402 , length = 0x003FE
PAGE 1 : RAM2 (RW) : origin = 0x001000 , length = 0x04000
PAGE 1 : RAM3 (RW) : origin = 0x3e0000 , length = 0x08000
}
SECTIONS
{
UNION
{
.task2_scn: load = RAM3, PAGE = 1, table(_task2_ctbl)
.task3_scn: load = RAM3, PAGE = 1, table(_task3_ctbl)
.task1_scn: load = RAM3, PAGE = 1, table(_task1_ctbl),
crc_table(_task1_crctbl)
} run = PROG, PAGE = 0, crc_table(_union_crctbl, algorithm=CRC16_ALT)
vectors : load = VECTORS, PAGE = 0
.text : load = PROG, PAGE = 0
.data : load = 440h, PAGE = 1
.cinit : > PROG, PAGE = 0
.ebss : > RAM3, PAGE = 1
.econst : > RAM3, PAGE = 1
.reset : > RESET, PAGE = 0
.stack : > RAM2, PAGE = 1
.sysmem : > RAM2, PAGE = 1
.esysmem : > RAM3, PAGE = 1
.ovly > RAM2, PAGE = 1
}
Copyright© 2017, Texas Instruments Incorporated. An IMPORTANT NOTICE for this document addresses availability, warranty, changes, use in safety-critical applications, intellectual property matters and other important disclaimers.