国产毛片a精品毛-国产毛片黄片-国产毛片久久国产-国产毛片久久精品-青娱乐极品在线-青娱乐精品

查看: 86557|回復: 87
打印 上一主題 下一主題

跟著我從零開始入門FPGA

[復制鏈接]
跳轉到指定樓層
樓主
發表于 2012-5-12 00:22:06 | 只看該作者 |只看大圖 回帖獎勵 |倒序瀏覽 |閱讀模式
這是一個大任務,但我打算只是引門外漢入門,大約7個帖子來完成,一周入門FPGA

1、假設讀者對硬件數字電路熟悉,比如自己可以用74芯片做跑馬燈
2、C語言都比較熟悉,因為下面用的Verilog語言就跟它很類似,暫時規避晦澀的VHDL

我打算分幾個部分
1、Verilog語法
2、組合邏輯設計
3、時序邏輯設計
4、阻塞和非阻塞
5、同步和異步設計
6、有限狀態機
7、設計一個只有4條指令的CPU

評分

參與人數 2積分 +40 收起 理由
步從容 + 10 thx!
@︻$▅▆▇◤ + 30

查看全部評分

沙發
 樓主| 發表于 2012-5-12 00:22:29 | 只看該作者
1、Verilog語法


沒錯,我們就是拿C語言照貓畫虎,下面是一個“老虎”的模型。
我們一個個看他跟“貓”不一樣的地方

module nand(
            input   in1,
            input   in2,
            output   out
);

    wire    tmp;
    assign tmp = in1 & in2;
    assign out = ~tmp;

endmodule


模塊定義跟C語言的函數很相似吧
1、模塊必須使用“module”關鍵字,他也沒有返回值。
2、模塊沒有beginmodule,只有endmodule
3、模塊對外接口有input,output,inout,但為了入門著想,只談input和output

模塊內部還有個中間變量耶,是不是看見了tmp就有很熟悉的感覺了。
沒錯,他就是中間“變量”,在硬件上他就是一根導線,wire望文生義即可。

看見了“=”就應該猜到這是賦值語句了,沒錯,但Verilog的語法要求前面必須有個苦B的assign關鍵字

至于“&”和“~”這2個運算符號,就不講了吧,C語法搞不清的兄弟,對不住了


有人會說,你這“變量”到底是int還是long還是flot抑或double呢?
好了,咱繼續照貓畫虎,不過老虎畢竟跟貓是不一樣的,比如老虎會虎嘯,貓只會喵喵。

wire[7:0]   tmp;
這一下子把tmp從一根線,擴展成了8根線,覺得是7根線的自己去看C語言課本去。

好了,我們要虎嘯了,同時喵喵幾下,對比著看

wire[7:0]   tmp;
wire[3:0]   high;

assign high = tmp[7:4];     //虎嘯的Verilog
high = tmp<<4;              //喵喵的C語言

硬件就是硬件,可以隨意飛線,你甚至可以把tmp里面的bit6,bit3,bit1,bit7組成一個Nibble
不知道Nibble不要緊,它就是Half Byte的

assign high = {tmp[6],tmp[3],tmp[1],tmp[7]};        //虎嘯的Verilog

high  = (tmp & 0x40) ? 0x08 : 0;                //喵喵的C語言
high |= (tmp & 0x08) ? 0x04 : 0;                //喵喵的C語言
high |= (tmp & 0x02) ? 0x02 : 0;;               //喵喵的C語言
high |= (tmp & 0x80) ? 0x01 : 0;;               //喵喵的C語言

這下知道喵喵跟虎嘯的差距了吧,C語言,把如貓添翼?表達式都用上了,還是4行代碼才表達出自己的意圖。
當然,Verilog也有他的?表達式,那用上了,就真的是如虎添翼了

C語言的switch/case語句
switch(tmp)
{
    case 1:
        high =1;
        break;
    case 3:
        high =5;
        break;
    case 5:
        high =2;
        break;
    case 9:
        high =1;
        break;
    default:
        high =11;
}
Verilog的case語句
case(tmp)
    1:          high =1;
    2:          high =5;
    3:          high =1;
    4:          high =1;
    default:    high = 1;

發現了沒,首先打字要少敲很多case了吧,case已經升級當主管了,小羅羅們直接跟這冒號就可以了。
細心的文藝青年,應該發現了一個大秘密,那個四處張揚,到處留種的break居然不見了。
Verilog不需要break了,它默認每個語句自動break,這時有人又擔心,那我有2個語句咋辦?

問得好,又有2個keyword要粉末登場了,begin/end
學會Pascal語言的朋友,肯定認得他倆,在C語言中被{和}所替代

Verilog本來也想用{和}的,畢竟寫代碼是要敲鍵盤的,能少敲誰也不愿意多敲。
可惜{和}被用掉了,用在了哪里?到上面找去,

case(tmp)
    1,2,3,4:
    begin
        high =1;
        high1 =3;
        high8 =9;
    end
    default:
        high = 1;

這個排版,是不是又點更像C語言的風格了
你也許已經看到了,C語言中多個case項公用一段代碼的情況,在Verilog里面也有,而且更TMD的簡潔

