MC68302 ユーザーマニュアル(私家版) 1.概要

11月 30 2022

はじめに

このマニュアルは私が独自に解釈して作成した私家版のユーザーマニュアルです。

内容の正確性については一切保証しませんので、実際の使用に当たっては1次ソースを必ず参照してください。

必要ないと思ったペリフェラル、箇所については翻訳を省いています(特にCPのプロトコル部分)


第1章 概要

MC68302 統合通信プロセッサ(以下IMP)は多種多様な制御装置に必要な主要機能を組み込んだ超大規模集積回路(VLSI)です。このデバイスは特に通信業界のアプリケーションに適しています。

IMPは密に結合された業界標準であるM68000マイクロプロセッサと柔軟な通信アーキテクチャを提供する最初のデバイスです。IMPは複数の一般的な業界インターフェイス(ISDNベーシックレートやターミナルアダプタを含む)をサポートするように構成できます。異なるプロトコルの同時動作は、アーキテクチャやプログラマブルな機能を組み合わせることにより容易に実現することができます。データコンセントレータ、ラインカード、モデム、ブリッジ、ゲートウェイなどがこのデバイスに適したアプリケーション例です。

IMPはM68000マイクロプロセッサコア、システムインテグレーションブロック(以下SIB)、コミュニケーションプロセッサ(CP)から構成される高密度相補型金属酸化膜半導体(HCMOS)デバイスです。

1.1 ブロック図

ブロック図を図1-1(準備中)に示します。

マイクロプロセッサコアとシリアルポート(CP内)、システムペリフェラル(SIB内)を統合することによって、IMPはすべてのISDN基本レート(2B+D)アクセスタスクのような複雑なタスクを処理することが可能です。例えば、IMPアーキテクチャとシリアル通信コントローラ(SCC)ポートは、S/Tトランシーバチップのインターフェイスと、下位部分(ビット操作)のISO/OSI第2層をサポートすることができます。そのほかの第2層機能および上位プロトコル層はM68000コアで実行されるソフトウェアで実装されます。

IMPの柔軟なメモリベース・バッファ構造を使用して、3つのSCCポートとシリアル通信ポートの間でデータバッファ情報を変換・共有することにより、ターミナルアダプタにも応用可能です。

各SCCチャンネルは HDLC/SDLC1、UART、BISYNC、DDCMP2、V.110、またはトランスペアレント動作に対応します。IMPは、様々なレート適応技術に対して多くの選択肢を提供し、ターミナルコントローラ、マルチプレクサ、コンセントレータなどの機能として使用することができます。

MC68302は、ローカル制御バスとX.25パケットネットワークを利用したリアルタイム制御を行うボードレベルの産業用コントローラなどのアプリケーションにも使用できます。このようなシステムでは、要求の厳しい周辺機器にリアルタイムで対応しながら、X.25パケット・ネットワークを介したリモート・モニタリングや通信を可能にします。

1.2 主な特色

IMPの特色を以下に示します。

  • 8/16ビットM68000ファミリシステムをサポートする音チップHCMOS MC68000/68008コア

  • システムインテグレーションブロック

      - 3つのハンドシェイク信号(/DREQ,/DACK,/DONE)を持つ独立DMA(IDMA)コントローラ
      - 2つの動作モードを持つ割り込みコントローラ
      - パラレルI/Oポート(一部は割り込みが使用可能)
      - オンチップの1152バイトデュアルポートRAM
      - ウォッチドッグタイマーを含む3つのタイマ
      - ウェイト・ステート・ジェネレータを含む4本のプログラマブル・チップ・セレクト
      - 出力可能なオンチップ・クロック・ジェネレータ
      - システム制御
          - 低レイテンシ・割り込み対応バス調停ロジック
          - システムステータスおよび制御ロジック
          - CPU無効化ロジック(M68000)
          - ハードウェアウォッチドッグ
          - 低消費電力(スタンバイ)モード
          - デバッグ用フリーズ制御
          - DRAMリフレッシュコントローラ
    
  • コミュニケーションプロセッサ

          - メインコントローラ(RISCプロセッサ)
          - 独立した3つの全二重シリアル通信コントローラ(SCC)
          - 各種プロトコルをサポート
              - HDLC/SDLC(High-Level/Synchronous Data Link Control)
              - 汎用非同期送受信機(UART)
              - バイナリ同期受信(BISYNC)
              - 同期/非同期デジタルデータ通信メッセージプロトコル(DDCMP)
              - トランスペアレントモード
              - V.110レート適応
          - 3つのSCCのための6つのシリアルDMAチャネル
          - SCCからアクセス可能な柔軟な物理インターフェイス
              - モトローラインターチップデジタルリンク(IDL)
              - 一般回路インターフェイス(GCI/IOM-2)
              - パルス符号変調(PCM)ハイウェイインターフェイス
              - 非多重シリアルインターフェイス(NMSI)
              - 標準モデムの実装
                  - 同期通信のためのSCP
                  - IDLとGCIをサポートする2つのシリアルマネージメントコントローラ(SMC)
    

