原文出处:https://mp.weixin.qq.com/s/ljYZwMj3JaPN29dTAXA3bQ

随着国内第一本RISC-V中文书籍《手把手教你设计CPU——RISC-V处理器篇》 正式上市,越来越多的爱好者开始使用开源的蜂鸟E203 RISC-V处理核,很多初学者留言询问有关RISC-V工具链使用的问题,因此本公众号将开始陆续发表若干篇有关RISC-V软件工具链使用的文章,包括:

  • RISC-V嵌入式开发准备篇1:编译过程简介
  • RISC-V嵌入式开发准备篇2:嵌入式开发的特点介绍
  • RISC-V嵌入式开发入门篇1:RISC-V GCC工具链的介绍
  • RISC-V嵌入式开发入门篇2:RISC-V汇编语言程序设计
  • RISC-V嵌入式开发上手篇:基于HBird-E-SDK平台的软件开发与运行
  • RISC-V嵌入式开发实践篇:运行开源蜂鸟E200 MCU更多示例程序
  • RISC-V嵌入式开发新奇篇:基于Windows Eclipse IDE的软件开发与运行
  • RISC-V嵌入式开发升华篇:基于开源蜂鸟E200 MCU移植RTOS

本文为RISC-V嵌入式开发准备篇2:嵌入式开发的特点介绍。

本文的目的是对嵌入式开发的特点进行简单的科普与回顾,为后续详细介绍“RISC-V GCC工具链”和“RISC-V汇编语言程序设计”打下基础。注:本文力求通俗易懂,主要面向初学者,对嵌入式开发有所了解的读者可以忽略此文。

在本号上次发表的文章《编译过程简介》中介绍过,嵌入式系统的程序编译过程和开发有其特殊性,譬如:

  • 嵌入式系统需要使用交叉编译与远程调试的方法进行开发。
  • 需要自己定义引导程序。
  • 需要注意减少代码体积(Code Size)。
  • 需要移植printf从而使得嵌入式系统也能够打印输入。
  • 使用Newlib作为C运行库。
  • 每个特定的嵌入式系统都需要配套的板级支持包。
    下文将分别予以介绍。

1 交叉编译和远程调试

在本号上次发表的文章《编译过程简介》中介绍了如何在Linux系统的PC电脑上开发一个Hello World程序,对其进行编译,然后运行在此电脑上。在这种方式下,我们使用PC电脑上的编译器编译出该PC电脑本身可执行的程序,这种编译方式称之为本地编译。

嵌入式平台上往往资源有限,嵌入式系统(譬如常见ARM MCU或8051单片机)的存储器容量通常只在几KB到几MB之间,且只有闪存而没有硬盘这种大容量存储设备,在这种资源有限的环境中,不可能将编译器等开发工具安装在嵌入式设备中,所以无法直接在嵌入式设备中进行软件开发。因此,嵌入式平台的软件一般在主机PC上进行开发和编译,然后将编译好的二进制代码下载至目标嵌入式系统平台上运行,这种编译方式属于交叉编译。

交叉编译可以简单理解为,在当前编译平台下,编译出来的程序能运行在体系结构不同的另一种目标平台上,但是编译平台本身却不能运行该程序,譬如,在x86平台的PC电脑上编写程序并编译成能运行在ARM平台的程序,编译得到的程序在x86平台上不能运行,必须放到ARM平台上才能运行。

与交叉编译同理,在嵌入式平台上往往也无法运行完整的调试器,因此当运行于嵌入式平台上的程序出现问题时,需要借助主机PC平台上的调试器来对嵌入式平台进行调试。这种调试方式属于远程调试。

常见的交叉编译和远程调试工具是GCC和GDB。在本号上次发表的文章《编译过程简介》中介绍了如何使用Linux自带的GCC本地编译一个Hello World程序并运行。但是,GCC不仅能作为本地编译器,还能作为交叉编译器;同理GDB不仅可以作为本地调试器,还可以作为远程调试器。

当作为交叉编译器之时,GCC通常有不同的命名,譬如:

  • arm-none-eabi-gcc和arm-none-eabi-gdb是面向裸机(Bare-Metal)ARM平台的交叉编译器和远程调试器。

    • 所谓裸机(Bare-Metal)是嵌入式领域的一个常见形态,表示不运行操作系统的系统
  • 而riscv-none-embed-gcc和riscv-none-embed-gdb是面向裸机RISC-V平台的交叉编译器和远程调试器。
    • 本号后续发文《RISC-V GCC工具链的介绍》将介绍RISC-V GCC工具链的更多信息。

2 移植newlib或newlib-nano作为C运行库

