GNU tools for ARC-V¶
The GNU toolchain for ARC-V processors is a pre-built and verified distribution of a GCC-based set of tools for building applications targeting supported ARC-V processors.
This toolchain is built on the pillars of proven and widely used open-source components such as GCC, Binutils, GDB, and Newlib. While many pre-built GCC-based toolchains are available around the Internet, and it is reasonably easy to build your own, the ARC GNU toolchain is prepared specifically to produce the best possible results for ARC processors. It also makes sure the results produced are reliable in all supported environments.
GNU Toolchain for ARC-V Processors v2024.06¶
This is the 2024.06 version of the GNU Toolchain for Synopsys ARC-V Processor IP. This is the first release that supports the latest GNU tools for both ARC Classic and ARC-V processors simultaneously.
More information about these processors can be found on Synopsys website:
New Features and Enhancements¶
There are no major changes for ARC targets except ARC-specific fixes. All components are updated on top of upstream releases.
Binary Distribution¶
Supported host operating systems: Windows 10/11 64-bit, 20.04.x, CentOS/RHEL 7.x
Prebuilt bare-metal toolchains for 64-bit Windows and Linux hosts; see table with references and list of artifacts.
Toolchain Components¶
GCC 14.1 with ARC patches
Uses upstream 14.1 release; see release announcement and complete list of changes.
Initial support of RVV extensions is added in upstream GCC 14.
Binutils 2.42 with ARC patches
Uses upstream 2.42 release; see release notes.
GDB 14.2 with ARC patches
Uses upstream 14.2 release; see release announcement for 14.1 and complete list of changes for 14.1 for major changes.
Newlib 4.4.0 with ARC patches
Uses upstream 4.4.0 release; see release announcement.
The upstream Newlib source tree now contains the latest changes for ARC Classic.
Known Issues¶
The size-optimized Newlib Nano configuration (used when
-specs=nano.specs
is passed to GCC) does not support printf() forfloat
anddouble
by default. Nano printf() is size-optimized and does not include support offloat
anddouble
. If you need that feature, pass-u _printf_float
to GCC when you compile your applications. This option picks up support offloat
anddouble
for size optimized printf() on demand.Some complex combinations of
-march=
and-mabi=
options might lead to unpredictable errors during compilation. Such issues are related to general support of RISC-V extensions in GCC. See #610.GCC 14 generates incorrect include directories if it’s configured with the
--with-sysroot=...
and--with-native-system-header-dir=...
options. This is a valid set of options for building GCC, but it leads to GCC including invalid paths in an include search list when-save-temps
is used while building binaries. This issue is not critical and will be addressed later. See #628.
Getting Help¶
Visit the GitHub discussions link for a community forum tailored to ARC-V processors. To report issues and enhancement requests, use GitHub issues (if you are not sure, start with discussions, as a discussion can always be converted into an issue).
Prebuilt Toolchains Available for Download¶
Archive SHA-256 checksums are:
b7c2b3f1ce3cda1d1472a0412c474e9a6ca1fe5ee48db4910664050456e28ceb arc_gnu_2024.06-rc1_prebuilt_riscv64_elf_le_win_install.tar.bz2
02580b4b8194695c3f5a6248fc52c1c0e16b252d7b4c2a24a059f653e2eb9eef arc_gnu_2024.06-rc1_prebuilt_riscv64_elf_le_linux_install.tar.bz2
GNU Toolchain for ARC-V Processors v2023.12¶
This is the 2023.12 version of the GNU Toolchain for Synopsys ARC-V Processor IP. This release introduces support for the ARC-V RMX and RHX processors series based on the RISC-V instruction-set architecture.
More information about these processors can be found on Synopsys website:
Note
This release of the ARC GNU tools doesn’t include prebuilt toolchains for ARC Classic processor families. Users of ARC 600, ARC 700, ARC EM, ARCv2 HS (HS3x and HS4x) and ARCv3 HS (HS5x and HS6x) should keep using ARC GNU Tools 2023.09 (latest version).
New Features and Enhancements¶
Support for RISC-V based ARC-V RMX and RHX Processors¶
In this release the following RISC-V extensions are supported:
Zba
,Zbb
,Zbs
,Zdinx
,Zfinx
,Zicbom
,Zicbop
, andZicboz
.Support for ARC-V RMX and RHX processors:
Instruction scheduling tuning for ARC-V RMX-100 (
-mtune=rmx100
) and RMX-500 (-mtune=rmx500
)Instruction fusion for the ARC-V RHX series (
-mtune=rhx
)Implementation of the code-size reduction extensions
Zcb
andZcmp
As this is the first release of prebuilt GNU tools with ARC-V support, some functionality might have issues, some processor features might not yet be fully supported, and performance might be lower than expected performance of the final product.
Binary Distribution¶
Supported host operating systems: Windows 10/11 64-bit, Ubuntu 18.04.x, 20.04.x, CentOS/RHEL 7.x
Prebuilt bare-metal toolchains for 64-bit Windows & Linux hosts; see table with references and list of artifacts.
Toolchain Components¶
GCC 13.2 with ARC patches
Uses upstream 13.2 release; see GCC release announcement and complete list of changes.
Added support for ARC-V RMX-100, RMX-500, and RHX processors.
Added code size reduction
Zcb
andZcmp
extensions.
Binutils pre-2.41 with ARC patches
Upstream sources of pre-v2.41; see Binutils release announcement.
Added code size reduction
Zcb
andZcmp
extensions.
GDB 12.1 with ARC patches
Upstream sources of v12.1; see GDB release announcement.
Newlib 4.3.0 with ARC patches
Updated sources of 4.3.0 release; see Newlib release announcement.
Improved start-up code to initialize GP (global pointer), SP (stack pointer), clear
.bss
section, and similar enhancements.Provided custom linker command file for use with fast on-chip memories (DCCM and ICCM).
Known Issues¶
No known issues for this release so far.
Getting Help¶
Also visit the GitHub discussions link for a community forum tailored to ARC-V processors. To report issues and enhancement requests, use GitHub issues (if you are not sure, start with discussions, since a discussion can always be converted into an issue).
Prebuilt Toolchains Available for Download¶
Archive SHA-256 checksums are:
4e555c1b214eb97fd3bf9e1ffa43d69a0d274deb295fec66bdfdebef714b7bf7 arc_gnu_2023.12_prebuilt_riscv64_elf_le_win_install.tar.bz2
9dedb6e496e52a45ce536c9a1970a44401319c42ea17b7b8f6655b6204b9b444 arc_gnu_2023.12_prebuilt_riscv64_elf_le_linux_install.tar.bz2
Command-Line Reference¶
Because the ARC-V GNU toolchain is built on top of standard components such as GCC, Binutils, and GDB, all functionality of these tools stays in place and can be studied in detail in the corresponding manuals:
GCC documentation: https://gcc.gnu.org/onlinedocs/gcc/
GNU LD documentation: https://sourceware.org/binutils/docs/ld/
GDB documentation: https://www.sourceware.org/gdb/documentation/
On top of the functionality that comes with the standard releases of these tools, the ARC GNU toolchain adds some ARC-V specific options:
The ARC-V GNU compiler has support for the ARC-V RMX-100, RMX-500, and RHX CPU families.
To enable compiler optimizations for those CPU families you must use certain
-mtune
options. The valid -mtune
options are:
arc-v-rmx-100-series
for the ARC-V RMX-100 family (rmx100
in v2023.12 and v2024.06 releases)arc-v-rmx-500-series
for the ARC-V RMX-500 family (rmx500
in v2023.12 and v2024.06 releases)arc-v-rhx-100-series
for the ARC-V RHX-100 family (rhx
in v2023.12 and v2024.06 releases)
The architecture options used by the ARC-V RMX-100, RMX-500 and RHX-100 families are -march=rv32im_zba_zbb_zbs_zicsr_zca_zcb_zcmp -mabi=ilp32
.
ARC-V .specs File¶
The new arcv.specs
file uses a custom starting routine based on the original crt0.S
with a special linker command file.
The latter is highly configurable, introducing four new linker defines:
txtmem_addr
if defined sets the ICCM start address; otherwise the ICCM start address is set to0x0
txtmem_len
if defined sets the ICCM length; otherwise the length is set to128K
datamem_addr
if defined sets the DCCM start address; otherwise the DCCM start address is set to0x8000_0000
datamem_len
if defined sets the DCCM length, otherwise the length is set to128K
Where:
ICCM is usually flash memory or ROM.
DCCM is usually SRAM.
You can set a linker define as follows (using the GCC driver): For example:
-Wl,-defsym=datamem_addr=0x10000
This spec file can be used with other spec files too.
For example, using semihosting: --specs=semihost.specs --specs=arcv.specs
.
The First Sample Application¶
Consider the following sample application:
#include <stdio.h>
extern void init_semihosting ();
int main(void) {
init_semihosting();
printf("Hello world!\n");
return 0;
}
Build and Run a Simple Application for Synopsys nSIM¶
The following commands can be used for compiling the sample for later execution in the nSIM simulator:
For an ARC-V RMX-100 processor:
$ riscv64-elf-gcc --specs=semihost.specs --specs=arcv.specs -mabi=ilp32 \ -mtune=arc-v-rmx-100-series -march=rv32im_zba_zbb_zbs_zca_zcb_zcmp_zicsr \ -T arcv.ld test.c -o a.out
For an ARC-V RMX-500 processor:
$ riscv64-elf-gcc --specs=semihost.specs --specs=arcv.specs -mabi=ilp32 \ -mtune=arc-v-rmx-500-series -march=rv32im_zba_zbb_zbs_zca_zcb_zcmp_zicsr \ -T arcv.ld test.c -o a.out
For an ARC-V RHX-100 processors:
$ riscv64-elf-gcc --specs=semihost.specs --specs=arcv.specs -mabi=ilp32 \ -mtune=arc-v-rhx-100-series -march=rv32im_zba_zbb_zbs_zca_zcb_zcmp_zicsr \ -T arcv.ld test.c -o a.out
After building, use the following command line to run the sample in nSIM:
$ nsimdrv -prop=nsim_isa_family=rv32 \
-prop=nsim_isa_ext=-all.i.zicsr.zifencei.zihintpause.b.zca.zcb.zcmp.zcmt.a.m.zbb.zba.zbs \
-prop=nsim_semihosting=1 -off=enable_exceptions a.out
Hello world!
For more details on use of the Synopsys nSIM simulator, see the section dedicated to it here: nSIM and NCAM.
Build and Run a Simple Application for QEMU (32-Bit Targets)¶
It is best to use the latest official QEMU version for the following exercises. As of today, it’s v9.0; see the release notes here: https://wiki.qemu.org/ChangeLog/9.0. The sources are on GitLab (https://gitlab.com/qemu-project/qemu) or in the official GitHub mirror (https://github.com/qemu/qemu).
Note
To get the latest version of QEMU on your system it might be much easier to build it from sources rather than trying to find a pre-built version which suits your host type and operating system.
Instructions for building on Linux are on QEMU’s Wiki: https://wiki.qemu.org/Hosts/Linux
First you need to install required additional packages, which depend on the Linux distribution used on the host.
For example, for Ubuntu 20.04 the following needs to be done:
$ apt install build-essential git libglib2.0-dev libfdt-dev \
libpixman-1-dev zlib1g-dev ninja-build python3-venv
For AlmaLinux 8 the following needs to be done:
$ dnf install git glib2-devel libfdt-devel pixman-devel zlib-devel \
bzip2 ninja-build python3 python38
After installation of prerequisites you can download sources, configure, and build in the following way:
# Clone v9.0 from GitHub repository
$ git clone --depth 1 -b stable-9.0 https://github.com/qemu/qemu.git
# Configure project for build selecting only RISCV32 full system emulation
$ ./configure --target-list=riscv32-softmmu
# Build QEMU
$ make
After the build is complete qemu-system-riscv32
is placed in the build
folder,
which you can add to the PATH
environment variable for convenience, and use the short binary name
instead of the full path.
The following commands can be used for compiling the sample for later execution in QEMU.
Basically the same compiler options are used, except that you need to make sure
DDR memory is used and the .text
section starts in the DDR beginning at 0x8000_0000
, where
the board jumps on start automatically. For that, you need to override two linker symbols otherwise defined in
ARC-V’s default linker script arcv.ld
: txtmem_addr
and datamem_addr
.
Then the command lines look like this:
For an ARC-V RMX-100 processor:
$ riscv64-elf-gcc -specs=semihost.specs -specs=arcv.specs -mabi=ilp32 \ -mtune=arc-v-rmx-100-series -march=rv32im_zba_zbb_zbs_zca_zcb_zcmp_zicsr \ -Wl,-defsym=txtmem_addr=0x80000000 -Wl,-defsym=datamem_addr=0x80100000 \ -T arcv.ld test.c -o a.out
For an ARC-V RMX-500 processor:
$ riscv64-elf-gcc -specs=semihost.specs -specs=arcv.specs -mabi=ilp32 \ -mtune=arc-v-rmx-500-series -march=rv32im_zba_zbb_zbs_zca_zcb_zcmp_zicsr \ -Wl,-defsym=txtmem_addr=0x80000000 -Wl,-defsym=datamem_addr=0x80100000 \ -T arcv.ld test.c -o a.out
For an ARC-V RHX-100 processor:
$ riscv64-elf-gcc -specs=semihost.specs -specs=arcv.specs -mabi=ilp32 \ -mtune=arc-v-rhx-100-series -march=rv32im_zba_zbb_zbs_zca_zcb_zcmp_zicsr \ -Wl,-defsym=txtmem_addr=0x80000000 -Wl,-defsym=datamem_addr=0x80100000 \ -T arcv.ld test.c -o a.out
After building, use the following command line to run the sample in QEMU:
$ qemu-system-riscv32 -semihosting -nographic -machine virt -cpu rv32 -bios none -kernel a.out
Hello world!
For more details on use of the QEMU simulator, see the official documentation here: https://www.qemu.org/docs/master/.
Build and Run a Simple Application for QEMU (64-Bit Targets)¶
According to the RISC-V ABI specification:
The medium low code model, or medlow, allows the code to address the whole RV32 address space or the lower
2 GiB and highest 2 GiB of the RV64 address space (0xFFFFFFFF7FFFF800 ~ 0xFFFFFFFFFFFFFFFF and
0x0 ~ 0x000000007FFFF7FF). By using the lui and load / store instructions, when referring to an
object, or addi, when calculating an address literal, for example, a 32-bit address literal can
be produced.
The medium any code model, or medany, allows the code to address the range between -2 GiB and +2 GiB
from its position. By using auipc and load / store instructions, when referring to an object, or addi,
when calculating an address literal, for example, a signed 32-bit offset, relative to the value of the
pc register, can be produced.
All pseudo-options like li
, la
, and so on, are transformed to real instructions according to the memory model used.
By default, GCC uses -mcmodel=medlow
. That means that the final binary cannot use memory regions above
0x000000007FFFF7FF
and below 0xFFFFFFFF7FFFF800
for 64-bit targets.
To overcome those restrictions, it is necessary to pass -mcmodel=medany
to choose the medany
memory model. Note
that the Synopsys GNU toolchain for ARC-V contains precompiled libraries for both the medlow
and medany
memory models.
When -mcmodel=...
is passed the appropriate libraries are linked with an application.
Consider this example:
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("argc: %d\n", argc);
for (int i = 0; i < argc; i++)
{
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}
Compiler this program and run using QEMU:
$ riscv64-elf-gcc \
-march=rv64imac -mabi=lp64 -mcmodel=medany -O2 -g3 \
-Wl,-defsym=txtmem_addr=0x80000000 -Wl,-defsym=datamem_addr=0x80100000 \
-specs=semihost.specs -specs=arcv.specs -T arcv.ld \
main.c -o main.elf
$ qemu-system-riscv64 \
-semihosting -nographic -display none -monitor none \
-machine virt -cpu rv64 -bios none -kernel main.elf -append "1 2 3"
argc: 4
argv[0] = main.elf
argv[1] = 1
argv[2] = 2
argv[3] = 3
Debugging a Simple Application¶
With the Synopsys LLDB Debugger and Visual Studio Code IDE¶
For detailed information on how to debug an application with the Synopsys LLDB Debugger and Visual Studio Code IDE, see the following section: The MetaWare Development Toolkit.
With QEMU and GDB¶
You can debug an application that is being run on QEMU with help of the built-in GDB server of QEMU. On the QEMU side, you need to enable the GDB sever and optionally instruct the QEMU not to start the application execution automatically, but instead wait for user input. For that you need to pass -s -S
commands to qemu-system-riscv32 so that the full command-line looks like this:
$ qemu-system-riscv32 -semihosting -nographic -machine virt -cpu rv32 -bios none -kernel a.out -s -S
Then you can attach the GDB client and start debugging. Do the following in a separate console:
$ riscv64-elf-gdb a.out
(gdb) target remote :1234
Remote debugging using :1234
0x00001000 in ?? ()
(gdb) b main
Breakpoint 1 at 0x800000b4: file ~/test.c, line 10.
(gdb) continue
Continuing.
Breakpoint 1, main () at ~/test.c:10
10 init_semihosting();
(gdb) c
Continuing.
[Inferior 1 (process 1) exited normally]
Note that the first location where the CPU is waiting for commands is 0x0000_1000. It’s a virt board reset vector location; see https://elixir.bootlin.com/qemu/v8.2.0/source/hw/riscv/boot.c#L396. It is not possible to suppress execution of that reset vector code as it’s an essential part of the QEMU board (think of it as a boot-ROM). You can only bypass it if you load the target executable from the GDB client with the load
command. Then execution starts from the loaded application entry point.
Testing Specific RISC-V Extensions¶
One of the benefits of RISC-V ISA is its extensibility. As a matter of fact, only a handful of instructions form the minimally required base. But the most interesting part is in utilizing application-specific extensions, which let you achieve benefits in certain situations.
In embedded and especially deeply embedded applications, one of the key objectives is to keep the memory footprint of the target application as tiny as possible. And the RISC-V Zc* extensions help to achieve exactly that. This section demonstrates a couple of those extensions in action. See also the documentation and related information on each extension on the corresponding GitHub resource here: https://github.com/riscv/riscv-code-size-reduction.
Zcmp Extension¶
Consider the following code snippet:
$ cat t01.c
void test (void) {
asm volatile (""
:
:
: "s0", "s1", "s2", "s3",
"s4", "s5", "s6", "s7",
"s8", "s9", "s10", "s11");
}
Here is a generated assembly listing targeting the Zcmp extension:
$ riscv64-elf-gcc -march=rv32im_zcmp -mabi=ilp32 t01.c -S -O1 -o -
.file "t01.c"
.option nopic
.attribute arch, "rv32i2p1_m2p0_zca1p0_zcmp1p0"
.attribute unaligned_access, 0
.attribute stack_align, 16
.text
.align 1
.globl test
.type test, @function
test:
cm.push {ra, s0-s11}, -64
cm.popret {ra, s0-s11}, 64
.size test, .-test
.ident "GCC: (RISC-V elf toolchain - build 6278) 13.2.0"
Note the generated cm.push
and cm.popret
instructions, which confirm that the compiler is capable of using the extension.
Zcb Extension¶
Another RISC-V extension that is easy to see in action also comes from the code-size-reduction group: Zcb. Consider the following C function:
$ cat t03.c
int test (int a, int b) {
return a + b;
}
Now compile and disassemble it:
$ riscv64-elf-gcc -march=rv32im_zca_zcb -mabi=ilp32 t03.c -c -O1
$ riscv64-elf-objdump -d t03.o
t03.o: file format elf32-littleriscv
Disassembly of section .text:
00000000 <test>:
0: 952e add a0,a0,a1
2: 8082 ret
Note the short 16-bit encoding of the add
instruction. Without use of Zc extensions, compiler generates a full 32-bit encoding as in the example below.
$ riscv64-elf-gcc -march=rv32im -mabi=ilp32 t03.c -c -O1
$ riscv64-elf-objdump -d t03.o
t03.o: file format elf32-littleriscv
Disassembly of section .text:
00000000 <test>:
0: 00b50533 add a0,a0,a1
4: 00008067 ret
Building a Toolchain for a Particular Architecture¶
The GNU toolchain for ARC-V targets contains a set of precompiled standard libraries
for different combinations of -march
, -mabi
and -mcmodel
. You can
find a list of available configurations this way:
$ riscv64-elf-gcc --print-multi-lib
.;
rv32i/ilp32;@march=rv32i@mabi=ilp32
rv32i/ilp32/medany;@march=rv32i@mabi=ilp32@mcmodel=medany
rv32iac/ilp32;@march=rv32iac@mabi=ilp32
rv32iac/ilp32/medany;@march=rv32iac@mabi=ilp32@mcmodel=medany
rv32im/ilp32;@march=rv32im@mabi=ilp32
rv32im/ilp32/medany;@march=rv32im@mabi=ilp32@mcmodel=medany
rv32imac/ilp32;@march=rv32imac@mabi=ilp32
rv32imac/ilp32/medany;@march=rv32imac@mabi=ilp32@mcmodel=medany
rv32imafc_zicsr/ilp32f;@march=rv32imafc_zicsr@mabi=ilp32f
rv32imafc_zicsr/ilp32f/medany;@march=rv32imafc_zicsr@mabi=ilp32f@mcmodel=medany
rv32imafd_zicsr/ilp32d;@march=rv32imafd_zicsr@mabi=ilp32d
rv32imafd_zicsr/ilp32d/medany;@march=rv32imafd_zicsr@mabi=ilp32d@mcmodel=medany
rv32imafdc_zicsr/ilp32d;@march=rv32imafdc_zicsr@mabi=ilp32d
rv32imafdc_zicsr/ilp32d/medany;@march=rv32imafdc_zicsr@mabi=ilp32d@mcmodel=medany
rv64imac/lp64;@march=rv64imac@mabi=lp64
rv64imac/lp64/medany;@march=rv64imac@mabi=lp64@mcmodel=medany
rv64imafdc_zicsr/lp64d;@march=rv64imafdc_zicsr@mabi=lp64d
rv64imafdc_zicsr/lp64d/medany;@march=rv64imafdc_zicsr@mabi=lp64d@mcmodel=medany
Libraries for this toolchain were built with these combinations of configurations:
-march=rv32i -mabi=ilp32 -mcmodel=medlow
-march=rv32i -mabi=ilp32 -mcmodel=medany
…
-march=rv32imafc_zicsr -mabi=ilp32f -mcmodel=medlow
…
The easiest way to build a toolchain for another custom configuration is to add a combination to the Crosstool-NG configuration. You can find a comprehensive guide about how to get and prepare Crosstool-NG here: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain.
First, set up a standard ARC-V configuration file and enter a configuration menu:
./ct-ng snps-riscv64-unknown-elf
./ct-ng menuconfig
For example, if you want to build Crosstool-NG with just these configurations:
-march=rv32imafc_zicond_zicsr_zifencei -mabi=ilp32f
-march=rv64imafdc_zicsr -mabi=lp64d
Navigate to the C compiler menu and
replace the value of Generator of RISC-V multilib variants with this string:
rv32imafc_zicond_zicsr_zifencei-ilp32f--;rv64imafdc_zicsr-lp64d--;
(place it right after the gcc extra config field).
Then build the toolchain as usual:
./ct-ng build
The output toolchain is located in the riscv64-unknown-elf
directory:
$ ./riscv64-unknown-elf/bin/riscv64-elf-gcc --print-multi-lib
.;
rv32imafc_zicond_zicsr_zifencei/ilp32f;@march=rv32imafc_zicond_zicsr_zifencei@mabi=ilp32f
rv64imafdc_zicsr/lp64d;@march=rv64imafdc_zicsr@mabi=lp64d
Now when you use -march=rv32imafc_zicond_zicsr_zifencei -mabi=ilp32f
or compatible combinations, a corresponding precompiled library is used.