1.3 MC68302システムアーキテクチャ

(工事中)

MC68000でCを書く

11月 28 2022

はじめに

とりあえず今の時点で上手く行った方法を書いてますが、適当に更新していくつもりです。

今回はgcc9.5.0をビルドしてMC68000でCのコードを走らせる方法を書いていきます。

gccのビルド

このレポジトリの内容に従うだけです。

最初はCygwinでやろうと思いましたが途中でコケたのでMSYS2を導入してビルド。

1
2
3
4
5
6
7
8
9
admin@LUNAEDGE-X260 MSYS ~
$ m68k-elf-gcc -v
Using built-in specs.
COLLECT_GCC=m68k-elf-gcc
COLLECT_LTO_WRAPPER=/opt/m68k-elf/libexec/gcc/m68k-elf/9.5.0/lto-wrapper
Target: m68k-elf
Configured with: /home/admin/m68k-elf-toolchain/projects/gcc/configure --prefix=/opt/m68k-elf --target=m68k-elf --enable-languages=c,c++,lto --enable-version-specific-runtime-libs --disable-libssp --disable-nls --disable-threads --disable-libmudflap --disable-libgomp --disable-libstdcxx-pch --with-gnu-as --with-gnu-ld --with-newlib --with-headers=/home/admin/m68k-elf-toolchain/projects/newlib-cygwin/newlib/libc/include/ --disable-shared --src=../../projects/gcc
Thread model: single
gcc version 9.5.0 (GCC)

こんな感じになります(PATHもちゃんと通しておく)。

スタートアップスクリプト

ここから先はKiさんに助言を乞いながら作成しました。

この場を借りて感謝申し上げます。

今回は極力アセンブラを使わずにスタートアップルーチンを記述します。

割り込みベクタも最小限しか書きません。

最低限動くコードがこちら

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

#include <stdint.h>

extern uint32_t __stack_top;

static void _init_board(void);
static void _reset_handler(void);

const volatile uintptr_t vectors[]
__attribute__((section(".isr_vector"))) = {
/*Reset: Initial SSP*/
(uintptr_t)(&__stack_top),
/*Reset: Initial PC*/
(uintptr_t)(&_reset_handler)
};

static void _init_board(void){
/*Do nothing*/
}

static void _memcpy(uint8_t* dst,uint8_t* src,size_t size){
while (size--) {
*dst++ = *src++;
}
}

static void _memclr(uint8_t* dst,size_t size){
while (size--) {
*dst++ = 0;
}
}

static void _reset_handler(void){
_init_board();

/*Xfer Init Value*/
extern uint8_t __data_top,__data_bottom,__text_bottom;
size_t size = (size_t)(&__data_bottom - &__data_top);
_memcpy(&__data_top,&__text_bottom,size);

/*Memory Clear*/
extern uint8_t __bss_bottom,__bss_top;
size = (size_t)(&__bss_bottom - &__bss_top);
_memclr(&__bss_top,size);

/*Call main()*/
main();

while(1);
}

スタートアップルーチンでは

  • ボードの初期化
  • 変数の初期値設定
  • main(エントリポイント)へのジャンプ

を行います。

ここで、main関数は別ファイルでextern宣言されています。

MC68kのベクタは0番にSSP初期値、1番にPC初期値が入ります。

リンカスクリプト

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/*
Ricochet用Bootloader リンカスクリプト
2022.11.23 c0ntra1l
*/

/*
メモリマップ(Bootloader実行時)
0x000000 - 0x7FFFFF Flash*
0x100000 - 0x13FFFF RAM(データ)
0x200000 - 0x23FFFF RAM(プログラム)

メモリマップ(ユーザープログラム実行時)
0x000000 - 0x13FFFF RAM(プログラム)*
0x100000 - 0x23FFFF RAM(データ)

*:実行するプログラム
*/

MEMORY {
vec (r) : ORIGIN = 0x000000, LENGTH = 0x400
rom (xr) : ORIGIN = 0x000400, LENGTH = 512K - 0x400
ram (rw) : ORIGIN = 0x100000, LENGTH = 256K
}