newlib是一个面向嵌入式系统的C运行库。相对于本号上次发表的文章《编译过程简介》中介绍的glibc,newlib实现了大部分的功能函数,但体积却小很多。newlib独特的体系结构将功能实现与具体的操作系统分层,使之能够很好地进行配置以满足嵌入式系统的要求。由于专为嵌入式系统设计,newlib具有可移植性强、轻量级、速度快、功能完备等特点,已广泛应用于各种嵌入式系统中。

由于嵌入式操作系统和底层硬件的多样性,为了能够将C/C++语言所需要的库函数实现与具体的操作系统和底层硬件进行分层,newlib的所有库函数都建立在20个桩函数的基础上,这20个桩函数完成具体操作系统和底层硬件相关的功能:

  • I/O和文件系统访问(open、close、read、write、lseek、stat、fstat、fcntl、link、unlink、rename);
  • 扩大内存堆的需求(sbrk);
  • 获得当前系统的日期和时间(gettimeofday、times);
  • 各种类型的任务管理函数(execve、fork、getpid、kill、wait、_exit);
    这20个桩函数在语义、语法上与POSIX(Portable Operating System Interface of UNIX)标准下对应的20个同名系统调用完全兼容。

所以,如果需要移植newlib至某个目标嵌入式平台,成功移植的关键是在目标平台下找到能够与newlib桩函数衔接的功能函数或者实现这些桩函数。本号后续发文《基于HBird-E-SDK平台的软件开发与运行》将介绍蜂鸟E200的HBird-E-SDK平台如何实现移植实现newlib的桩函数。

注意:newlib的一个特殊版本newlib-nano版本进一步为嵌入式平台减少了代码体积(Code Size),因为newlib-nano提供了更加精简版本的malloc和printf函数的实现,并且对库函数使用GCC的-Os(侧重代码体积的优化)选项进行编译优化。

3 嵌入式引导程序和中断异常处理

在本号上次发表的文章《编译过程简介》中介绍了如何在Linux系统的PC电脑上开发一个Hello World程序,对其进行编译,然后运行在此电脑上。在这种方式下,程序员仅仅只需要关注Hello World程序本身,程序的主体由main函数组织而成,程序员可以无需关注Linux操作系统在运行该程序的main函数之前和之后需要做什么。事实上,在Linux操作系统中运行应用程序(譬如简单的Hello World)时,操作系统需要动态地创建一个进程、为其分配内存空间、创建并运行该进程的引导程序,然后才会开始执行该程序的main函数,待其运行结束之后,操作系统还要清除并释放其内存空间、注销该进程等。

从上述过程中可以看出,程序的引导和清除这些“脏活累活”都是由Linux这样的操作系统来负责进行。但是在嵌入式系统中,程序员除了开发以main函数为主体的功能程序之外,还需要关注如下两个方面:

  • 引导程序:

    • 嵌入式系统上电后需要对系统硬件和软件运行环境进行初始化,这些工作往往由用汇编语言编写的引导程序完成。
    • 引导程序是嵌入式系统上电后运行的第一段软件代码。引导程序对于嵌入式系统非常关键,引导程序所执行的操作依赖于所开发的嵌入式系统的软硬件特性,一般流程包括:初始化硬件、设置异常和中断向量表、把程序拷贝到片上SRAM中、完成代码的重映射等,最后跳转到main函数入口。
    • 本号后续发文《基于HBird-E-SDK平台的软件开发与运行》将结合HBird-E-SDK平台的引导程序实例了解引导程序的更多细节。
  • 中断异常处理
    • 中断和异常是嵌入式系统非常重要的一个环节,因此,嵌入式系统软件还必须正确地配置中断和异常处理函数。有关RISC-V架构的中断和异常的详细信息,请参见RISC-V中文书籍《手把手教你设计CPU——RISC-V处理器篇》 中第13章内容《不得不说的故事——中断和异常》。
    • 本号后续发文《基于HBird-E-SDK平台的软件开发与运行》将结合HBird-E-SDK程序实例了解如何配置中断和异常处理函数。

4 嵌入式系统链接脚本

在本号上次发表的文章《编译过程简介》中介绍了如何在Linux系统的PC电脑上开发一个Hello World程序,对其进行编译,然后运行在此电脑上。在这种方式下,程序员也无需关心编译过程中的“链接”这一步骤所使用的链接脚本,无需为程序分配具体的内存空间。

但是在嵌入式系统中,程序员除了开发以main函数为主体的功能程序之外,还需要关注“链接脚本”为程序分配合适的存储器空间,譬如程序段放在什么区间、数据段放在什么区间等等。

本号后续发文《基于HBird-E-SDK平台的软件开发与运行》将结合HBird-E-SDK的“链接脚本”实例了解更多细节。

5 减少代码体积

