C Part II · Module 20 · DPI-C Type Mapping
Mapping SystemVerilog Integer Types to C
Four types, four obvious-looking C counterparts, and three of the four hide an assumption worth knowing about. The most useful one concerns the simplest row: SystemVerilog's byte maps to C's char, and plain char's signedness is implementation-defined — measured signed here, and measured reversing on this same machine with one compiler flag. This chapter is the four rows and what each one assumes, and it ends with the spelling to actually write in your C, which is not the one the mapping table names.
Advanced20 min readCPart IIVerificationDPI-C
Part II · Module 20 · Chapter 20.3 · Mapping SystemVerilog Integer Types to C
1. The Four Rows
From the LRM, the integer mapping:
| SystemVerilog | C | Width |
|---|---|---|
byte | char | 8 |
shortint | short int | 16 |
int | int | 32 |
longint | long long int | 64 |
Measured on this machine:
SV byte -> char sizeof 1 range -128..127
SV shortint -> short sizeof 2 range -32768..32767
SV int -> int sizeof 4 range -2147483648..2147483647
SV longint -> long long sizeof 8 range -9223372036854775808..9223372036854775807Everything lines up, every width is what the table says, and Chapter 20.2 §4's line puts all four on the no-header side.
So the table is correct and this chapter is not about the table. It is about the four assumptions underneath it, three of which are real and one of which will change what you write.
2. The First Row Is the Dangerous One
byte → char looks like the simplest row and it is the one to be careful about, for a reason Chapter 14.1 established in Part II's very first chapter.
Plain char's signedness is implementation-defined. Not signed char, not unsigned char — plain char is a third type, and whether it behaves as signed or unsigned is the implementation's choice.
Measured:
plain char is SIGNED here
CHAR_MIN -128 CHAR_MAX 127
the byte 0x80 through a plain char: -128
through signed char: -128 through unsigned char: 128And Chapter 14.1 §2 measured the part that makes this more than trivia: the same source on the same machine with -funsigned-char gives the opposite answer. It is a build input, not a property of the chip.
So a C function written as
int c_handle(char value); /* the mapping table's spelling */has an argument whose range is -128..127 on this build and 0..255 on another, from the same source. SystemVerilog's byte is signed and byte unsigned is not; the C side should say which it is rather than inherit a choice from a compiler flag.
int c_handle(int8_t value); /* SV byte */
int c_handle(uint8_t value); /* SV byte unsigned */Chapter 14.1 §5 gave the rule — if a representation is specified outside your program, declare it explicitly rather than inheriting the compiler's default — and a DPI declaration is exactly such a specification.
3. The Second and Third Rows
Both assume something C does not promise, and in practice both are fine.
shortint → short. C guarantees short is at least 16 bits, not exactly 16. Measured 2 bytes here. A target with a 32-bit short is permitted by C and is essentially unknown in practice, but the assumption is there.
int → int. C guarantees int is at least 16 bits; SystemVerilog's int is exactly 32. Measured 4 bytes here, so they coincide. On a 16-bit embedded target C's int would be 16 bits and the mapping would not hold — which is why the DPI mapping is specified against the simulator's host rather than against any target.
Chapter 14.1 §4 established the underlying fact and it is the same one in both rows: C fixes minimum ranges, not widths. Two types agreeing in size on your machine is Chapter 16.8's observed, not guaranteed.
4. The Fourth Row, and Why It Is Not long
longint maps to long long int, and the obvious-looking alternative is wrong in a way that is invisible here.
Measured:
long 8 bytes, long long 8 bytes -- equal HERE
LONG_MAX 9223372036854775807
LLONG_MAX 9223372036854775807Identical. Both 8 bytes, both with the same maximum. A function written with long works perfectly on this machine and passes every test.
From the C standard: long is guaranteed at least 32 bits; long long is guaranteed at least 64. On 32-bit targets and on 64-bit Windows, long is 4 bytes. A long parameter there carries half of a longint.
That is why the mapping names long long — it is the type that is 64 bits everywhere — and it is a good illustration of the chapter's theme. The row looks like a free choice between two synonyms, the two are synonyms on the machine in front of you, and one of them is a portability bug waiting for a different host.
5. What to Write
The mapping tells you what the SystemVerilog type is. It does not tell you the best way to spell the C side, and the two answers differ in one row.
#include <stdint.h>
int8_t /* SV byte */
int16_t /* SV shortint */
int32_t /* SV int */
int64_t /* SV longint */Three reasons, all of them Chapter 14.2's:
The width is stated rather than assumed. int32_t is 32 bits by definition; int is 32 bits on this host.
The signedness is stated. This is the one that matters, and §2 is why: int8_t is signed and uint8_t is unsigned, and neither depends on a compiler flag the way char does.
It reads as a transcription. A reviewer comparing the C against the DPI declaration sees int32_t against int and checks one fact. Seeing long against longint requires knowing which targets make them differ.
And the one honest caveat, from the callout: the exact-width types are optional in principle. On any host that runs a simulator they exist, so this is a recommendation you can follow without reservation — but it is worth knowing why the standard did not make it for you.
6. Where the Width Is Not the Question
One thing this chapter has held fixed deliberately: every row above is about width, and signedness has been treated as settled.
It is not. Chapter 20.1 §2 measured the shape of the problem:
0x7F 127 127 yes
0x80 -128 128 NO <- differA byte and a byte unsigned are the same width and carry different meanings, and a C side that picks the wrong one is correct for every value below the halfway point. That is a separate axis from everything in this chapter, it is rated Critical where this one is High, and it is Chapter 20.4.
7. The Misconception: The Table Tells Me What to Write
"The mapping says
byteischar, so I writechar."
The table says what the SystemVerilog type corresponds to. It is a statement about the language mapping, not advice about your declaration, and following it literally on the first row gives you an argument whose signedness is a build input.
Measured: plain char is signed here, -funsigned-char reverses it (14.1 §2), and the byte 0x80 is therefore -128 or 128 depending on how the project is built.
The same reasoning applies more weakly to the other rows — long long is the right spelling and int64_t is a clearer one — but the first row is the one where the table's spelling is actually a poor choice.
The corrected reading:
Read the table to learn what the SystemVerilog type is. Write the exact-width C type that says the same thing, with the signedness explicit.
And the general habit, which is Chapter 16.8 §7's and Chapter 20.1 §7's: put the SystemVerilog type in a comment beside the C, so the next reader can check one line against the declaration without consulting a table.
/* DPI import: function int c_decode(input byte unsigned tag, input int len); */
int32_t c_decode(uint8_t tag, int32_t len);8. What You Should Be Able to Say Now
- Four rows:
byte→char,shortint→short,int→int,longint→long long int. Measured 1, 2, 4 and 8 bytes here, all matching. - All four are scalar types, so none needs
svdpi.h(20.2 §4). - The first row hides the real assumption: plain
char's signedness is implementation-defined. Measured signed here, and 14.1 §2 measured-funsigned-charreversing it on the same machine. - So
0x80is-128or128depending on the build — measured both ways. - Rows two and three assume minimums are exact. C guarantees
short≥ 16 andint≥ 16; SystemVerilog'sintis exactly 32. They coincide here. - Row four is not
long. Measured:longandlong longare both 8 bytes here with identical maxima — andlongis 4 bytes on 32-bit targets and 64-bit Windows. - The mapping names traditional types because they always exist; the exact-width types are optional in principle (14.2 §2) and present on every simulator host.
- Write
int8_t/int16_t/int32_t/int64_t— width stated, signedness stated, and it reads as a transcription. - This chapter held signedness fixed, and that axis is separate, Critical, and Chapter 20.4's.
- Put the SystemVerilog declaration in a comment beside the C, so one line can be checked against one line.
9. Exercises
- Print
sizeofand the range forchar,signed char,unsigned char,short,int,longandlong long. Which pairs are indistinguishable on your machine? - Compile a function taking a plain
charand print what it receives for the byte0x80. Then rebuild with-funsigned-charand repeat. - Write the C side of
import "DPI-C" function void c_f(input byte a, input byte unsigned b, input longint c);using the exact-width types, with the declaration in a comment. - §4 says
longwould be a portability bug. Name two targets where it breaks and say what alongintargument would arrive as. - The callout says
int8_tis optional. Find the condition in the C standard under which an implementation may omit it, and say whether a simulator host could meet it. - Take a DPI import you have seen with a
byteargument. Was the C sidechar,signed char,unsigned char, or an exact-width type? What range of values has it been tested with? - §6 says signedness is a separate axis. Construct a function that is correct for all values of a
byteand wrong for half the values of abyte unsigned, with the same C declaration.
10. Summary
- Four rows:
byte→char,shortint→short,int→int,longint→long long. Measured 1 / 2 / 4 / 8 here. - All scalar — no
svdpi.hneeded (20.2). - The first row is the dangerous one: plain
char's signedness is implementation-defined — measured signed, reversed by-funsigned-char(14.1 §2).0x80is-128or128by build flag. - Rows two and three assume minimums are exact — C fixes ranges, not widths (14.1 §4).
- Row four is not
long— measured identical here, and 4 bytes on 32-bit targets and 64-bit Windows. - The mapping names traditional types because they always exist; exact-width types are optional in principle, present in practice.
- Write
int8_t…int64_t— width and signedness stated, reads as a transcription. - Signedness is a separate, Critical axis — Chapter 20.4.
- Put the declaration in a comment beside the C.
Width has been settled four times and each time the chapter said "assuming the signedness is right". It is the other half of every one of those rows, it is the half with no size to check it against, and the mistake it produces passes every test that uses ordinary values.
Standards & specifications
- Governing standard
- IEEE Std 1800 (SystemVerilog)(opens IEEE in a new tab)
Defines SystemVerilog language semantics — syntax, data types, scheduling and the behaviour a conforming simulator must produce. It does not define tool-specific synthesis support or vendor methodology.
This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.
Where this fits
Part of the C Programming curriculum.