SECTIONS {
/*ベクタ*/
.isr_vector : {
__rom_top = .;
__rom_vec = .;
} > vec

/*.textセクション*/
.text : {
__text_top = .;
*(.text*)
__text_bottom = .;
*(.rodata*)
} > rom

/*.dataセクション*/
.data :
{
__data_top = .;
*(.data)
__data_bottom = .;
} > ram

/*.bssセクション*/
.bss :
{
__bss_top = .;
*(.bss)
*(COMMON)
__bss_bottom = .;
__stack_top = .;
} > ram
}

メモリ配置を定義します。hoge_topとhoge_bottomはスタートアップルーチンで転送(初期化)するメモリの領域計算に使用します。

.dataセクションが初期値あり変数、.bssセクションが初期値なし変数、.textがプログラム、.rodataが初期値あり変数の初期値が入る領域のようです。

Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
prefix		= m68k-elf-
CC = $(prefix)gcc
AS = $(prefix)as
LD = $(prefix)ld
OBJCOPY = $(prefix)objcopy
OBJDUMP = $(prefix)objdump
RM = rm

PROJECT = r68k_bootloader
PROGRAM = $(PROJECT).elf
TARGET_S = $(PROJECT).s68
TARGET_B = $(PROJECT).bin

CFLAGS = -g -O2 -m68000 -Wall -fomit-frame-pointer
LDFLAGS = -Map $(PROJECT).map -T ricochet.lds -nostdlib

MAKEFILE = Makefile

OBJS = main.o startup.o

LIBS =

all: $(TARGET_B) $(TARGET_S)

$(TARGET_B): $(PROGRAM)
$(OBJCOPY) -O binary --remove-section=.data $(PROGRAM) $(TARGET_B)
@echo '-------------------------------------------------------------------'
@echo " $(TARGET_B) build succeeded. [`date`]"
@echo ''

$(TARGET_S): $(PROGRAM)
$(OBJCOPY) -O srec --remove-section=.data $(PROGRAM) $(TARGET_S)
$(OBJDUMP) -D $(PROGRAM) >$(PROGRAM).dasm
$(OBJDUMP) -h $(PROGRAM)
@echo '-------------------------------------------------------------------'
@echo " $(TARGET_S) build succeeded. [`date`]"
@echo ''

$(PROGRAM): $(OBJS) $(LIBS)
$(LD) $(LDFLAGS) -o $(PROGRAM) $(OBJS)

%.o: %.c
$(CC) $(CFLAGS) -c $<

clean:
$(RM) -f $(PROGRAM)
$(RM) -f $(TARGET_S)
$(RM) -f $(TARGET_B)
$(RM) -f *.o

tar:
cd .. && tar czvf $(PROJECT)-`date '+%Y%m%d'`.tar.gz $(PROJECT)

今回はシミュレータでのテスト用にbinファイルだけでなくSレコードも出力しています。
また、gccのオプションに-fomit-frame-pointerを指定し、フレームポインタを使用しないようにしています。

テストプログラム

1
2
3
4
5
6
7
8
9
10
11
12
13
#include "startup.h"
#include <stdint.h>

extern signed main(void);

uint8_t array[128];

signed main(void){
for(uint8_t i = 0;i < 128;i++){
array[i] = i;
}
return 0;
}

コンパイルできることをチェックするために適当なプログラムをでっちあげました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
admin@LUNAEDGE-X260 MSYS /c/Users/Admin/Documents/Bootloader_68k
$ make all
m68k-elf-gcc -g -O2 -m68000 -Wall -fomit-frame-pointer -c main.c
m68k-elf-gcc -g -O2 -m68000 -Wall -fomit-frame-pointer -c startup.c
startup.c: In function '_reset_handler':
startup.c:47:2: warning: implicit declaration of function 'main' [-Wimplicit-function-declaration]
47 | main();
| ^~~~
m68k-elf-ld -Map r68k_bootloader.map -T ricochet.lds -nostdlib -o r68k_bootloader.elf main.o startup.o
m68k-elf-objcopy -O binary --remove-section=.data r68k_bootloader.elf r68k_bootloader.bin
-------------------------------------------------------------------
r68k_bootloader.bin build succeeded. [Mon Nov 28 08:01:45 JST 2022]

m68k-elf-objcopy -O srec --remove-section=.data r68k_bootloader.elf r68k_bootloader.s68
m68k-elf-objdump -D r68k_bootloader.elf >r68k_bootloader.elf.dasm
m68k-elf-objdump -h r68k_bootloader.elf

r68k_bootloader.elf: file format elf32-m68k