嵌入式平台上往往存储器资源有限,嵌入式系统(譬如常见的ARM MCU或8051单片机)的存储器容量通常只在几KB到几MB之间,且只有闪存而没有硬盘这种大容量存储设备,在这种资源有限的环境中,程序的代码体积(Code Size)显得尤其重要,因此,有效地降低降低代码体积(Code Size)是嵌入式软件开发必须要考虑的问题,常见的方法如:

  • 使用newlib-nano作为C运行库以取得较小代码体积(Code Size)的C库函数。
  • 尽量少使用C语言的大型库函数,譬如在正式发行版本的程序中避免使用printf和scanf等函数。
  • 如果在开发的过程中一定需要使用printf函数,可以使用某些自己实现的阉割版printf函数(而不是C运行库中提供的printf函数)以生成较小的代码体积。
  • 除此之外,在C/C++语言的语法和程序开发方面也有众多技巧以取得更小的代码体积(Code Size)。
  • 本号后续发文《基于HBird-E-SDK平台的软件开发与运行》将结合HBird-E-SDK平台实例了解更多“减少代码体积”的实现细节。

减小代码体积(Code Size)的方法很多,本文在此不做一一赘述,请初学的读者自行查阅相关资料进行学习。

6 支持printf函数

在本号上次发表的文章《编译过程简介 》中介绍了如何在Linux系统的PC电脑上开发一个Hello World程序,程序中使用C语言的标准库函数printf打印了一个“Hello World”字符串。该程序在Linux系统里面运行的时候字符串被成功的输出到了Linux的终端界面上。在这个过程中,程序员无需关心Linux系统到底是如何将printf函数的字符串输出到Linux终端上的。事实上,如《编译过程简介》中所述,在Linux本地编译的程序会链接使用Linux系统的C运行库glibc,而glibc充当了应用程序和Linux操作系统之间的接口,glibc提供的 printf 函数就会调用如sys_write等操作系统的底层系统调用函数,从而能够将“字符串”输出到Linux终端上。

从上述过程中可以看出,由于有glibc的支持,所以printf函数能够在Linux系统中正确的进行输出。但是在嵌入式系统中,printf的输出却不那么容易了,基于如下几个原因:

  • 嵌入式系统使用newlib作为C运行库,而newlib的C运行库所提供的printf函数最终依赖于如本文中所介绍的newlib桩函数write,因此必须实现此write函数才能够正确的执行printf函数。
  • 嵌入式系统往往没有“显示终端”存在,譬如常见的单片机其作为一个黑盒子一般的芯片,根本没有显示终端。因此,为了能够支持显示输出,通常需要借助单片机芯片的UART接口将printf函数的输出重新定向到主机PC的COM口上,然后借助主机PC的串口调试助手显示出输出信息。同理,对于scanf输入函数,也需要通过主机PC的串口调试助手获取输入然后通过主机PC的COM口发送给单片机芯片的UART接口。
  • 从以上两点可以看出,嵌入式平台的UART接口非常重要,往往扮演了输出管道的角色,为了能够将printf函数的输出定向到UART接口,需要实现newlib的桩函数write,使其通过编程UART的相关寄存器将字符通过UART接口输出。

本号后续发文《基于HBird-E-SDK平台的软件开发与运行》将结合HBird-E-SDK平台移植printf函数的实例了解更多细节。

7 提供板级支持包

对于特定的嵌入式硬件平台,为了方便用户在硬件平台上开发嵌入式程序,硬件平台一般会提供板级支持包(Board Support Package,BSP)。板级支持包所包含的内容没有绝对的标准,通常说来,其必须包含如下内容:

  • 底层硬件设备的地址分配信息
  • 底层硬件设备的驱动函数
  • 系统的引导程序
  • 中断和异常处理服务程序
  • 系统的链接脚本
  • 如果使用newlib作为C运行库,一般还提供newlib桩函数的实现。

由于板级支持包往往会将很多底层的基础设施和移植工作搭建好,因此应用程序开发人员通常都无需关心本文第1.2节至第1.6节中描述的内容,能够从底层细节中被解放出来避免重复建设而出错。本号后续发文《基于HBird-E-SDK平台的软件开发与运行》将结合HBird-E-SDK平台的BSP实例了解更多细节。

更多信息

感兴趣的读者可以通过下面二维码关注公众号“硅农亚历山大”,了解Verilog、IC设计、CPU、RISC-V和人工智能AI相关的更多设计技巧和经验分享,注意:由于干货太多,请自备茶水。