if/else語句就不講了,這方面貓和老虎太像了,照貓畫虎就八九不離十了。


好了,下面有個用得非常多的always語句

always(tmp1, tmp2)
    begin
        out1 = tmp1 ^ tmp2;
        out2 = tmp1 + tmp2;
    end
又是喵喵和虎嘯的區別了,C語言的while也是always的意思,但while不如always忠誠。
C的while語句,是隨著CPU的時鐘節奏,一步一步的走,然后Loop循環回來,直到永遠或者有人叫她出臺(霸王的break或者while條件不滿足了)
Verilog的always可就忠誠多了,只要tmp1和tmp2中的任何一個變動,out1和out2都跟著動,clk來不來都會工作,這就是主動和被動的差別


好了,看到這里,你應該知道,文藝青年和苦B青年其實也有很多共同之處的,如果你認識文藝青年,那跟苦B青年交朋友也不難了。
板凳
 樓主| 發表于 2012-5-12 00:22:51 | 只看該作者
2、組合邏輯設計


2、組合邏輯設計

組合邏輯是神馬?
所謂組合邏輯就是,一堆輸入注定了一個(或多個)輸出,明天你再送同樣的這一堆輸入,可以得到跟今天完全相同的結果。
或者說,輸出的值跟先前任何狀態沒有一毛錢的關系,只跟當前的輸入有關系。

來個最簡單的:
assign out = in1 & in2;
這是個與門,out的值只跟in1和in2有關。

這時候?語句很有作用了,比如
assign out = sel ? in1 : in2;
這是一個二選一的選擇器。

你肯定覺得二選一太簡單了,來個4選一看看
assign out = (sel==2'b00) ? in0 : (sel==2'b01) ? in1 : (sel==2'b10) ? in2 : (sel==2'b11) ? in3;
不知道你感覺如何,反正我從第二個問號開始花眼,咋辦?
首先一個辦法,?表達式的復合,我們可以用括號來區分層次,但仍然感覺很不直觀。
想到了什么,C語言的switch/case,OK,我們就用Verilog的case語句寫一下

always @(sel or in0 or in1 or in2)
    case(sel)
        2'b00:  out = in0;
        2'b01:  out = in0;
        2'b10:  out = in0;
        2'b11:  out = in0;
    endcase

OK,我們看看這個case語句是什么?沒錯,他就是那個真值表的美麗化身。
怎么,你還想到了卡諾圖輔助邏輯表達式化簡,當年讀書時候,整天對著田字格橫看豎看的,很神奇的。

現在我們有Verilog語言了,化簡的事情交給綜合器好了。
啥,你不知道綜合器是啥?C語言的C編譯器,你知道吧,他倆基本是一個地位的。

always的小老鼠后面的括號里不是有很多“變量”嗎,那叫敏感信號。
只要敏感信號任何一個有變動,下面的語句就執行一次,其實這是個形象的說法,幾乎是專門給C語言工程師定制的一個解釋。

說到邏輯電路,我們找個非典型的用途吧-----地址譯碼
CPU就說是8051吧,其實其他的也一樣

我們有個外設,內部有8個寄存器,我們打算把它安排到地址0xF080~0xF0087,設計它的片選信號

比較笨的方法:
assign sel = (addr==16'hF080) | (addr==16'hF081) | ...............
我就不敲完了,8個啊,復制完了,還要一個個修改,還要校對,夠苦B的了

always @(addr)
    case(addr)
        16'hF080, 16'hF081, 16'hF082, 16'hF083, 16'hF084, 16'hF085, 16'hF086, 16'hF087:
            sel = 1;
        default:
            sel = 0;
    endcase
這個case語句簡單多了吧,16'hF080的含義就是此數據有16個bit,h表示后面是十六進制表示的。
F080你要不知道什么意思,估計你們學校是體育老師講計算機基礎課,當然也可能是政治老師。

其實,case里面連續寫8個項,然后一個冒號,感覺也很是苦B

那我們手工分析下0xF080~0xF0087的特征,高位的3個Nibble是F08,低位Nibble是0~7
我們再用二進制的方式看看:
1111 0000 1000 0000
1111 0000 1000 0001
1111 0000 1000 0010
1111 0000 1000 0011
1111 0000 1000 0100
1111 0000 1000 0101
1111 0000 1000 0110
1111 0000 1000 0111
---------------------------------------
1111 0000 1000 0xxx

這3個小x的含義我就不說了,這點歸納的邏輯都沒有的話,您真不適合做工程師,適合做公務員。

always @(addr)
    casex(addr)
        16'b1111_0000_1000_0xxx:
            sel = 1;
        default:
            sel = 0;
    endcase
是不是帥呆了,如果你不是太粗心的話,應該看到了這個是casex而不是case


