組み込みシステム開発環境には、主に次のものが含まれます。
- 統合開発ツール
- クロスコンパイラ
- バッチファイル
- makefile
- リンクスクリプト
- デバッグツール
- ダウンロードツール
- その他のツール(オフラインツール)
- エミュレータ
- バージョン制御ツール
次に、上記のツールについて個別に説明します。
1.統合開発ツール
通常、CPUメーカーはCPUに統合開発環境(IDE)を提供しますが、実際のアプリケーションでは、ほとんどの組み込みプロジェクト開発会社は引き続き独自の開発環境を使用します。1つは、プロジェクトの一部の機能の特別な要件であり、もう1つは、すべてのCPUモデルに対応するIDEがあるわけではないということです。
2、クロスツール
クロスツールには次のものが含まれます。
- クロスアセンブラ
- クロスコンパイラ
- クロスライブラリ
- クロスリンカー
- ダンプツール(実行可能ファイルのアセンブリ言語コードへの変換に関する情報)
- デバッグツール(GNU gdb)
GNU Tool-ChainのCコンパイラgccを例にとると、以下はいくつかのコンパイルオプションです。
- -エラー:すべての警告メッセージをエラーメッセージに変換します。警告メッセージが生成されると、ターゲットファイルは生成されません。
- -S:コンパイル時にアセンブリ言語コードを出力します。
- -C:コンパイル中にオブジェクトファイルのみが生成されます
- -E:オブジェクトファイルを生成せずに前処理のみを実行します
- -D:コンパイル時にマクロ定数を定義します
- -O、-O2、-O3:最適化レベル。
- -g:コンパイル時にデバッグ情報を追加して、GDBでデバッグできるようにします
例として、GNUTool-ChainのCリンカーリンカーを取り上げます。いくつかのオプションを次に示します。
- -T:リンクスクリプトファイルを作成する
- -マップ:接続時に、プログラム内のすべてのシンボルのアドレス情報を含むマップファイルを生成します。
GNUツールチェーンは多くの異なるCPUをサポートでき、ユーザーは必要に応じて構成を設定できます。たとえば、arm-elf-gccはelf形式でARMマシンコードを生成するCコンパイラであり、68K-coff-ldはCOFF形式で68000マシンコードを生成するリンカーです。
3、作る
makeは、自動コンパイル用のプログラムツールです。makefileを使用して、どのツール(.c、 .obj、...)をどのツール(.c、 .obj、...)に使用するか(最適化されていないオブジェクトファイルを生成する)を詳細に記述している限り、)、makeは、これらのファイルが古くなっているかどうかもチェックします。古くなっている場合は、コンパイルが必要なファイルのみを再コンパイルします(makeは、ファイル間の依存関係と日付を比較して、ファイルを再コンパイルする必要があるかどうかを判断します)。通常のバッチプログラム(Windowsの.batプログラムなど)を処理し、一部のファイルを更新した後、すべてのファイルを再コンパイルする必要があります。
「GNUソフトウェア/ GNUプログラミングによるプログラミング」
「make / makeプロジェクト開発ツールを使用したプロジェクトの管理」
上記の2冊の本には、makeの使用に関する詳細な説明があります。
(1)makefileの重要な概念
- ターゲット:生成するファイルの名前。
- 依存関係:2つのファイル間に依存関係があるかどうかを定義します。
- 前提条件(必須ファイル):ターゲットを作成できるファイル。ターゲットは通常、複数のファイルによって作成されます。
- 最新(新しいバージョン):ファイルが依存するファイルよりも新しいと仮定すると、ファイルのバージョンが新しいことを意味します。
makefileの基本的な構文:
#文件名:sample.mak
Target:Dependency list
command1
command2
上記のmakefileを実行するコマンドは次のとおりです。make -f sample.mak
-fでmakefileが指定されていない場合、makeは現在のディレクトリで「makefile」という名前のファイルを検索します。また、targetが指定されていない場合、makeはmakefileの最初のターゲットファイル名をターゲット名として使用します。
(2)makefileの例
- 次の例は、makefileでのマクロ定義の例です。
#File Name : DEFINE.MAK
#
#定义其它makefile中会用到的宏,思想和C语言的#define一样
#
!IFNDEF _DEFINE_MAK
_DEFINE_MAK = DEFINED
#
#定义项目相关文件所在的磁盘机编号
#
PRJ_DRIVER = Y:
#
#定义项目工具所在目录
#
PRJ_TOOLS_ROOT = $(PRJ_DRIVER)\Tools
#
#定义编译器所在目录
#
GNU_CC = $(GNU33_ROOT)\kcc33
GNU_LK = $(GNU33_ROOT)\ld
GNU_AR = $(GNU33_ROOT)\ar
#
#定义项目程序所在目录
#
SRC_ROOT = $(PRJ_DRIVER)\Project2020
SRC_INC = $(SRC_ROOT)\include
#
#当编译时传入-DXXX参数,其效果如同在程序中写了#define XXX
#
PRJ_CONFIG = -DPRJ_2020 -DCPU_ARM9 -DLCD_160X160
#
#定义执行C compiler时的参数
#
?CFLAGS= -c -gstabs -mlong-calls -fno-builtin -mgda=0 -mdp=1 -O3
-I$(GNU_INCLUDE)
-I$(SRC_INC)
-I$(PRJ_CONFIG)
#
#定义执行linker时的参数
#
LDFLAGS= -T main.lds -Map $(TARGET).map -N
#...
#...
!ENDIF
- 次の例は、より複雑な例です。
#
#在makefile中,也可以和include一样,包含其它makefile
#
!IF "$(_DEFINE_MAK)" == ""
!INCLUDE DEFINE.MAK
!ENDIF
#
#定义各模块包含的object file,每个object都是一个target
#
MODEL1_OBJS = m1_001.obj m1_002.obj m1_003.obj
MODEL2_OBJS = m2_001.obj m2_002.obj
#
# 项目中所有需要的object file
#
OBJS = $(MODEL1_OBJS) $(MODEL2_OBJS)
#
#定义会用到的库函数
#
LIBS = $(GNU_LIB)\libgcc.a
#
#第一个target产生最终可执行文件main.elf,
#和main.elf有依赖关系的target有:所有的object file,main.mak,Link Script
#"$@"表示target本身,即main.elf
#
main.elf : $(OBJS) main.mak main.lds
$(GNU_LK) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
#
# $* 表示target名称去掉扩展名
# $@ 表示target本身
# $< 表示XXX.c
#
m1_001.obj : $(SRC_ROOT)\m1\m1_001.c $(SRC_INC)\m1.h
$(GNU_CC) $@ $(CFLAGS) $*.c
m1_002.obj : $(SRC_ROOT)\m1\m2_001.c $(SRC_INC)\m1.h
$(GNU_CC) $@ $(CFLAGS) $*.c
...
- 同じ拡張子を持つ複数のファイルを繰り返し処理する必要がある場合は、通常、makeのプリセットコンパイルルールを使用できます。たとえば、同じルールで拡張子.objのすべてのターゲットをコンパイルする必要がある場合は、次のステートメントを使用できます。
?.c.obj:;$(GNU_CC) $@ $(CFLAGS) $<
プリセットコンパイルルールの構文の説明:
.c.obj:;このディレクトリ行は、ターゲットを.objファイルとして標準化し、.cファイルの事前設定されたコンパイルルールに依存するために使用されます。
- 事前コンパイルルールを設定するときにマクロを引き続き使用できます
(3)非ファイル名のターゲット
clean:
del $(OBJS)
del main.elf
del main.bin
上記のmakefileステートメントにはターゲットのみがあり、依存関係はありません。つまり、ターゲットは以下のdelコマンドを確実に実行します。通常、すべてのファイルを再コンパイルする前に実行するために使用されます。
この非ファイル名ターゲットは、他のターゲットの依存関係としても使用できます。これは、ターゲットを作成するときに最初に一連の命令を実行するために使用されます。
build_all : clean
...
...
(4)バージョン管理
システムが正式にリリースされる前に、プログラムコードには間違いなくデバッグ用のコードが多数含まれています。しかし実際には、組み込みシステムのストレージリソースが限られているため、デバッグコードを含むプログラムを最終コードとしてボードに書き込むことはできません。したがって、設計時には、通常、2つのバージョン(デバッグバージョンとリリースバージョン)が設計されます。もちろん、プログラムの開発時に、これらのデバッグコードを1つずつ手動で削除することはできません。現時点では、C言語での条件付きコンパイルのアイデアを使用できます。次の分析を参照してください:
デバッグバージョンのバッチファイル:make_debug.bat
REM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
REM Make_debug.bat
REM~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
REM 设定Windows/DOS的系统环境变量
REM
set BUILD_MODE = Debug
REM make我们的程序
REM
make target
リリースバッチファイル:make_release.bat
REM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
REM Make_release.bat
REM~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
set BUILD_MODE = Release
make target
makefile:
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#makefile
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
!IF "$(BUILD_MODE)" == "Debug"
# 如果BUILD_MODE等于“Debug”,则设定编译时期的参数CFLAGS_DEBUG = __DEBUG_VERSION
# 反之,则设定编译时期的参数为空
#
CFLAGS_DEBUG = -D__DEBUG_VERSION
!ENDIF
target:
gcc $(CFLAGS_DEBUG) xxx.c
#-D参数用来在编译时期设定宏变量“__DEBUG_VERSION”
4、リンクスクリプト
実行可能ファイルを作成するプロセスでは、最初にすべてのプログラムファイルをオブジェクトファイルにコンパイルする必要があります。次のステップは、リンカーリンカーを介してすべてのオブジェクトファイルとライブラリファイルを実行可能ファイルにリンクすることです。リンク方法と接続先のアドレスは、拡張子が.ldのリンクスクリプトファイルで指定されます。
オペレーティングシステムの場合、異なるプログラムには独自のアドレススペースがあり、相互に干渉しません。このようなプログラムはすべてRAM(メモリ)で実行され、すべてのプログラムは同じ開始アドレスから接続する必要があるだけです。ただし、組み込みプログラムにはオペレーティングシステムがない場合が多く、システムとプログラムは通常同じアドレススペースにあり、ハードディスクがないことがよくあります。プログラムはROMまたはフラッシュでのみ実行できます。ただし、データはRAMでのみアドレス指定できるため、接続するときは、プログラムセグメントをアドレス指定する場所(ROMの開始アドレス)とデータセグメントをアドレス指定する場所(RAMの開始アドレス)をリンカーに通知します。 。
(1)プログラムセクションの構造
- テキストセグメント:コードセグメント。テキストセグメントの内容は実行中に変更されません。メモリにロードせずにROMで直接実行できます。
- 読み取り専用データ(rodata)セクション:constとして定義された変数と文字列は、rodataセクションに分類されます。、これもROMで直接実行されます。
- データセクション:初期値を持つグローバル変数は、このセクションに配置されます。接続期間中、これらの初期値は実行可能ファイルに追加する必要がありますが、RAMアドレスにアドレス指定する必要があります;実行期間中、これらの変数はROMに保存されますが、使用するためにRAMにロードする必要があります。の値は可変です。したがって、データセグメントはROMに追加されますが、RAMにアドレス指定する必要があります。
- Bssセクション:初期値のないグローバル変数は、bssセクションに分類されます。初期値がないため、接続時にRAMにアドレス指定されている限り、プログラムに追加する必要はありません。実行中のロードの問題はありませんでしたが、マシンのRESET後、システムはbssセグメント全体をアクティブにクリアしました。
(2)リンクスクリプトの内容
実行中のメモリ使用量:
- LMA(ロードメモリアドレス)与VMA(仮想メモリアドレス)
データはROMに配置されますが、実行時にRAMにロードする必要があります。ROM内のアドレス(最終ストレージアドレス)はLMAと呼ばれ、RAM内のアドレス(実行期間)はVMAです。
次の接続要件を持つリンクスクリプトを作成してみてください。
- システムにはROMがあり、その開始アドレスは0xC00000で、別のRAM、その開始アドレスは0です。
- 実行可能ファイルには、テキスト、rodata、およびデータセグメントが含まれています。テキストおよびrodataセグメントはROMで実行できるため、0xC00000にアドレス指定され、rodataはテキストの後に続きます。
- bssセグメントには初期値がないため、実行可能スペースまたはROMスペースを占有せず、RAM0の開始アドレスにアドレス指定されます。
- データセクションはより複雑で、その内容も実行可能ファイルに含まれている必要があり、実行中にRAMにロードされる必要があります。したがって、データのVMAはbssセグメントの後にRAMにあり、LVAはrodataセグメントの後にあります。
拡張:プログラムをより高速で実行する場合は、LMAをROMに配置するだけで、VMAがプログラムをRAMにアドレス指定し、実行前にROMからRAMにロードします。
/*********************************************************
Link Script sample
存储器地址配置:ROM起始地址(0xC00000),RAM起始地址(0)
输出ARM9机器码,可执行文件格式为elf32
**********************************************************/
OUTPUT_FORMAT("elf32-arm9")
OUTPUT_ARCH(arm9)
SEARCH_DIR(.);
SECTIONS
{
/*****************************************
定义text段,起始地址(VMA)从0xC00000开始,
若没有指定LMA,表示LMA起始地址同VMA。
*****************************************/
.text 0xC00000:
{
/* 定义变量__START_text,句号.表示当前的VMA,即0xC00000 */
__START_text = . ;
/* *(.text)表示将当前目录中所有的.text段加入到这个段*/
*(.text);
/* 定义变量__END_text,目前VMA应该是0xC00000加上所有.text段的size总和 */
__END_text = . ;
}
/*****************************************
定义rodata段,起始地址(VMA)从__END_text开始(跟在text段之后),
若没有指定LMA,表示LMA起始地址同VMA。
*****************************************/
.rodata __END_text :
{
__START_rodata = . ;
*(.rodata);
__END_rodata = . ;
}
/*****************************************
定义bss段,起始地址(VMA)从0开始,
若没有指定LMA,表示LMA起始地址同VMA。
*****************************************/
.bss 0x00000000:
{
__START_bss = .;
*(.bss);
__END_bss = .;
}
/* 定义可在程序中使用的变量__SIZE_BSS,表示bss段的大小。*/
__SIZE_BSS = __END_bss - __START_bss;
/*****************************************
定义data段,其LMA应该在ROM,而VMA在RAM。
所以,VMA跟在bss段后面,LMA跟在rodata段之后
*****************************************/
.data __END_bss : AT(__END_rodata)
{
__START_data = .;
*(.data);
__END_data = .;
}
/*定义变量__START_data_LMA,表示data段的起始地址*/
__START_DATA_LMA = LOADADDR(.data);
/* 定义可在程序中使用的变量__SIZE_DATA,表示data段的大小。*/
__SIZE_DATA = __END_data - __START_data;
/***********************************************
speed_up模块的VMA和LMA都是跟在data段之后,
它会被加到可执行文件中,但执行时要载入到RAM才能执行
**************************************************/
.text_speed_up __END_data : AT(__START_data_LMA + SIZEOF(.data))
{
__START_text_speed_up = .;
speed_up_main.o(.text);
speed_up_main.o(.rodata);
speed_up_main.o(.data);
__END_text_speed_up = .;
/* 为便于说明,假设该模块没有bss段*/
}
__START_text_speed_up_LMA = LOADADDR(.text_speed_up);
__SIZE_TEXT_SPEED_UP = __END_text_speed_up - __START_text_speed_up;
}
プログラムモジュール(speed_up)をより高速なメモリに転送して実行するためのコードは次のとおりです。
extern unsigned char * __START_text_speed_up;
extern unsigned char * __START_text_speed_up_LMA;
extern int __SIZE_TEXT_SPEED_UP;
void copy_data_section(void)
{
//一个字节一个字节的传输(性能较差)
int i;
unsigned char *dest = __START_text_speed_up;
unsigned char *src = __START_text_speed_up_LMA;
for(i=0; i<__SIZE_TEXT_SPEED_UP; i++)
dest[i] = src[i]
}
bssセグメントに0に割り当てられたコードは次のとおりです。
extern unsigned char * __START_bss;
extern int __SIZE_BSS;
void clear_bss_section(void)
{
int i;
unsigned char * dest = __START_bss;
for(i=0; i<__SIZE_BSS;i++)
dest[i] = 0;
}
(3)マップファイルまたはシンボルテーブル
リンク後に実行可能ファイルを生成することに加えて、通常、プロジェクト内のすべてのシンボル(プログラム内のすべての関数、ライブラリ関数、グローバル変数、およびリンカー)を記録するために使用されるマップファイル(GNUリンカー「ld」の-mパラメーター)を生成する必要があります。各セクションの開始アドレスと終了アドレスの自動生成された変数のLMAとVMAの間の対応する関係。以下の情報は、マップファイルから取得できます。
- プログラムの各セクションのアドレス指定が正しいかどうか。
- プログラムの各セクションのサイズ、つまりROMとRAMの使用量。
- プログラム内の各シンボルのアドレス。
- メモリ内の各シンボルの順序
- 各プログラムファイルのストレージ使用量
接続が完了したら、実行可能ファイルを実際のストレージにダウンロードする前に、通常、マップファイルをチェックして、各セクションの開始アドレスとサイズが独自の想定を満たしていることを確認する必要があります。次の図は、マップファイルの部分的なスクリーンショットです。
5、ROMメーカー
実行可能ファイルにはさまざまな形式(ELF、COFF、HEX、S-Record、EXEなど)がありますが、ボードROMに書き込まれるバイナリファイルはバイナリファイルであるため、実行可能ファイルを取得したら、ROMメーカーで変換する必要があります。プログラムできるのはバイナリファイルのみです。もちろん、組み込みシステムには通常ハードディスクがないため、実行可能なバイナリファイル以外のファイルも一緒に完全な単一のバイナリファイル(イメージファイル)に変換する必要があります。このプロセスはmakeROMと呼ばれます。具体的なプロセスを次の図に示します。
バイナリ実行可能ファイルに加えて、画像ファイルに追加されるファイルは通常、画像(JPGファイルなど)です。一般的な方法は、画像ファイルをバイト単位の定数C配列に変換し、名前を付けることです。 、プログラムでは、メモリを直接操作することでこれらのデータを使用できるため、ファイルシステムの使用を回避できますが、スケーラビリティが低く、更新が面倒です。したがって、これらの画像ファイルが多い場合は、ファイルシステムを使用して管理する方が便利です。
実際、私たちは通常、ディレクトリ内のすべてのファイルをCアレイプログラムに変換するツールを開発し、同時に、データマイニングと各アレイを表すすべてのCアレイ宣言を含む.hヘッダーファイルが生成されます。サイズ、使用時に.hファイルを含める限り、これらの配列を使用できます。
- ファイルシステムイメージ
実際、ファイルシステムは、データにアクセスするための単なるインターフェイスであり、データストレージ、階層編成、アクセス、取得などの操作を実装する一連の抽象的なデータタイプです。ファイルシステムが保存されている場所と使用する形式が可能です。システムにファイルを書き込む必要がない場合でも、ファイルシステムをROMに保存できます。
組み込みシステムでは、データを書き込む必要がない場合は、通常、インデックステーブルを使用して、ファイル名とその最初のアドレスおよびファイルサイズをメモリに記録します。検索の便宜のために、インデックステーブルは通常最も高いアドレスに配置されます。
6、オフラインツール
開発フェーズで使用され、PCで開発および実行する必要があるツールは、オフラインツールと呼ばれます。これらのツールは、次の6つのカテゴリに分類できます。
-
プログラムジェネレーター(プログラム全般)
- システム構成設定ツール:PC上で実行され、システム構成(特定の機能スイッチ、LCD解像度など)を選択し、.hファイルまたはmakeファイルを自動的に生成するために使用されるツール。
- リソースマネージャー:システムに追加される文字列、グラフィック、またはデータファイルはリソースです。たとえば、UIインターフェイスに使用される小さなグラフィックファイルはC配列に変換してプログラムに追加する必要があり、他のプログラムはリソースIDを介して配列に対応します。このツールを使用すると、リソースファイル名を編集するだけで、すべてのリソースID定義を含む.hファイルとこれらのリソースコンテンツのC配列を含む.cファイルを自動的にエクスポートできます。
- データメーカー:一般的な組み込みシステムはmySQLなどのデータベースを使用できないため、アプリケーションの特性に従って設計する必要があります。
- ファイルシステム:厳密に言えば、ファイルシステムもデータベースですが、その単位はレコードではなくファイルです。
- データベース:いわゆるデータベースには、基本的にデータファイルと複数レベルのインデックステーブルが含まれています。CPUの計算能力が限られているという前提の下で、データベース形式と圧縮アルゴリズムの設計は特に重要です。
- 製品の組み込みファイル:たとえば、MP3組み込みの音楽や電子書籍の組み込みの本など、ユーザーが削除できないファイルは、ユーザーが編集できるファイルと区別する必要があります。これには、複数のファイルストレージ領域または複数のファイルシステムが含まれます。
- 製品情報またはデフォルトの工場設定:頻繁に変更される情報(製造元、日付など)については、プログラムに書き込まず、ファイルまたはデータベースに保存してシステムを実行する傾向があります。その中の情報を取得するだけです。
- イメージメーカー:その機能は、最後にメモリに書き込まれるイメージを作成することです。この画像には、プログラムだけでなく、製品情報、ファイルシステム画像、データベースなども含まれています。
- ダウンロードツール:バーナーを使用してイメージをメモリに書き込むことに加えて、部分的なダウンロード用のダウンロードツールを提供する必要もあります。これは、ファイルシステム内の一部のプログラムまたは一部のファイルのみを更新する場合があるためです。
- 大量生産ツール:メーカーの一部の情報(メーカー名、バッチ番号、日付など)は、書き込み前にしか決定できません。現時点では、メモリの書き込みに進む前に、この情報を画像の特定の場所に書き込むためのツールをメーカーに提供する必要があります。
- エミュレータ
- その他のツール
7.ダウンロードして実行します
厳密に言えば、いわゆるROMを書き込むことはできません。大量生産の前に、私たちが提供する画像ファイルに従って、専門のMask-ROMメーカーに1回限りの書き込みを委託する必要があります。開発段階では、NORフラッシュ、EPROM、EEPROMなどの他の繰り返し可能な代替手段を選択する必要があります。データを書き込むには、一般的にいくつかの方法があります。
- 最初にICEを使用して、実行とテストのためにRAMにダウンロードします。
- バーナー:まずメモリのチップをバーナーのソケットに置き、次にPCを使用してメーカーが提供する書き込みプログラムを操作し、画像ファイルを選択して書き込みを実行します。(開発段階のボードのメモリは、最初にソケットを介してボードに接続するように設計されています。焼き付けたICをソケットに取り付けてクランプするだけです)
- ROM-Emulator:このツールは、EPROM / EEPROMをエミュレートするためのもので、その一端はボードのソケットに接続され、他端はPCに接続されます。メーカー提供のプログラムにより、ROMエミュレータのメモリに画像ファイルをダウンロードすることができ、本物のROMと同じように本機をエミュレータに接続します。
- 更新プログラム:実際のボードを何らかの方法(USB、RS232、ネットワークケーブル)でPCに接続してデータを送信できる場合は、PC側で画像ファイルの内容を受信してNORフラッシュに書き込む更新プログラムモジュールを開発できます。このようにして、マシン上のプログラムバージョンを更新する機能が完了します。
8.バージョン管理
組み込みシステムであろうと一般的なソフトウェアプロジェクトであろうと、それが複数人の共同開発を伴う限り、バージョン制御を行う必要があります。ソフトウェア開発が特定のマイルストーンに到達するか、大きな進歩を遂げた場合、管理者は現在のバージョンに名前を付けることができます。これをラベルまたはタグと呼び、誰でも特定の時点でバージョン制御サーバーからすべてのプログラムをダウンロードできます。
システムが新しい機能を開発する必要がある場合は、ブランチを確立してそのブランチで開発できます。成功したら、マスターブランチとマージします。