RISC-V嵌入式开发准备篇2:嵌入式开发的特点介绍相关推荐

  1. iOS开发UI篇—CALayer简介

    iOS开发UI篇-CALayer简介 一.简单介绍 在iOS中,你能看得见摸得着的东西基本上都是UIView,比如一个按钮.一个文本标签.一个文本输入框.一个图标等等,这些都是UIView. 其实UI ...

  2. android 串口开发第二篇:利用jni实现android和串口通信

    一:串口通信简介 由于串口开发涉及到jni,所以开发环境需要支持ndk开发,如果未配置ndk配置的朋友,或者对jni不熟悉的朋友,请查看上一篇文章,android 串口开发第一篇:搭建ndk开发环境以 ...

  3. Hololens开发入门篇-郑洪智-专题视频课程

    Hololens开发入门篇-572人已学习 课程介绍         本课程使用Hololens模拟器,基于Unity2017.2及Visual Studio 2017开发 课程收益     学会Ho ...

  4. react native开发Android 篇——集成自定义的字体

    react native开发Android 篇--集成自定义的字体 第一种:link添加自定义字体 第二种:直接复制字体到`android/app/src/main/assets/fonts`目录下 ...

  5. android tableview实现多选功能,iOS开发UI篇-tableView在编辑状态下的批量操作(多选)...

    先看下效果图 直接上代码 #import "MyController.h" @interface MyController () { UIButton *button; } @pr ...

  6. Cocos2d-Lua手游开发基础篇-何韬-专题视频课程

    Cocos2d-Lua手游开发基础篇-62875人已学习 课程介绍         Cocos2d-Lua是一个开源的移动游戏框架,可以轻松运行在iOS.Android.Blackberry等操作系统 ...

  7. RISC-V嵌入式开发准备篇1:编译过程简介

    原文出处:https://mp.weixin.qq.com/s/-syKN0DibKGGPCllaeNqMg 随着国内第一本RISC-V中文书籍<手把手教你设计CPU--RISC-V处理器篇&g ...

  8. Linux嵌入式系统开发之Led开发——应用篇(一)

    与Linux嵌入式系统开发之Led开发--驱动篇(一),对于的应用篇 看看咱们的开发板,有四个led灯,对吧,这次就是向办法用程序来点亮它,请看下边的代码: #include <stdlib.h ...

  9. 基于涂鸦智能开发的墨水屏座位管理器——2.嵌入式功能实现篇

    随着互联网连接技术的日益普及,以及大众环保意识增强,电子纸显示市场不断发展,墨水屏的应用场景也越来越多.墨水屏座位管理器方案具体功耗低,多节点管控,信息实时同步等特点,可应用于智慧办公,智慧零售,智慧 ...

最新文章

  1. PostgreSQL新手入门教程
  2. 184使用 Core Image 框架处理照片
  3. #279. [SYZOI Round1] 滑稽♂树(树状数组套主席树)
  4. UC浏览器云标签怎么用 UC浏览器云标签使用方法
  5. 嵌入式操作系统内核原理和开发(等值block内存池设计)
  6. 洛谷 2312 / bzoj 3751 解方程——取模
  7. MyBatis的ResultMaps
  8. 用HashAlgorithm來加强數據的保險程度。
  9. Win10电脑只有一个C盘怎么分区分盘?
  10. 计算机圣诞节教案,2010我们的圣诞节(2010圣诞节教案)
  11. IoT原型开发利用现成的单板设计---凯利讯半导体
  12. 华为社招/东莞、杭州、成都、西安
  13. python直联(适合于企业内部的内网邮箱)发送邮件(带附件)、发送给多人、抄送给多人的示例
  14. oracle 朱志辉_甲骨文(Oracle)授权教育中心落户我校
  15. java中的依赖是啥意思_java – “ – ”(箭头)在gradle的依赖图中是什么意思?
  16. 操作系统导论期末复习题
  17. Arcgis ToolBox学习之三——Tabulate Intersection
  18. 网龙网络控股有限公司公布二零一八年中期业绩
  19. 电子招投标管理系统源码-电子招投标认证服务平台-权威认证
  20. 全志A64内核编译一些错误解决方法

热门文章

  1. dism 分割镜像_DISM对原版镜像进行改造 整合驱动及补丁
  2. 如何挑选合适自己的内存
  3. 读标准02-IEEE1451.5-智能无线传感器标准介绍
  4. 武大博士后出站后应聘在社区做社工引热议,内卷还是人各有志?
  5. oh god job
  6. vue 菜单路由重复点击报错
  7. reduceByKeyAndWindow基于滑动窗口的热点搜索词实时统计(Scala版本)
  8. 【表盘识别】基于Hough变换实现指针式仪表识别(倾斜矫正)
  9. 路由器经常断网,网络不稳定怎么办, 教你如何让旧路由器也能运转飞起
  10. 【日语】【歌词】「ヤキモチ」--高橋優