其實帥不是目的,泡到妞才是根本。看一個assign語句就可以搞定的事,何必羅嗦這么多呢。
assign sel = (addr==16'b1111_0000_1000_0xxx);

估計你還看到了一個小東西,那下劃線。
沒錯,他就是個擺設,增加可讀性的擺設,去掉也可以,不過我舍不得去掉。


C語言里如果要判斷這個sel信號,一般用bit mask的方法
sel = ((addr&0xFFF8)==0xF0808);
看來照貓畫虎,這招也可以用在這里,雖然代碼稍微差異,思路近似。

關于case語句的一個注意事項,就是所謂的full-case,這個很重要
always @(addr)
    casex(addr)
        16'b1111_0000_1000_0xxx:
            sel = 1;
    endcase
那么,當地址不落在我們指定范圍內的時候,沒有語句來處理。
沒來命令,就是要原地駐軍。

其實相當于,
always @(addr)
    casex(addr)
        16'b1111_0000_1000_0xxx:
            sel = 1;
        default:
            sel = sel;
    endcase

天啊,sel=sel,這就是傳說中的意外的latch,這跟我們的意圖不一致
即使邏輯上可以完成指定功能,他也額外用掉了一個寄存器。

記住,用case語句的時候一定要full-case處理,除非你設計的就是寄存器

寄存器的latch是時序邏輯里面的內容
欲知后市如何,請聽下回分解。
地板
 樓主| 發表于 2012-5-12 00:23:03 | 只看該作者
3、時序邏輯設計

所謂時序邏輯,簡而言之,就是CLK驅動,不來時鐘不干活,同時能自我保持。
最簡單的例子,跑馬燈

model led_led(input rst, input clk, output out0, output out1, output out2, output out3);
reg ary[3:0];

assign out0 = ary[0];
assign out1 = ary[1];
assign out2 = ary[2];
assign out3 = ary[3];
always @(clk)
begin
    if(rst)
        ary <= 4'h00;
    else
    begin
        ary[3] <= ary[2];
        ary[2] <= ary[1];
        ary[1] <= ary[0];
        ary[0] <= ary[3];
    end
end
endmodele

有人會說那個ary的“中間變量”是不是可以省掉,還真省不了,因為它不僅僅是“臨時變量”。
它是個鎖存器,寄存器-----------數據保持是它的特點。而wire信號是無法保持的。

當然那4個信號單獨賦值,挺形象,但是不夠緊湊,C語言可以用ary = ary<<1來表示,那Verilog就如下:
model led_led(input rst, input clk, output out[3:0]);
reg ary[3:0];
assign out = ary;
always @(clk)
begin
    if(rst)
        ary <= 4'h00;
    else
        ary <= {ary[2:0],ary[3]};
end

跑馬燈,用組合邏輯做不出來,是因為它的輸出不僅僅跟輸入有關,也跟前一個狀態有關。

看到時序邏輯,很多人發現了無比強大的clk信號,到處都有他的影子,他才是真正的到處留種的風流才子。

“輸出不僅僅跟輸入有關,也跟前一個狀態有關”,這句話,應該很熟悉吧。 -----沒錯,就是狀態機。
后面有個專門貼講FSM,這是一個非常有效的解決問題的工具------智商99的人可以做到智商120的設計。
如果天才用了,那肯定要更加newbility了。

下面看個十進制的計數器,是前幾天給一個初學者的sample,但愿這小哥真的去看懂了,而不是搞完畢設就完蛋鳥。

    module bcd_counter(rst, clk, qout);
    input rst;
    input clk;
    output[7:0] qout;

    reg [3:0] low;
    reg [3:0] high;

    assign qout ={high,low};

    always @(posdage clk)
        if(rst)
            begin
                low <= 4'h0;
                high <= 4'h0;
            end
        else
            begin
                case(low)
                0,1,2,3,4,5,6,7,8:
                    low <= low+4'h1;
                9:
                    begin
                        low <= 4'h0;
                        case(high)
                            0,1,2,3,4,5,6,7,8:
                                high <= high+4'h1;
                            9:
                                high <= 0;
                        endcase
                    end
                endcase
            end
    end module  

這里面有2個十進制數據,分別占用一個nibble---4個bit,其實就是BCD碼。
使用了case語句來完成,而且兩個case還套起來用了,跟C語言一個樣,照貓畫虎即可。

細心的文藝青年,應該發現了,module的定義貌似跟以前不一樣了。
不用貌似,就是不一樣了,這是2種風格,其實苦B的C語言也有這個風格的。
bool fun1(X,Y)
int X, Y;
{
}
C語言的古老風格,現在很少人用了,畢竟多敲鍵盤,不代表多干活。
那時候C語言還沒有國際標準,此K&R C是以2個工程師的名字首字母命名的。
后來才有ANSI C和ISO C(C89/C90/C99),這巨人的肩膀好高啊,搞技術就不能有恐高癥。

Verilog亦是如此,建議使用輸入輸出定義在括號里面的風格,至少要少碼好幾個字母不是。


不知道有沒有人注意到,上面幾個帖子用的都是always @(posdage clk),敏感信號列表中不見rst的身影。
下面的判斷rst的信號,決定復位還是干活,這叫做同步復位。

同步復位,你要理解成rst一有效就復位就錯了。同步的含義,與CLK同步,類似于與襠中央保持一致。

異步復位,是立即的。異步復位,同步釋放。這個話題就比較遠了,后面有個帖子,會專門說這個話題。


后面的會稍微有些難度,但我會為了入門的學習臺階,盡量把難的趕跑,容易的留下-----“男的趕跑,女的留下”。


預知后市如何,明天再看紅盤綠盤。
地下室
 樓主| 發表于 2012-5-12 00:23:20 | 只看該作者
4、阻塞和非阻塞

話說大禹治水,因為他老爹治水失敗被咔咔了,他不得已去頂缸。
他也琢磨啊,其父也不是等閑之輩,沒搞定,說明必須得換個法子,否則自己也得被大哥給嗝屁了。
大禹父子治水,分別用的是阻塞和非阻塞的方法,下面我們就扯一下邏輯電路中的阻塞和非阻塞。

通常所說的阻塞和非阻塞,指的是always塊中的語句。
always語句中有時序邏輯,也有組合邏輯。前者用非阻塞,后者用阻塞。
其實“阻塞”這個術語,也是專門給軟件出身的電工看的,硬電工才懶得管你阻不阻的呢。

    reg[7:0] in1;
    reg[7:0] out;

    always @ (posedge clk)
    begin
        in1 <= in1+8'h01;
        out <= in1;
    end
endmodule


先從容易的下刀,我們先看看這個非阻塞的語句,它很簡單,就是in1的自身完成一個自加一
注意這個“<=”,是不是又想起了C語言里用來搞指針的“->”,不過真的沒有一毛錢的關系。

in1拿到的是clk上升沿之前的“in1”值再加1,跟clk上升沿之后的in1沒有關系了。


正如,已畢業的小明的時候對還在讀大四GF小芳承諾說,哥等你大學畢業就討你做LP了。
時光如箭,日月如梭,時間如白駒過隙,學校7月份小芳走出了象牙塔的大學校門。
小明履行承諾,娶小芳為妻。話說,無巧不成書。
小芳大學畢業了,但大三的同學也該升級讀大四了,正好里面也有個女娃的名字也叫小芳。
搶答開始,問:小明,娶的是哪個小芳?

答案是,去年讀大四今年畢業的那個小芳,而不是去年大三今年大四的那個小芳。
您感覺拗口嗎,反正我有點繞口令的感覺了。

非阻塞操作也是這個效果,你娶的是畢業(clk上升沿)之前的那個大四的小芳。

我們知道硬件是并行執行的,所以,上面的代碼,這么寫,效果一樣。
        in1 <= in1+8'h01;   //老小芳畢業,新小芳升級大四
        out <= in1;         //小明娶老婆
   


但,如果把非阻塞改為阻塞的,那小明娶的老婆,到底是誰?且看分析。
        in1 <= in1+8'h01;   //老小芳畢業,新小芳升級大四
        out <= in1;         //小明娶老婆
所謂阻塞,就是一步一步來,就是寫軟件的那個思路,小明順利娶他昔日的戀人為妻。

我們要調整語句順序了,再看看小明的執行結果咋樣
        in1 = in1+8'h01;    //老小芳畢業,新小芳升級大四
        out = in1;          //小明娶老婆

要順序執行的哦,先完成“老小芳畢業,新小芳升級大四”,然后“小明娶老婆”。
小明娶到了剛剛大三升大四的小芳,你完全可以認定,小明是一個喜新厭舊的文藝青年。

而如果,做下語句的調整,就像下面這樣
        out = in1;          //小明娶老婆
        in1 = in1+8'h01;    //老小芳畢業,新小芳升級大四
小明喜新厭舊的企圖,被強大的阻塞語句,給堵回去了。

一般用阻塞語句來實現assign語句描述困難的組合邏輯,一般情況下代碼塊會比較小。
非阻塞的一般是用于時序邏輯,時序邏輯往往比較復雜,有時候復雜得有些變態。
如果月老執行Verilog語句的時候,一不小心,小明就娶錯了老婆。


阻塞,有個地方用起來很方便,也許你也猜到了,testbench

tb代碼本身,就不被用來綜合到電路,所以,可以大膽使用阻塞語句
        #10 rst = 1;
        #10 clk = 0;
        #10 clk = 1;
        #10 clk = 0;
        #10 clk = 1;
        #10 rst = 0;
        repeat(100)
        begin
            #10 clk = 0;
            #10 clk = 1;
        end

這是一段,模擬單片機復位釋放以及振蕩器啟動的激勵。
反正是順序執行的,就拿這寫軟件的腦袋來理解就夠了,估計軟電工都喜歡。


客戶下單了,我得看看還有多少物料,還得準備安排焊接。

今天就先扯到這里吧。
請一定記住今天的學習任務,我們要幫小明娶到正確的老婆。
6
 樓主| 發表于 2012-5-12 00:23:28 | 只看該作者
5、同步和異步設計

前面已有鋪墊,同步就是與時鐘同步。
同步就是走正步,一二一,該邁哪個腳就邁那個腳,跑的快的要等著跑的慢的。
異步就是搞賽跑,各顯神通,盡最大力量去跑,誰跑得快,誰拿獎牌。

我們舉個例子,SPI接口,他是一個低成本的單端的高速串行數據傳輸協議。
四個信號,nCS、SCK、MISO、MOSI

下面是一個Slave SPI的接口部分,簡化了,

model mySPI(input nCS, input SCK, input MOSI, output MISO);
    reg[3:0]    bitcnt;
    reg[7:0]    shift_in;   //寫入
    reg[7:0]    shift_out;  //讀出

    reg[7:0]    data_wt;
    reg[7:0]    data_rd;

    always @(posdge SCK)
    if(nCS)
        bitcnt <= 0;
    else
    begin
        if(bit_cnt!=4'h7)
        begin
            bitcnt <= bitcnt+4'h1;
            shift_in <= {shift_in[6:0], MOSI};
            shift_out <= {shift_out[6:0], 0};
        end
        else
        begin
            bitcnt <= 4'h0;
            ...........
            data_wt <= deshift_in;
            shift_out <= data_rd;
        end
    end
endmodule

這段代碼是同步的還是異步的?
其實,他遠看是同步的,近看是異步的,仔細一看還是同步的。

大致一看,丫的還配時鐘呢,按鐘點走步,八成是同步的。
然后一想,不對啊,SPI的SCK是Master提供的,跟自家的全局時鐘沒有必然關系啊,異步的。
思索一陣,假如俺系統全局時鐘都靠SCK不就是同步的了嗎?

實際情況如何呢?
舉個例子,SPI Flash,比如25系列,其實就是同步的,SCK就是全局時鐘。

比如某ARM core的MCU內置SPI模塊,為了簡化問題,我們只談Slave的情況,問題就來了。
ARM MCU肯定有其自家的時鐘,SPI的Master又送來一個時鐘,咋辦呢?

當你發愁的時候,你該慶幸自己遇到了幾乎所有入門的人都必須解決的問題----多時鐘系統。

多時鐘,各自都是同步,放在一起就是異步。
正如兩隊人馬,都在走正步,**走得快,國軍走的更快,他們各自都是同步的,扯蛋到一塊就是異步。
咋辦呢?
叢林法則要起作用了,單一時鐘同步化處理,勢力小的聽勢力大的人安排。

model mySPI(input clk, input nCS, input SCK, input MOSI, output MISO);

    always @(clk)
    ...................................
endmodule

clk是自家的全局CLK信號,對方的SCK信號,只在自家CLK觸發才看一眼對方的各個信號,包括SCK信號。
這就是強者的統戰部,你家的可汗(SCK),見到我家皇帝(clk),也是稱臣子。

當然,這個處理方法是有前提的,就是clk的頻率要遠遠高于SCK信號。
所謂遠遠高于,就是即使我clk的上升沿,瞄你一眼,就不會漏掉你所有的表現。
根據XXXOOO定律,要達到采樣不丟信息,尼瑪的頻率至少是人家的2倍,實際應用中一般保證4倍,或更高。
就好比有4個小弟的人,叫只有一個小弟的人,對自己稱臣,聽話大家就,不聽就干你。

前面有朋友談到了復位信號的同步化處理,最簡單的就是復位和釋放都同步處理,我前面幾個帖子有用到。
復位,是什么,是殺頭,復位釋放是什么,是重新投胎。
你跟情敵斗志斗勇的時候,想到了制勝的一招時候,你覺得是立馬去執行,還是等下一次例行見面時再執行。
當然是立馬執行了,這不就是異步把情敵給復位了嘛。
你擊敗情敵之后,要對全班同學宣布的你勝利,是每天早會宣布呢,還是里面召集同學宣布呢?
此時大勢已定,當然是按CLK四平八穩來得妥當,大家會認為你是一個做事不魯莽,有步驟的,電工十佳青年。

所以,我們稱之為,異步復位,同步釋放。
always @ (posedge clk or negedge nRST)
    if (!nRST)
        擊敗情敵;
    else
        把擊敗情敵的戰果宣布;
   

再舉個例子,Memory的訪問,為了簡化,我們做個ROM,這樣只有讀的一種情況,適合理解記憶
model memory8(input[7:0] addr, output[7:0] dat)
    reg[7:0]    rom[255:0];
    assign dat = rom[addr];
endmodule

model memory8(input clk, input[7:0] addr, output[7:0] dat)
    reg[7:0]    rom[255:0];
    reg[7:0]    outbuf;

    assign dat = outbuf;
    always @(posedge clk)
        outbuf <= rom[addr];
endmodule

簡單的是異步的,只要地址變化了,輸出立馬就表現。
我們實際使用的27系列的EPROM,61系列的異步SRAM,都是這樣的,始終把OE信號置于有效即可。

復雜的就是同步的了,我不管你地址變了沒,在CLK上升沿到來之前,我懶的理你。
異步的SRAM,剛查了下,也有61系列的; 看來不能以前綴來瞎子摸象了。

一般實際用的時候,異步SRAM肯定比同步的好用,同步的老要CLK,你是IO口模擬呢,還是怎么輸出呢?
但同步的Memory也不是吃素的,吃的多必然長得壯,同步可以提高更高的傳輸速度。

該往回說了,為何同步電路可以提高更高的速度呢?

異步,就是賽跑,速度以跑得慢的人為準,團隊精神嘛,這不能平均,只能搞木桶原理。
同步,就是大家按一個節奏,你慢的話,就用2個節奏完成,但必須按節奏。

這樣負責協調的那個人,就是喊一二一的那個人(clk),可以把握全局的節奏來達到速度最大化。

所以一般FPGA里面都有全局時鐘,強大的扇出能力,最小的傳輸延遲,因為他是老大,好資源他先挑的。
他就好比系統的原子銫鐘,他很精確,我們每天跟他對一下時間,我們自家的表,就不會產生誤差積累。

異步,2個隊伍,各自有自家的老大,比如一個是地址線,一個是數據先,某個時刻,主控一抓。
可能地址線跑得快,數據線跑的慢,就會出現數據錯位的情況,數字電路上叫競爭。
你作為運籌帷幄的總統,不能斷定2個隊伍能同事到達,你仍然用這個方法,你就是在冒險。

作為設計而言,應盡量避免競爭冒險。
如果系統簡單,工期緊,速度要求低,邏輯簡單,用異步的。
如果系統龐大,速度要求越高越好,邏輯交叉錯節,堅決用同步的。
同步設計就是個工具,讓智商90的人可以干智商120的人的工作。

Asynchronous 和 Synchronous 這兩個單詞我老是分不清
后來學軟件學邏輯電路,給記住了,帶A的要要冒尖的,是異步的

明天要講的有限狀態機,是以同步設計為基礎的設計方式,然后我們就可以用90的智商做150智商的工作了。
7
 樓主| 發表于 2012-5-12 00:23:37 | 只看該作者
6、有限狀態機

狀態機,只要C代碼寫過2年的人,估計無人不識君,稍微復雜的邏輯都可以借助狀態機來簡化問題。

為了方便,我們使用前面用過的一個例子,來說明狀態機的應用,也就是說我們前面已經有意無意的用過狀態機了。

我們以SPI的Slave接口,為例,來說明狀態機的使用


為了簡化問題
1、我們沒有把信號同步到本地時鐘
2、把其他信號同步到SCK
3、我們把SPI暫時按照單向來分析

下面,我們分析SPI通訊
1、nCS高電平時候,總線是空閑的
2、nCS低電平時傳輸數據
3、滿了8個bit,湊夠了一個字節,要保存當前已經收到的字節,并準備收下一個;

nCS高電平的時候,我們稱之為idel態(IDEL)
接受0~7逐個bit的時候,稱之為bit接受態(BIT_RECV)
收滿一個字節,稱之為字節轉存態(BYTE_SAVE)

我們開始畫狀態轉移圖


model SlaveSPI(input nCS, input SCK, input MOSI, output MISO);

parameter IDEL = 0,
          BIT_RECV = 1,
          BIT_SAVE = 2;

    reg[3:0]    bitcnt;
    reg[7:0]    shift_in;   //寫入
    reg[7:0]    shift_out;  //讀出

    reg[7:0]    data;

    reg[1:0]    state;
    reg[1:0]    next_state;

    always @(*)
    begin
        case(state)
        IDEL:
            if(nCS==1'b1)
                next_state = IDEL;
            else
                next_state = BIT_RECV;
        BIT_RECV:
            if(nCS==1'b0)
            bgein
                if(bitcnt<4'h8)
                    next_state = BIT_RECV;
                else
                    next_state = BYTE_SAVE;
            end
            else
                next_state = IDEL;
        BYTE_SAVE:
            if(nCS==1'b0)
                next_state = BIT_RECV;
            else
                next_state = IDEL;
        defalut:
            next_state = IDEL;
        endcase
    end


    always @(posdge SCK)
        if(nCS)
            bitcnt=0;
        else
            state = next_state


    always @(posdge SCK)
        case(state)
        BIT_RECV:
            begin
                bitcnt <= bitcnt+4'h1;
                shift_in <= {shift_in[6:0], MOSI};
            end
        BYTE_SAVE:
            begin
                bitcnt <= 4'h0;
                data <= deshift_in;
            end
        endcase
   
我用了所謂的三段式,來描述這個狀態機,用了3個always語句
第一個always用來描述狀態轉移的條件
第二個always用來描述狀態轉移
第三個always用來描述狀態機的輸出,也就是狀態機實際要干的活


與前面帖子(同步和異步設計)中的SPI代碼相比,是不是冗長了很多。
冗長,并不代表著脫褲子放屁,自找麻煩。您難道沒有反顯,代碼很容易讀懂了嗎?

沒錯,這就是空間換時間的策略,我們寫更長的代碼來增強可分析性。

狀態機的代碼撰寫一般不復雜,復雜的是狀態機的構建過程,這個過程中,我們要分析
實際遇到的各個情況,同時把狀態機進行優化,某些狀態進行合并,某些狀態去掉。
  
有人問,為何某些狀態要去掉?
這要說下FSM的來頭了,有限狀態機,是有限的狀態機。
自然界實際的狀態機,往往起狀態的數量,是非常大的,直接建模使用簡直是勞民傷財。
而且,現實的往往是無限的狀態機,這根本無法用于工程實現,所以有限狀態機就橫空出世了。

正如,軟件算法中的DAG(Directed Acyclic Graph)有向無環圖,比純粹的圖有實用價值。
二叉樹,比多叉樹,也更容易實現。

越說,軟件和FPGA越近了。可謂是天下大勢,分久必合,合久必分。

好了,這篇比較倉促,周六晚上專門來辦公室寫這個帖子。
8
 樓主| 發表于 2012-5-12 00:23:44 | 只看該作者
7、設計一個只有4條指令的CPU


我們要設計一個簡單的CPU

既然做CPU,我們要做流水線的,要簡單,做2級流水線就夠了。

為了實例的簡單,我們選擇設計一個8bit的MCU的內核
仍然我們要簡單,所以選擇RISC的內核,類似PIC的結構
還是為了要簡化,我們只支持4條指令
繼續為了要簡化,我們不考慮Status寄存器

有人會問,只有4條指令,你還加減法都有,有一個不就可以了。
這也是我有意的,你想,假設ALU只能做加法,你不覺得ALU這個名稱太不名副其實了嗎。

mov  A,#35H 把立即數mov到A寄存器
add  A,#42H (A) + 12 -> A
sub  A,#62H (A) - 12 -> A
JMP  imd    跳轉到某地址


我們先給他們做機器編碼,我們用16bit寬度的指令集編碼

0x0035      00是MOV的OP code
0x0142      01是ADD的OP Code
0x0265      02是SUB的OP code
0x8000      80是JMP的OP CODE

我們繼續看,指令集,用Verilog的方式來描述
16'b0000_0000_????_????     MOV
16'b0000_0001_????_????     ADD
16'b0000_0010_????_????     SUB
16'b1???_????_????_????     JMP

我們可以看到JMP的跳轉地址范圍是15個bit地址,也就是32K地址范圍

有人說ALU很重要,好,我們就先來看ALU的組成,因為只有加減2種情況,所有ALU的OP代碼只用1個bit表示
op為1的時候,做加法,為0的時候做減法。

module alu(input op, input[7:0] in1, input[7:0] in2, output[7:0] out)
    assign out = op ? (in1+in2) : (in1-in2);

看到上面的代碼,估計不少人大跌眼鏡,莫非傳說中的alu就這么簡陋。
沒錯,如果你只要做加法和減法,而且不考慮進位和溢出的ALU,就是這么easy的。

好了,cpu的運轉過程,包括加載指令,解碼指令,執行指令,大家都知道。

我們還要使用流水線技術,雖然這里不用也許更簡單,但我們的目標是學習。

      一  |   加載指令1   |   加載指令2    |   加載指令3    |   ..........
----------+---------------+----------------+----------------+-----------------
      二  |               |   解碼1 執行1  |   解碼2 執行2  |   解碼3 執行3

我們可以看到加載和解碼和執行,并沒有在一個周期中完成,而是分開了
在運行第二條指令的時候,CPU正在加載第三條指令,一心二用,事事不耽擱。

clkcnt;

always @(posdge clk)
    if(nCS)
        clkcnt <= 0;
        instr <= 0;
    else
        instr <= rom_dat_out;


下面是CPU的解碼和執行過程

always @(posdge clk)
    if(!nCS)
    casex(instr)
        16'b0000_0000_????_????:        //MOV
            begin
                acc <= instr[7:0];
                pc <= pc + 16'h0001;
            end
        16'b0000_0001_????_????:        //ADD
            begin
                acc <= aluout;
                pc <= pc + 16'h0001;
            end
        16'b0000_0010_????_????:        //SUB
            begin
                acc <= aluout;
                pc <= pc + 16'h0001;
            end
        16'b1???_????_????_????:        //JMP
            begin
                pc <= instr[14:0];
                pc <= pc + 16'h0001;
            end


下面完成CPU核心和ALU之間的連線

assign aluop  = (instr[15:8]==8'h01);
assign aluin1 = acc;
assign aluin2 = instr[7:0];

alu alu1(aluop, aluin1, aluin2, aluout);

有人說,只看到執行指令,沒看到解碼指令的過程,有木有啊?當然有

16'b0000_0000_????_????     MOV
16'b0000_0001_????_????     ADD
16'b0000_0010_????_????     SUB
16'b1???_????_????_????     JMP

這幾個逐個的case不就是在做解碼?只是沒有獨立的解碼步驟而已,因為太簡單了嘛。

還有個地方,我故意做了遺漏,就是JMP指令的處理。

所謂流水線,就是取指和執行是同時的,但JMP的到來,帶來了異常。
正常都是PC加一,所以取指其實一直在取下一條指令,而JMP的目標是不確定的,所以取的指令就不對了

我們一般稱之為預測失敗,然后繼續取JMP目標地址的指令,但執行部分,會有一個空的指令周期。
從CPU的用戶角度看,就是JMP指令要使用2個指令周期。

CPU的設計基本到此結束了。
關于FPGA,我也沒有能力做太深入的講解了,否則誤人子弟,豈不是背離了我的目標。

我的blog基本上也是很少更新,你點進去看也沒多少貨。
有個哥們說得好,MP的博客,寫得比別人的微薄還短小。

至此,這個帖子連載終于完成了,總算沒有辜負自己和一些網友。
9
 樓主| 發表于 2012-5-12 00:24:40 | 只看該作者
占樓完成,下周每天一貼。
10
發表于 2012-5-12 09:24:46 | 只看該作者
算沙發吧
11
發表于 2012-5-12 09:39:31 | 只看該作者
頂一把毛片哥,多謝先啦!     
12
發表于 2012-5-12 12:46:21 | 只看該作者
mp哥太厲害了,fpga也會,崇拜ing。
13
發表于 2012-5-12 16:41:34 | 只看該作者
要自己搞一個小項目來做,一步一步貼出來,包括軟件的使用。
毛片用X還是A的?
14
 樓主| 發表于 2012-5-12 17:15:56 | 只看該作者
如果你不需要自己設計,只需要看懂一些代碼,理解Team中別人的意思。
這個帖子就是教你一周學會XXOO的

如果你已經入門而登堂入室了,這個帖子真的不應該看了,否則會退步的。
15
發表于 2012-5-13 08:18:12 | 只看該作者
thanks
16
 樓主| 發表于 2012-5-13 18:35:41 | 只看該作者
要自己搞一個小項目來做,一步一步貼出來,包括軟件的使用。
毛片用X還是A的?
忘情天書 發表于 2012-5-12 16:41


還軟件的使用,要不要拿著你的手敲鍵盤呢,你還以為你花錢請我做你的家庭教師呢。
哥哥我這一周教你學會XXOO可都是實打實的料,不負責教人用軟件拉這個menu點那個button的。

我只寫Verilog的,至于X還是A要看哪個有優勢了-------采購周期短,價格石灰,等等。
17
發表于 2012-5-13 18:46:12 | 只看該作者
田鼠的存在意義就是呵呵大家鄙視的
18
發表于 2012-5-13 21:50:34 | 只看該作者
VHDL語言也不是很難啊
19
發表于 2012-5-13 22:36:59 | 只看該作者
田鼠的存在意義就是呵呵大家鄙視的
dddg 發表于 2012-5-13 18:46
我鄙視你個卵子!哥哥早在08年就用EP2C8做過項目。FPGA最難就是用硬件的思維去實現軟件。用軟件有人教最好啦,不然你會在線仿真設置?離線仿真編寫?那些語言還不是差球不多,幾天看本書就行了。就是一些思維要轉變而已,什么阻塞和非阻塞的區別,同步異步,單一時鐘N個時鐘同步,保持,狀態機,我插死你。你要是從頭開始,花在軟件上的時間比那些爛語言要多得多,除非你不是很懂C和硬件基礎。tnnd,你要是綜合出錯了,都不知道在那里找原因。綜合知道嗎?我一磚拍死你個瓜娃子。
20
發表于 2012-5-13 22:37:57 | 只看該作者
還軟件的使用,要不要拿著你的手敲鍵盤呢,你還以為你花錢請我做你的家庭教師呢。
哥哥我這一周教你學會XXOO可都是實打實的料,不負責教人用軟件拉這個menu點那個button的。

我只寫Verilog的,至于X還是A要看 ...
McuPlayer 發表于 2012-5-13 18:35
我一磚拍死個毛皮

評分

參與人數 1積分 -10 收起 理由
kbgyzp -10 鄙視你!丫的要不你來給哥上一課

查看全部評分

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

關于我們  -  服務條款  -  使用指南  -  站點地圖  -  友情鏈接  -  聯系我們
電子工程網 © 版權所有   京ICP備16069177號 | 京公網安備11010502021702
快速回復 返回頂部 返回列表
主站蜘蛛池模板: 99久久精品国产高清一区二区 | 久久久毛片免费全部播放 | 精品国内一区二区三区免费视频 | 国产精品国产欧美综合一区 | 中文字幕在线2021一区 | 91视频青青草 | 大陆国语自产精品视频在 | 久久综合久久精品 | 日本精品一区二区在线播放 | 91亚洲精品丁香在线观看 | 91久久网 | 欧美性生交xxxxx久久久 | 久久免费手机视频 | 日韩在线视频免费播放 | 91自产拍在线观看精品 | 国产亚洲一区二区三区在线观看 | 中文字幕欧美视频 | 国产三级精品三级在线观看 | 国产成人精品福利网站人 | 一区二区在线视频 | 久久精品国产影库免费看 | 国产精品国产三级国产专业不 | 精品哟哟哟国产在线不卡 | 91app在线观看 | 欧美成人免费网在线观看 | 日韩视频免费看 | 艳母在线免费看影视网站 | 国产yw855.c免费观看网站 | 欧美男女性生活视频 | 又粗又硬又爽的三级视频 | 日韩每日更新 | 一级毛毛片毛片毛片毛片在线看 | 精品欧美一区二区三区四区 | 成人综合在线观看 | 美女伊人| 国产精品一区二区手机看片 | 91蜜桃传媒一二三区 | 中文在线日本免费永久18近 | 久久久久久一级毛片免费野外 | 日本 在线播放 | 日本高清免费不卡视频 |