[8位二進制CPU的設計和實現] CPU微機架構的實現


CPU微機架構的實現

本文是對B站UP躊躇月光出的8位二進制CPU的設計和實現的文字教程復現第二部分 CPU微機架構的實現
相關 github 地址:https://github.com/StevenBaby/computer
PS:有錯誤的地方請指正,謝謝!共同學習,一起進步!

ALU和半自動加法機

參考工程:16 ALU和半自動加法機

回顧第一部分做的加法運算器,封裝一下即得到ALU(算術邏輯單元(arithmetic and logic unit))

  • A,B是兩個8位數輸入,做相加運算用
  • OP是選擇做加法還是減法,為1時做減法,為0時做加法
  • O是相加輸出,CO是進位輸出

半自動加法機

通過ALU三位計數器ROM寄存器我們可以組成一個半自動加法器,如下所示

其中

  • 三位計數器用來遍歷ROM里所有地址的數據
  • ALU負責做加法運算
  • 寄存器暫存加法運算后的數據

計算步驟

  • 上電之后,點擊左上角按鍵,計數器和寄存器都被置為0。接着我們打開計數器使能,寄存器寫使能,給寄存器一個時鍾信號,將ROM里0地址的數寫到加法器的A輸入口。

  • 然后再給計數器一個時鍾信號,讀出ROM中1地址的數據。ALU經過加法運算后輸出到寄存器的輸入。

  • 再給寄存器一個時鍾信號,讓計算結果再寫到A

  • 依次循環,即可實現半自動加法。

全自動加法機

參考工程:17 全自動加法機

上面我們做出來一個半自動加法機,現在我們嘗試實現一個全自動加法機,不需要我們手動去給時鍾信號。

  • 首先我們創建一個自動化總線:可以控制啟動和停止,並提供時鍾信號的一個總線

我們需要兩個開關和兩個按鍵:

  • POW:啟動開關,1啟動,0關閉
  • MAN:手動時鍾開關,1手動提供時鍾,0自動時鍾(測試用)
  • RES:復位按鍵,按下之后總線提供一個復位信號
  • PUL:手動時鍾按鍵,通過該按鍵手動提供時鍾信號(測試用)

同時提供一個停止輸入時鍾輸入復位輸出時鍾輸出

  • HLT:停止信號輸入,提供一個輸入口讓外部提供一個停止信號,時鍾信號就被關閉
  • CLOCK:內部提供一個時鍾輸入,沒有接口
  • RES:一個復位輸出信號,給總線上的器件一個復位信號(初始化或者復位時使用)
  • CP:時鍾輸出,給總線上器件提供時鍾信號

總線布局如下:

分析一下兩個信號輸出:時鍾輸出復位輸出

  • 時鍾輸出

用文字語言描述就是:有兩種提供時鍾信號的方式,分別是時鍾信號手動脈沖信號,受MAN的控制,前提是電源打開,沒有復位,沒有停止信號。

  • 復位輸出

    復位信號的條件是:1、電源沒有打開 2、電源打開的同時按下復位信號

然后我們將前面的半自動加法器移植到這里,構成全自動加法機

運行步驟

ROM里預置數據

  • 啟動仿真,RES復位一直拉高,計數器和寄存器復位(清零)

  • 按下POW,啟動全自動加法機,此時時鍾信號打開,復位信號關閉(為了方便觀察,使用手動信號),按下PUL輸入,第一個上升沿,寄存器執行寫操作,將ROM的0地址數據寫到A輸入口;松開PUL,第一個下降沿,對應計數器的第一個上升沿,計數器+1,ROM地址+1,讀出ROM的1地址數據。

  • 依次類推,關閉手動時鍾信號,開啟自動時鍾信號,全自動加法機自動完成運算(讀到FF數據停止運算)

程序計數器和內存

參考工程:18 程序計數器和內存

這一節我們來實現一個程序計數器,來支持后面微控制器程序的運行,程序計數器和前面的8位計數器其實差不多。

但是因為要支持跳轉指令,所以要原來的8位計數器需要做個改造,加一個預置數的功能,所以其實程序計數器本質是一個寄存器加上+1的功能。

程序計數器實現

  • EN:三態門使能,在輸入輸出端分別加上三態門用來隔離總線;EN=1輸出(計數),EN=0輸入(預置數)

功能測試

  • 上電(POW打開),程序計數器CS=1WE=1寫使能,EN=1計數使能,程序計數器(PC)從零開始增加

  • 預置數6,打開三態門,數據被送到總線上,EN=0程序計數器預置數功能打開,程序計數器預置數6

  • 打開PC計數功能,計數器從6開始自增,打開預置數器的總線隔離,總線上數據6消失

