12

ちょっとややこしい質問です。しかし、私は本当に低レベルのプログラミングを学びたいと思っています。問題は、Arduino/Etc のような開発ボードです。何が起こっているのかを本当に隠します。

私はコンピュータ アーキテクチャ、ロジック/ゲート/シーケンシャル ロジックなどについて学ぶことに時間を費やしてきました. CMOSトランジスタなどを使用してゲートがどのように作られているかなど)。

しかし、それはそれがどこで終わるかについてです....そして、命令(16進数/またはアセンブリ/などのコードなど)が可能な限り単純なコンピューター(私が使用した多くの本が行きましたGates のようなものからコンピュータへとまっすぐに....その中間はありません)。レジスタやメモリの場所に値を保存するような単純なものでさえ(そしておそらくピクセルに印刷する?など)。

最終的にエミュレーターを作成することでさえ、興味深いものになると思います。私は高水準言語の経験がありますが、多くのアセンブリを使用し、命令セットがそれほど大きくないため、6502 のようなものが良いスタートになるかもしれないと聞きました。

役立つリソース/考え/本を知っている人はいますか? 私は「Elements of Computing Systems」を読んだことがありますが……良い本ですが、実際に何が起こっているのか、それが起こっているのかを見ていないとは思えません。これは Electronics.stackexchange の質問かもしれません。もしそうなら、お詫び申し上げます。

4

6 に答える 6

6

概要

あなたには本当にたくさんの選択肢があります。命令がどのように翻訳されるかについての私の見解を要約しますが、翻訳を始めたときに私が持っていたいくつかのオプションも提供します。

私のテイク

まず、バイナリ入力の観点から考えるのが最も簡単です。16 ビットのマイクロプロセッサがあるとします。(つまり、命令は 16 バイナリ ビットでエンコードされます。) 数値をレジスタに配置するアセンブリ操作 SET を考えてみましょう。例えば:

SET(R1, 12) // Stores 12 into register 1

任意に (標準的なアーキテクチャであっても、選択は本当に任意であるため)、SET 命令が次の 16 ビット バイナリ値 I に変換されるように選択します。

0001 0001 0000 1100

基本的に、私はちょうどコンベンションを作り上げました。しかし、これが私がそれを分解する方法です。ビットI [15:12] (ビッグ エンディアン表記で表される) が特定の命令を表すようにすることにしました。整数 1を命令SETに対応させることにしました。ここで、その規則を決定しました。SET 命令がある場合、ビット I[11:8] をレジスタに対応させると言えます。(明らかに、これは 16 個のレジスタしかないことを意味します: 4^2=16)。最後に、ビット I[7:0] を、特定のレジスタに格納するデータに対応させます。SET(R1, 12) をバイナリでもう一度見てみましょう (わかりやすくするために、4 つの各グループを改行で区切ります)。

if I =  0001 0001 0000 1100
I[15:12] = 0001 (binary) = 1 (decimal) = SET instruction
I[11:8] = 0001 (binary) = 1 (decimal) = R1 since I[15:12] correspond to SET.
I[7:0] = 0000 1100 (8-bit binary) = 12 (decimal) = value to store in R1.

ご覧のとおり、マイクロプロセッサ内の他のすべては非常に単純になります。RAMに4行の命令を保存するとしましょう。時計にカウンターが取り付けられています。カウンターは、R​​AM 内の行をカウントします。時計が「時を刻む」と、ラムから新しい命令が出てきます。(つまり、次の命令が RAM から出てきます。ただし、JUMP ステートメントを挿入すると、多少恣意的になる可能性があります。) RAM の出力は、複数のビット セレクターを通過します。ビット I[15:12] を選択し、それらをコントロール ユニット (CLU) に送信します。CLUは、伝達しようとしている命令を通知します。つまり、SET、JUMP などです。次に、見つかった命令に応じて、レジスタの書き込みを許可するか、レジスタの追加を許可するか、アーキテクチャに含めるものを選択できます。

ありがたいことに、マシン命令のバイナリ値の任意の規則が既に選択されています (それらに従う場合)。これは、まさに命令セット アーキテクチャ (ISA) によって定義されているものです。たとえば、MIPSHERAなどです。明確にするために、回路などを設計するときに作成する実際の実装をマイクロアーキテクチャと呼びます。

