從與或非門開始構(gòu)建一個(gè)計(jì)算機(jī)的教程(寫給軟件工程師)一

準(zhǔn)備

作為一個(gè)軟件工程師,計(jì)算機(jī)是我們賴以生存的工具,所有我們開發(fā)的軟件都在計(jì)算機(jī)上執(zhí)行,那么通過(guò)與或非等最基礎(chǔ)的門電路構(gòu)建一個(gè)計(jì)算機(jī)一方面可以更深入的了解計(jì)算機(jī),一方面也可以更好的開發(fā)軟件。

基于門電路從頭開始當(dāng)然可以,但連接電路比較費(fèi)勁,所以這里采用了 verilog + Fpga 的形式。verilog + Fpga 本質(zhì)就是自己設(shè)計(jì)連接電路,和自己從頭基于門電路連接一樣。

第一個(gè)項(xiàng)目來(lái)搭建開發(fā)環(huán)境和構(gòu)建最基礎(chǔ)的幾個(gè)電路。

硬件

  • tangnano 4k

硬件采用國(guó)產(chǎn)的 tangnano 而沒(méi)有采用國(guó)外大廠的。一方面為了支持國(guó)產(chǎn) IC 一方面中國(guó)是以制造業(yè)為主的國(guó)家、國(guó)產(chǎn)的 Fpga 性價(jià)比較高。

搭建環(huán)境

常見的 EDA 工具一般僅支持 windows 和 linux 不支持 macos,這里不對(duì)專有和開源軟件的優(yōu)劣做對(duì)比,以免引戰(zhàn)。在本教程中我盡量使用開源軟件而不使用廠家的專有軟件。

# 安裝綜合的工具
brew install yosys
pip install apycula
brew install eigen

# 安裝 gowin 的布線工具
git clone git@github.com:YosysHQ/nextpnr.git
cmake . -DARCH=gowin
make -j$(nproc)
sudo make install
# 參考 https://github.com/YosysHQ/nextpnr#nextpnr-gowin

# 安裝刷入 Fpga 的工具
brew install openfpgaloader --HEAD

# 安裝編譯成可仿真的文件
brew install icarus-verilog

# 安裝查看波形的軟件
brew install --cask gtkwave

綜合和布線其實(shí)就是生成電路,和物理的連接電路一樣,具體的 Fpga 的原理請(qǐng)參考 https://zh.wikipedia.org/zh-tw/%E7%8E%B0%E5%9C%BA%E5%8F%AF%E7%BC%96%E7%A8%8B%E9%80%BB%E8%BE%91%E9%97%A8%E9%98%B5%E5%88%97 。

Nand

Nand 又稱為與非門,其他門一般通過(guò)它來(lái)構(gòu)建,是基本門電路,原因如下:

  1. 考慮電子元件的成本,也許從邏輯上來(lái)看與非門和或非門比與門和非門更復(fù)雜,但是實(shí)際上由于Mos管的物理結(jié)構(gòu),實(shí)現(xiàn)與非門和或非門需要的元件其實(shí)更少,成本更低,而簡(jiǎn)單的與門和或門其實(shí)在結(jié)構(gòu)上比前者更復(fù)雜。
  2. 或非門和與非門具有邏輯完備性,任何一個(gè)門通過(guò)組合可以實(shí)現(xiàn)任意電路,而與門和或門不具有這樣的能力
  3. 仍然和電子元件的物理結(jié)構(gòu)有關(guān),與非門和或非門實(shí)際運(yùn)行效率比與門和或門更高。

與非門的真值表:

A B A NAND B
0 0 1
0 1 1
1 0 1
1 1 0

非門是用 Nand(與非門)實(shí)現(xiàn)的,代碼如下:

/**
 * Not gate:
 * out = not in
 */
`include "Nand.v"
`default_nettype none

module Not(
    input in,
    output out
);
Nand NAND(.a(in), .b(1'b1), .out(out));
endmodule

非門顧名思義就是取反操作,0 轉(zhuǎn)成 1 , 1 轉(zhuǎn)成 0 。所以只要輸入和 1 與然后取反就可以了,利用與非門很容易做到。

或門利用非門和與非門實(shí)現(xiàn),a or b == not (not a and not b),如果想了解這個(gè)公式怎么來(lái)的,請(qǐng)查閱“德摩根定律”。

 /**
 * Or gate:
 * out = 1 if (a == 1 or b == 1)
 *       0 otherwise
 */
`default_nettype none
module Or(
    input a,
    input b,
    output out
);
    wire nota;
    wire notb;
    Not NOT1(.in(a), .out(nota));
    Not NOT2(.in(b), .out(notb));
    Nand NAND(.a(nota), .b(notb), .out(out));


endmodule

與門很簡(jiǎn)單,與非門的結(jié)果取反就行了。

/**
 * And gate: 
 * out = 1 if (a == 1 and b == 1)
 *       0 otherwise
 */

`default_nettype none

module And(
    input a,
    input b,
    output out
);
    wire notaandb;

// your implementation comes here:
    Nand NAND(.a(a), .b(b), .out(notaandb));
    Not NOT(.in(notaandb), .out(out));


endmodule

異或

異或門是第一個(gè)有挑戰(zhàn)的門電路,異或門的意義是兩個(gè)輸入不同結(jié)果為 1 ,w1 和 w2 分別斷言 a = 1, b = 0 和 a = 0 , b = 1 ,w1 or w2 是上述有一個(gè)成立結(jié)果就為 1。因?yàn)?a b 只有兩種取指,上述描述就覆蓋了所有情況。

/** 
* Xor (exclusive or) gate:
* If a<>b out=1 else out=0.
*/
`include "Not.v"
`include "And.v"
`include "Or.v"
`default_nettype none

module Xor(
    input wire a,
    input wire b,
    output wire out
);
    wire nota;      //new wire must be declared
    wire notb;
    Not NOT1(.in(a), .out(nota));    //NOT1 is instance name
    Not NOT2(.in(b), .out(notb));
    
    wire w1;
    wire w2;
    And AND1(.a(a),.b(notb),.out(w1));
    And AND2(.a(nota),.b(b),.out(w2));

    Or OR(.a(w1),.b(w2),.out(out));
endmodule

真值表如下:

A B A XOR B
0 0 0
0 1 1
1 0 1
1 1 0

激勵(lì)

所謂激勵(lì)和軟件工程師的單元測(cè)試差不多,以下激勵(lì)就是用來(lái)測(cè)試異或門的,很好理解。

a = ... b= ... 就是給 a b 賦值來(lái)測(cè)試在不同取值下的結(jié)果。結(jié)果通過(guò) display 寫入Xor.out 文件中,波形寫入 Xor_tb.vcd 中,待會(huì)介紹波形。

`include "Xor.v"
`default_nettype none
module Xor_tb();

    integer file;

    reg a = 0;
    reg b = 0;
    wire out;
    
    Xor XOR(
        .a(a),
        .b(b),
        .out(out)
      );

    task display;
        #1 $fwrite(file, "| %1b | %1b | %1b |\n", a,b,out);
    endtask
    
    initial begin
        $dumpfile("Xor_tb.vcd");
        $dumpvars(0, Xor_tb);
        file = $fopen("Xor.out","w");
        $fwrite(file, "| a | b |out|\n");
        
        a=0;b=0;
        display();
        
        a=0;b=1;
        display();
        
        a=1;b=0;
        display();
        
        a=1;b=1;
        display();
        $finish();  
    end

endmodule

編譯

編譯 Xor_tb.v,有編譯錯(cuò)誤在這步就會(huì)打印出來(lái)。

iverilog -o Xor_tb.vvp Xor_tb.v

仿真(模擬)

所謂仿真其實(shí)就是模擬,仿真其實(shí)就是模擬硬件,在不同的輸入(信號(hào))下展現(xiàn)不同的波形。

vvp sample_tb.vvp
open -a gtkwave

使用 gtkwave 查看波形,右鍵點(diǎn)擊Xor_tb ,選擇 a b out ,然后 append ,刪除內(nèi)部變量??梢钥吹疆?dāng) a b 不同結(jié)果為 1 (高電平)。

[圖片上傳失敗...(image-2cac4e-1654746740561)]

比較

*.cmp 是提供的比較文件, *.out 是我們執(zhí)行仿真產(chǎn)生的結(jié)果文件。二者應(yīng)該是相同的,采用這種方式來(lái)斷言我們的程序?qū)懙膶?duì)不對(duì)。

diff Xor.out Xor.cmp

如果程序?qū)懙膶?duì)什么也不輸出,否則輸出不同的地方。

上傳到 tangnano

綜合和布線

綜合和布線就是通過(guò) verilog 產(chǎn)生電路的過(guò)程。

yosys -p "read_verilog Xor.v; synth_gowin -json Xor.json"

綜合比較簡(jiǎn)單,布線需要引入所謂的約束文件:

IO_LOC "out" 10;
IO_LOC "a" 15;
IO_LOC "b" 14;

14 和 15 是 tangnano 的兩個(gè)按鈕,分別被綁定到了 a b ,10 是 led 燈。需要注意的是按鈕松開的時(shí)候是 1 ,按下是 0 。約束文件把程序中的變量綁定到了實(shí)際的物理硬件,改變物理量就改變了變量。

nextpnr-gowin --json Xor.json --write pnrXor.json --device GW1NSR-LV4CQN48PC6/I5 --cst tangnano4k.cst

最后生成二進(jìn)制文件。

gowin_pack -d GW1NSR-LV4CQN48PC6/I5 -o pack.fs pnrXor.json

寫入硬件

刷入 Fpga 。

openFPGALoader -b tangnano4k pack.fs

測(cè)試

實(shí)際按下按鈕試試。當(dāng)按鈕的狀態(tài)不同時(shí),結(jié)果為 1 ,反之結(jié)果為 0。完成了異或門。下面是視頻,點(diǎn)擊即可。

結(jié)果

<img src="https://tva1.sinaimg.cn/large/e6c9d24egy1h2qp7wk0ttj20c60iadh8.jpg" alt="" style="zoom:50%;" />

代碼:https://github.com/buhe/bugu-computer/tree/master/00

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容