通過程序計數器讀取內存

前面說道,程序計數器的作用就是用來讀取程序,一般程序都放在RAM里,所以這里我們利用程序計數器來讀取一下RAM里的數據。

  • 設置RAM為隨機數,打開仿真,先看一下RAM里的隨機數

  • 選擇手動脈沖輸入(MAN打開),初始,讀出RAM的0地址數據B

  • 打開計數器計數功能(CS=WE=EN=1),RAM的總線隔離打開(CS=WE=1),手動輸入一個脈沖,則讀出第一個數據23(但是並沒有讀到總線上,關掉總線隔離即可讀到總線上)

  • 現在要指定讀RAM的5地址的數據,查的是85,讀取步驟:打開RAM總線隔離(CS=WE=1)->打開計數器預置數功能(CS=WE=1,EN=0)->關閉預置數(提前設置為5)總線隔離->給一個時鍾脈沖

微程序控制

參考工程:19 微程序控制

!!!注意:此章是本部分最重要的內容

前面做了全自動加法機程序計數器兩個重要的功能,其中程序計數器PC是程序運行的一個重要部分,他的功能就是順序讀取RAM或者ROM里的數據(計數器自增)和跳轉功能,那么本節就是來講如何將對寄存器以及其他器件的控制轉化ROM中的數據(即程序)。

CPU的控制分為兩種,分布是微程序控制硬布線微程序控制是較為簡單的一種,我們這里就介紹這種,相信介紹完之后,大家會對硬布線的理解也會更加深刻。

考慮實現這樣一個功能:將RAM中0地址和1地址的數據相加,結果放到2地址中。

首先,我們需要改造一下ALU,將他也連到總線上

功能描述:將RAM中0地址和1地址的數據相加,結果放到2地址中。

硬件設計

要完成上面的功能,需要完成以下操作

  • RAM里讀寫數據
  • 需要額外的三個寄存器來作為中間量做運算使用
  • 需要一個ALU單元做加法運算
  • 需要一套取指系統(從ROM讀取程序)
  • 控制總線(要通過這個控制總線來控制所有的器件)
  • 數據總線(所有器件的數據交換需要通過這個總線)

我們一個個來解決:

  • RAM里讀寫數據

使用前面搭建的PC程序計數器讀取RAM電路

  • 需要額外的三個寄存器來作為中間量做運算使用、一個ALU單元做加法運算

A寄存器中數據加上B寄存器中數據送到C寄存器中

  • 需要一套取指系統(從ROM讀取程序)

  • 控制總線(要通過這個控制總線來控制所有的器件)、數據總線(所有器件的數據交換需要通過這個總線)

指令設計

重點分析一下控制總線:

控制總線總共16

  • 寄存器AWE接0位,CS接1位
  • 寄存器BWE接2位,CS接3位
  • 寄存器CEW接4位,CS接5位
  • ALU單元OP接6位,EN接7位
  • RAM總線隔離WE接8位,CS接9位
  • PC計數器WE接10位,EN接11位,CS接12位
  • 程序停止HLT的接15位

其實根據我們前面的經驗可以知道,運算步驟其實就是控制這些位的通斷,那么根據這個原理我們設計一下該套系統的指令

WE_A = 2 ** 0  # 寄存器A的WE位接0位,使能為1
CS_A = 2 ** 1  # 寄存器A的CS位接1位,使能為1

WE_B = 2 ** 2  # 寄存器B的WE位接2位,使能為1
CS_B = 2 ** 3  # 寄存器B的CS位接3位,使能為1

WE_C = 2 ** 4  # 寄存器C的WE位接4位,使能為1
CS_C = 2 ** 5  # 寄存器C的CS位接5位,使能為1

ALU_ADD = 0    # ALU單元的OP接6位,設為0 加法
ALU_SUB = 2 ** 6 # ALU單元的OP接6位,設為1 減法
ALU_OUT = 2 ** 7 # ALU單元的EN接7位,使能為1

WE_M = 2 ** 8  # RAM的總線隔離WE位接8位,使能為1
CS_M = 2 ** 9  # RAM的總線隔離CS位接9位,使能為1

WE_PC = 2 ** 10 # PC計數器的WE位接10位,使能為1
EN_PC = 2 ** 11 # PC計數器的EN位接11位,使能為1
CS_PC = 2 ** 12 # PC計數器的CS位接12位,使能為1