学習リソース

テキスト

Harris と Harris の本は、学部のコンピューター アーキテクチャ コース向けの最も有名なテキストの 1 つです。とてもシンプルで使いやすいテキストです。全体は、ランダムな学校によって無料で PDF で入手できます。(すぐにダウンロードしてください!) とても役に立ちました。基本的な回路、離散数学のトピックについて説明し、第 7 章に到達するまでに、マイクロプロセッサの構築は簡単です。その本を読んでから、16 ビット マイクロプロセッサを完成させるのに 3 日ほどかかりました。(確かに、私は離散数学のバックグラウンドを持っていますが、それはそれほど重要ではありません。)

別の非常に役立つ非常に標準的な本は、ヘネシーとパターソンの本で、ランダムな学校から PDF 形式でも入手できます。(すぐにダウンロードしてください!)ハリスとハリスの本は、この本に基づいて簡略化されたものです。この本はもっと詳しく書かれています。

オープン ソース マイクロプロセッサ

そこにはオープンソースのマイクロプロセッサがたくさんあります。最初のマイクロプロセッサを構築するときに、それらを参照できることは非常に役に立ちました。Logisim ファイルを含むものは、それらをグラフィカルに表示し、クリックしてそのようにいじることができるため、特に楽しくプレイできます。以下に、私のお気に入りのサイトと特定の mp をいくつか示します。

4 ビット:

16 ビット:

Open Cores - このサイトはよくわかりません。私はアカウントを申請しましたが、彼らは実際には戻ってきていません... 大ファンではありませんが、あなたがアカウントを持っているなら、それは素晴らしいに違いないと思います.

ツール

ロジシム

前述したように、Logisimは優れたリソースです。レイアウトは完全にグラフィカルであり、ワイヤを選択することで、いつでもビット単位で何が起こっているかを非常に簡単に確認できます。それは Java で書かれているので、どんなマシンでも動作すると確信しています。また、グラフィカル コンピューター プログラミング言語の興味深い歴史的展望でもあります。

シミュレーション

Logisim では、実行中の実際のソフトウェアをシミュレートできます。ターゲットとする ISA にバイナリをコンパイルするコンパイラがある場合は、バイナリ ファイルまたは hex ファイルを Logisim RAM にロードしてプログラムを実行するだけです。(コンパイラがない場合でも、4 行のアセンブリ プログラムを作成し、それを自分で翻訳することは可能であり、すばらしい練習になります。) シミュレーションは、プロセス全体の中で最もクールで最も満足のいく部分です! :D Logisim は、これをよりプログラム的に行うためのCLIも提供します。

HDL

マイクロ アーキテクチャを生成/設計する最新の形式は、ハードウェア記述言語 (HDL) を使用することです。最も有名な例には、Verilog と VHDL が含まれます。これらは多くの場合 (紛らわしいことに!)、Ada や C/C++ などの逐次言語をモデルにしています。ただし、モデル/設計の検証がより適切に定義されるため、これははるかに好ましい設計方法です。私の意見では、グラフィックで調べるよりも、テキスト表現について推論する方がはるかに簡単です。プログラマーがコードをうまく整理できないのと同じように、ハードウェア開発者は、マイクロアーキテクチャのグラフィック デザインのグラフィック レイアウトをうまく整理できません。(ただし、この議論は確かに HDL に適用できます。) グラフィックよりもテキストで文書化する方が簡単であり、一般的に HDL を使用してよりモジュール的に設計する方が簡単です。

これを学ぶことに興味がある場合は、HDL を使用して回路とマイクロアーキテクチャを記述する方法について議論し、学習する、オープン カリキュラムと実習を伴う学部ハードウェア コースがたくさんあります。これらはグーグルで簡単に見つけることができます。または、次のステップ (C/C++ コードを HDL に変換するツール) を通じて HDL の学習を試みることもできます。興味があれば、Icarus Verilogは、Verilog 用の優れたオープン ソース コンパイラおよびシミュレータです。

シミュレーション

Icarus Verilog などのツールを使用すると、バイナリから実行される実際のプログラムを簡単にシミュレートすることもできます。バスを介してファイルまたは文字列を RAM にロードする別の Verilog スクリプトでマイクロプロセッサをラップするだけです。簡単です!:D

HLS

近年、高位合成 (HLS) も市場で大きな足場を築いています。これは、C/C++ コードを実際の回路に変換することです。既存の C/C++ は (常にではありませんが) ハードウェアに変換できるため、これは非常に驚くべきことです。

(すべての C/C++ コードが合成可能であるとは限らないため、必ずしもそうとは言えません。回路では、ビット ストリームはどこにでも一度に存在します。ソフトウェアでは、コードはシーケンシャルであると考えられます。ハードウェアを設計する!!)

しかし、ご想像のとおり、この機能は、行列演算や一般的な数学など、ハードウェア上のコードの特定の側面を最適化するのに非常に優れています。ただし、HLS ツールを使用して、内積の C 実装 (たとえば) がどのように HDL に変換されるかを確認できるため、これはあなたにとって重要です。個人的には、これは素晴らしい学習方法だと感じています。

シミュレーション

HLS シミュレーションは、高レベル コードが HDL に変換されるだけなので、HDL のシミュレーションと同じくらい簡単です。次に、上で説明したとおりにシミュレートしてテストを実行できます。

于 2014-06-25T14:35:54.970 に答える
5

これは、非常に単純な 6502 命令セット シミュレータのフレームワークの例です (6502 について複数回言及したため)。それは単純な 6502 プログラムから始まり、これらが私が実演しようとしている唯一の命令ですが、このようなプログラムでも理解を得ることができ、何かが動作するのを見てすぐに満足することができます.

   and #$00
   ora #$01
 top:
   rol
   bcc top
   and #$00

はい、シミュレーターを正しく起動していないことは十分承知しています。このバイナリは、xa65 アセンブラ (apt-get install xa65) を使用してアドレス 0 に基づいていると想定しています。アセンブル後のバイナリは次のとおりです。

hexdump -C a.o65 
00000000  29 00 09 01 2a 90 fd 29  00                       |)...*..).|
00000009

これはシンプルで縮小された命令、シミュレーターです

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE *fp;

#define MEMMASK 0xFFFF
unsigned char mem[MEMMASK+1];

unsigned short pc;
unsigned short dest;
unsigned char a;
unsigned char x;
unsigned char y;
unsigned char sr;
unsigned char sp;
unsigned char opcode;
unsigned char operand;

unsigned char temp;

int main ( void )
{
    memset(mem,0xFF,sizeof(mem)); //if we execute a 0xFF just exit

    fp=fopen("a.o65","rb");
    if(fp==NULL) return(1);
    fread(mem,1,sizeof(mem),fp);
    fclose(fp);

    //reset the cpu
    pc=0; //I know this is not right!
    a=0;
    x=0;
    y=0;
    sr=0;
    sp=0;
    //go
    while(1)
    {
        opcode=mem[pc];
        printf("\n0x%04X: 0x%02X\n",pc,opcode);
        pc++;
        if(opcode==0x29) //and
        {
            operand=mem[pc];
            printf("0x%04X: 0x%02X\n",pc,operand);
            pc++;
            printf("and #$%02X\n",operand);
            a&=operand;
            if(a==0) sr|=2; else sr&=(~2);
            sr&=0x7F; sr|=a&0x80;
            printf("a = $%02X sr = $%02X\n",a,sr);
            continue;
        }
        if(opcode==0x09) //ora
        {
            operand=mem[pc];
            printf("0x%04X: 0x%02X\n",pc,operand);
            pc++;
            printf("ora #$%02X\n",operand);
            a|=operand;
            if(a==0) sr|=2; else sr&=(~2);
            sr&=0x7F; sr|=a&0x80;
            printf("a = $%02X sr = $%02X\n",a,sr);
            continue;
        }
        if(opcode==0x2A) //rol
        {
            printf("rol\n");
            temp=a;
            a<<=1;
            a|=sr&0x01;
            sr&=(~0x01); if(temp&0x80) sr|=0x01;
            if(a==0) sr|=2; else sr&=(~2);
            sr&=0x7F; sr|=a&0x80;
            printf("a = $%02X sr = $%02X\n",a,sr);
            continue;
        }
        if(opcode==0x90) //bcc
        {
            operand=mem[pc];
            printf("0x%04X: 0x%02X\n",pc,operand);
            pc++;
            dest=operand;
            if(dest&0x80) dest|=0xFF00;
            dest+=pc;
            printf("bcc #$%04X\n",dest);
            if(sr&1)
            {
            }
            else
            {
                pc=dest;
            }
            continue;
        }
        printf("UNKNOWN OPCODE\n");
        break;
    }
    return(0);
}

その単純なプログラムのシミュレータ出力。

0x0000: 0x29
0x0001: 0x00
and #$00
a = $00 sr = $02

0x0002: 0x09
0x0003: 0x01
ora #$01
a = $01 sr = $00

0x0004: 0x2A
rol
a = $02 sr = $00

0x0005: 0x90
0x0006: 0xFD
bcc #$0004

0x0004: 0x2A
rol
a = $04 sr = $00

0x0005: 0x90
0x0006: 0xFD
bcc #$0004

0x0004: 0x2A
rol
a = $08 sr = $00

0x0005: 0x90
0x0006: 0xFD
bcc #$0004

0x0004: 0x2A
rol
a = $10 sr = $00

0x0005: 0x90
0x0006: 0xFD
bcc #$0004

0x0004: 0x2A
rol
a = $20 sr = $00

0x0005: 0x90
0x0006: 0xFD
bcc #$0004

0x0004: 0x2A
rol
a = $40 sr = $00

0x0005: 0x90
0x0006: 0xFD
bcc #$0004

0x0004: 0x2A
rol
a = $80 sr = $80

0x0005: 0x90
0x0006: 0xFD
bcc #$0004

0x0004: 0x2A
rol
a = $00 sr = $03

0x0005: 0x90
0x0006: 0xFD
bcc #$0004

0x0007: 0x29
0x0008: 0x00
and #$00
a = $00 sr = $03

0x0009: 0xFF
UNKNOWN OPCODE

完全な 6502 命令セットは、ゼロから作成する場合、長い週末の最小限の作業に値するものです。プロジェクトを数回再起動することになる可能性があるため、完全に自然です。プロセッサ用のハードウェア (一般に、必ずしも 6502 である必要はありません) は、シミュレーターで発生するものと概念が大きく異なるわけではありません。命令を取得し、命令をデコードし、オペランドをフェッチし、実行し、結果を保存する必要があります。ソフトウェアでこれを行うのと同じように、ハードウェアでも、より速くしたり小さくしたり、目的が何であれ、興味深い方法を作成できます。

全体を実装する場合、6502 は依然として大きなプロジェクトであり、z80 ほど大きくはありませんが、risc16 のようなものは、シミュレーター全体を理解して記述するのにおそらく 30 分かかります (その後、アセンブラーを作成するのにさらに 30 分かかります)。pic12、14、または 16 は risc16 よりも手間がかかりますが、それほど多くはなく、すぐに使いこなすことができ、デザインがいかにシンプルであるかを学ぶことができます。pdp11 と msp430 は間違いなく何らかの形で関連しており、どちらも十分に文書化されており (私が言及したものはすべて十分に文書化されています)、適切に/ほとんど直交しており、デコードのような risc は 6502/z80/x86 のような cisc とは異なる経験です。(pdp11 は gcc/gnu ツールによってネイティブにサポートされています)。分岐遅延スロットの周りでアルゴリズムを操作できれば、Mips は非常に単純です。

頑張ってください、楽しんでください...

于 2013-04-19T00:57:57.387 に答える
5

独自の単純な CPU を作成してみてください。見た目ほど難しくありません: LOGISIM

于 2013-04-18T12:24:38.060 に答える
3

ゲートから完全に機能するコンピューター、さらにはその先へと一歩一歩進むテキストの 1 つは、Noam Nisan と Shimon Schocken によるThe Elements of Computing Systems 2005 ISBN 9780262640688 です。

于 2013-04-18T17:29:48.507 に答える
0

The Art of Assembly Languageの最初の数章では、ラッチを使用してビットを物理的に格納する方法について説明し、データのバイトを保持するレジスタを組み立てるためにこれらを使用する方法と、左/右シフトなどの命令をハードウェアで実装する方法について説明します。おそらくあなたが探しているほど深くはありませんが、これを読んだときは本当に目が開かれました.

于 2013-05-14T22:12:42.680 に答える