Sections:
Idx Name Size VMA LMA File off Algn
0 .isr_vector 00000008 00000000 00000000 00002000 2**1
CONTENTS, ALLOC, LOAD, DATA
1 .text 00000064 00000400 00000400 00002400 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .bss 00000080 00100000 00100000 00004000 2**2
ALLOC
3 .debug_info 0000149d 00000000 00000000 00002464 2**0
CONTENTS, READONLY, DEBUGGING
4 .debug_abbrev 000004b9 00000000 00000000 00003901 2**0
CONTENTS, READONLY, DEBUGGING
5 .debug_loc 00000116 00000000 00000000 00003dba 2**0
CONTENTS, READONLY, DEBUGGING
6 .debug_aranges 00000040 00000000 00000000 00003ed0 2**0
CONTENTS, READONLY, DEBUGGING
7 .debug_ranges 00000028 00000000 00000000 00003f10 2**0
CONTENTS, READONLY, DEBUGGING
8 .debug_line 00000336 00000000 00000000 00003f38 2**0
CONTENTS, READONLY, DEBUGGING
9 .debug_str 0000054d 00000000 00000000 0000426e 2**0
CONTENTS, READONLY, DEBUGGING
10 .comment 00000011 00000000 00000000 000047bb 2**0
CONTENTS, READONLY
11 .debug_frame 00000048 00000000 00000000 000047cc 2**2
CONTENTS, READONLY, DEBUGGING
-------------------------------------------------------------------
r68k_bootloader.s68 build succeeded. [Mon Nov 28 08:01:45 JST 2022]

make allを実行するとこのようになります。

Warningが出ていますが、コレはアセンブラを使わないようにするためにasm("jmp main")のかわりにmain()としたためです。

逆アセンブルを見てみると

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

r68k_bootloader.elf: file format elf32-m68k


Disassembly of section .isr_vector:

00000000 <vectors>:
0: 0010 0080 orib #-128,%a0@
4: 0000 0418 orib #24,%d0

Disassembly of section .text:

00000400 <main>:
400: 41f9 0010 0000 lea 100000 <__data_bottom>,%a0
406: 4200 clrb %d0
408: 10c0 moveb %d0,%a0@+
40a: 5200 addqb #1,%d0
40c: 0c00 ff80 cmpib #-128,%d0
410: 66f6 bnes 408 <main+0x8>
412: 7000 moveq #0,%d0
414: 4e75 rts
...

00000418 <_reset_handler>:
418: 203c 0010 0000 movel #1048576,%d0
41e: 0480 0010 0000 subil #1048576,%d0
424: 6718 beqs 43e <_reset_handler+0x26>
426: 43f9 0010 0000 lea 100000 <__data_bottom>,%a1
42c: 0680 0000 0464 addil #1124,%d0
432: 41f9 0000 0464 lea 464 <__text_bottom>,%a0
438: 12d8 moveb %a0@+,%a1@+
43a: b1c0 cmpal %d0,%a0
43c: 66fa bnes 438 <_reset_handler+0x20>
43e: 203c 0010 0080 movel #1048704,%d0
444: 0480 0010 0000 subil #1048576,%d0
44a: 6710 beqs 45c <_reset_handler+0x44>
44c: 41f9 0010 0000 lea 100000 <__data_bottom>,%a0
452: 4218 clrb %a0@+
454: b1fc 0010 0080 cmpal #1048704,%a0
45a: 66f6 bnes 452 <_reset_handler+0x3a>
45c: 4eb9 0000 0400 jsr 400 <main>
462: 60fe bras 462 <_reset_handler+0x4a>

Disassembly of section .bss:

00100000 <array>:
...

問題なくコンパイルできているようです。

いろいろ気になるところはありますが、とりあえず動いたのでよしとしましょう。

続きは実機で動かしてからですね。

シミュレータに突っ込む

S68をシミュレータに食わせてPCを0x000418にセット、走らせてメモリを覗いてみるとこんな感じになってます。

問題なさそうですね。

思った点

今回はMC68kのシミュレータにEASy68kを使いました。

なかなか使いやすくて良いのですが、いろいろ不満点もあります。

  • ベクタを読まない(開始アドレスが0x400、SSPが0x100000固定っぽい?)
  • S68だけでは逆アセンブルが表示されない(L68が必要らしい、どうすればいい?)

前者は直接PCを書き換えてリセットハンドラに飛べばいいのでまだなんとかなりますが、後者はかなり厳しいです。

今は横に逆アセンブルリストを出して、目視でデバッグをしていますが規模が大きくなるとそれすらも大変になりそうです。

なんとかならないものでしょうか…

おわり。

ブログ作った

11月 28 2022

とりあえずブログをちゃんと作ってみようということで。

今回は自分で書くのをやめてジェネレータを使っています。

まぁ結局テーマにゴリゴリ手を入れているので労力としてはそんなに変わんないです。

あとはCloudflareのデプロイサービスを使ってみたり。

そんなこんなで近代的になっています。