HLT = 2 ** 15   # 程序停止HLT位接15位,使能為1 程序停止

程序分析

要完成將RAM中0地址和1地址的數據相加,結果放到2地址中這樣一個任務,我們分析一下需要如何做。

  • 首先將RAM0地址的數據取出送到A寄存器
  • 然后將RAM1地址的數據去除送到B寄存器
  • 通過ALU加法運算過后的數據送到C寄存器
  • 最后將計算過后的數據(在C寄存器里)送到RAM2地址
  • 程序結束

詳細動作

  • 首先將RAM0地址的數據取出送到A寄存器
    RAM輸出總線隔離關閉(CS=1,WE=0),寄存器A的寫打開(CS=WE=1),完了之后PC計數器打開計數器功能(WE=EN=CS=1)
    翻譯為機器語言就是CS_M | CS_A | WE_A | WE_PC | EN_PC | CS_PC(0001 1110 0000 0011b=1e 03h)

  • 然后將RAM1地址的數據去除送到B寄存器
    RAM輸出總線隔離關閉(CS=1,WE=0),寄存器B的寫打開(CS=WE=1),完了之后PC計數器打開計數器功能(WE=EN=CS=1)
    翻譯為機器語言就是CS_M | CS_A | WE_A | WE_PC | EN_PC | CS_PC(0001 1110 0000 1100b=1e 0ch)

  • 通過ALU加法運算過后的數據送到C寄存器
    ALU運算單元設置為加法使能(OP=0,EN=1),然后將寄存器C的寫打開(CS=WE=1)
    翻譯為機器語言就是ALU_SUB | ALU_OUT | CS_C | WE_C(0000 0000 1011 0000b=00 b0ch)

  • 最后將計算過后的數據(在C寄存器里)送到RAM2地址
    寄存器C的讀打開(CS=1,WE=0),RAM的寫打開(CS=1,WE=1),完了之后PC計數器打開計數器功能(WE=EN=CS=1)(也可以不用)
    翻譯為機器語言就是CS_C | WE_M | CS_M | WE_PC | EN_PC | CS_PC(0001 1111 0010 0000b=1f 20ch)

  • 程序結束
    HLT置1
    翻譯為機器語言就是HLT(1000 0000 0000 0000b=80 00ch)

總結所有的指令為

    CS_M | CS_A | WE_A | WE_PC | EN_PC | CS_PC,
    CS_M | CS_B | WE_B | WE_PC | EN_PC | CS_PC,
    ALU_ADD | ALU_OUT | CS_C | WE_C,
    CS_C | WE_M | CS_M | WE_PC | EN_PC | CS_PC,
    HLT

編譯下載

將指令轉換為二進制文件

import os

dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, 'ins.bin')


WE_A = 2 ** 0  # 1
CS_A = 2 ** 1  # 1x

WE_B = 2 ** 2  # 1xx
CS_B = 2 ** 3

WE_C = 2 ** 4  # 1xx
CS_C = 2 ** 5

ALU_ADD = 0
ALU_SUB = 2 ** 6
ALU_OUT = 2 ** 7

WE_M = 2 ** 8
CS_M = 2 ** 9

WE_PC = 2 ** 10
EN_PC = 2 ** 11
CS_PC = 2 ** 12

HLT = 2 ** 15

micro = [
    CS_M | CS_A | WE_A | WE_PC | EN_PC | CS_PC,
    CS_M | CS_B | WE_B | WE_PC | EN_PC | CS_PC,
    ALU_ADD | ALU_OUT | CS_C | WE_C,
    CS_C | WE_M | CS_M | WE_PC | EN_PC | CS_PC,
    HLT,
]

with open(filename, 'wb') as file:
    for value in micro:
        result = value.to_bytes(2, byteorder='little')
        file.write(result)
        print(value, result)

print('Finish compile!!!')

將二進制文件導入到ROM中

運行測試

提前往RAM里寫入兩個數據

啟動程序

可以步進跟蹤程序,也可以改一改指令。

邏輯運算

參考工程:20 邏輯運算

ALU除了支持加法減法,還要支持,乘法除法(可以用加法減法來實現),邏輯運算

這一節來實現一下邏輯運算中的異或的八位邏輯運算

  • 8位運算 8AND

  • 8位運算 8OR

  • 8位運算 Invert
    第一部分已實現過,即8位反轉

  • 8位異或運算 8XOR

程序狀態字

參考工程:21 程序狀態字

ALU擴充

然后我們將以上的功能添加到ALU上,同時將運算過后的輸出狀態輸出到程序狀態字PSW


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM