1. <menuitem id="edugg"><ins id="edugg"><delect id="edugg"></delect></ins></menuitem> <progress id="edugg"></progress>

      <samp id="edugg"><ins id="edugg"></ins></samp>
      <samp id="edugg"></samp>
        <menuitem id="edugg"></menuitem>
        <dl id="edugg"></dl>

        《電子技術應用》
        您所在的位置:首頁 > 可編程邏輯 > 其他 > FPGA教學——基于FPGA的電子計算器系統設計(附代碼)

        FPGA教學——基于FPGA的電子計算器系統設計(附代碼)

        2022-08-11
        來源: The last one FPGA技術江湖

          導讀

          本篇介紹了一個簡單計算器的設計,基于 FPGA 硬件描述語言 Verilog HDL,系統設計由計算部分、顯示部分和輸入部分四個部分組成,計算以及存儲主要用狀態機來實現。顯示部分由六個七段譯碼管組成,分別來顯示輸入數字,輸入部分采用4*4矩陣鍵盤,由0-9一共十個數字按鍵,加減乘除四個運算符按鍵,一個等號按鍵組成的。通過外部的按鍵可以完成加、減、乘、除四種功能運算,其結構簡單,易于實現。本篇為本人畢業設計部分整理,各位大俠可依據自己的需要進行閱讀,參考學習。

          前言

          在國外,電子計算器在集成電路發明后,只用短短幾年時間就完成了技術飛躍,經過激烈的市場競爭,現在的計算器技術己經相當成熟。計算器已慢慢地脫離原來的“輔助計算工具”的功能定位,正在向著多功能化、可編程化方向發展,在各個領域都得到了廣泛的應用。用計算器不僅可以實現各種各樣復雜的數學計算還可以用來編制、運行程序,甚至解方程組,圖形計算器還可以進行圖形處理。計算器內置的軟件允許用戶進行類似于對計算機的文件和目錄管理等操作,允許用戶對圖形界面進行定制,同時各種新技術也被應用到計算器里使計算器功能越來越強大??梢哉f,計算器就是一個“微微型”的計算機。國內也有廠商利用計算器芯片開發新的產品,但對計算器技術的研究、計算器芯片的設計還處于起步階段。計算器的主要功能還是在于“計算”,不妨稱之為“低檔計算器”。即便是對這種計算器,很多廠商也只從事計算器的組裝、銷售業務。一些IC設計公司、芯片提供商也開始研究計算器技術。

          本次設計基于現場可編程邏輯器件FPGA進行設計,應用硬件描述語言Verilog編程并在Altera公司的QuartusⅡ軟件上實現仿真。需要進行計算器的常用運算功能的實現,通過外接鍵盤輸入、LED數碼顯示來達成運算目的。

          一、緒論

          Verilog HDL 是一種硬件描述語言(HDL:Hardware Description Language),以文本形式來描述數字系統硬件的結構和行為的語言,用它可以表示邏輯電路圖、邏輯表達式,還可以表示數字邏輯系統所完成的邏輯功能。Verilog HDL和VHDL是世界上最流行的兩種硬件描述語言,都是在20世紀80年代中期開發出來的。前者由Gateway Design Automation公司(該公司于1989年被Cadence公司收購開發)。兩種HDL均為IEEE標準。

          1.1 Verilog HDL 的發展

          Verilog是由Gateway設計自動化公司的工程師于1983年末創立的。當時Gateway設計自動化公司還叫做自動集成設計系統(Automated Integrated Design Systems),1985年公司將名字改成了前者。該公司的菲爾·莫比(Phil Moor by)完成了Verilog的主要設計工作。1990年,Gateway設計自動化被Cadence公司收購。

          1990年代初,開放Verilog國際(Open Verilog International,OVI)組織(即現在的Accellera)成立,Verilog面向公有領域開放。1992年,該組織尋求將Verilog納入電氣電子工程師學會標準。最終,Verilog成為了電氣電子工程師學會1364-1995標準,即通常所說的Verilog-95。

          設計人員在使用這個版本的Verilog的過程中發現了一些可改進之處。為了解決用戶在使用此版本Verilog過程中反映的問題,Verilog進行了修正和擴展,這部分內容后來再次被提交給電氣電子工程師學會。這個擴展后的版本后來成為了電氣電子工程學會1364-2001標準,即通常所說的Verilog-2001。Verilog-2001是對Verilog-95的一個重大改進版本,它具備一些新的實用功能,例如敏感列表、多維數組、生成語句塊、命名端口連接等。目前,Verilog-2001是Verilog的最主流版本,被大多數商業電子設計自動化軟件包支持。

          2005年,Verilog再次進行了更新,即電氣電子工程師學會1364-2005標準。該版本只是對上一版本的細微修正。這個版本還包括了一個相對獨立的新部分,即Verilog-AMS。這個擴展使得傳統的Verilog可以對集成的模擬和混合信號系統進行建模。容易與電氣電子工程師學會1364-2005標準混淆的是加強硬件驗證語言特性的SystemVerilog(電氣電子工程師學會1800-2005標準),它是Verilog-2005的一個超集,它是硬件描述語言、硬件驗證語言(針對驗證的需求,特別加強了面向對象特性)的一個集成。

          2009年,IEEE 1364-2005和IEEE 1800-2005一共兩個部分合并為IEEE 1800-2009,成為了一個新的、統一的SystemVerilog硬件描述驗證語言(hardware description and verification language,HDVL)。

          1.2 Verilog HDL 的特點

          描述復雜的硬件電路,設計人員總是將復雜的功能劃分為簡單的功能,模塊是提供每個簡單功能的基本結構。設計人員可以采取“自頂向下”的思路,將復雜的功能模塊劃分為低層次的模塊。這一步通常是由系統級的總設計師完成,而低層次的模塊則由下一級的設計人員完成。自頂向下的設計方式有利于系統級別層次劃分和管理,并提高了效率、降低了成本?!白缘紫蛏稀狈绞绞恰白皂斚蛳隆狈绞降哪孢^程。

          1.3 Verilog HDL 的語言結構

          Verilog的設計初衷是成為一種基本語法與C語言相近的硬件描述語言。這是因為C語言在Verilog設計之初,已經在許多領域得到廣泛應用,C語言的許多語言要素已經被許多人習慣。一種與C語言相似的硬件描述語言,可以讓電路設計人員更容易學習和接受。不過,Verilog與C語言還是存在許多差別。另外,作為一種與普通計算機編程語言不同的硬件描述語言,它還具有一些獨特的語言要素,例如向量形式的線網和寄存器、過程中的非阻塞賦值等??偟膩碚f,具備C語言的設計人員將能夠很快掌握Verilog硬件描述語言。下面介紹Verilog語言基本規范:

          1)空白符

          空白符是指代碼中的空格(對應的轉義標識符為\b)、制表符(\t)和換行(\n)。如果這些空白符出現在字符串里,那么它們不可忽略。除此之外,代碼中的其他空白符在編譯的時候都將會被視為分隔標識符,即使用2個空格或者1個空格并無影響。不過,在代碼中使用合適的空格,可以讓上下行代碼的外觀一致(例如使賦值運算符位于同一個豎直列),從而提高代碼的可讀性。

          2)注釋

          為了方便代碼的修改或其他人的閱讀,設計人員通常會在代碼中加入注釋。與C語言一樣,有兩種方式書寫注釋。第一種為多行注釋,即注釋從/*開始,直到*/才結束;另一種為單行注釋,注釋從//開始,從這里到這一行末尾的內容會被系統識別為注釋。

          3)某些電子設計自動化工具,會識別出代碼中以特殊格式書寫、含有某些預先約定關鍵詞的注釋,并從這些注釋所提取有用的信息。這些注釋不是供人閱讀,而是向第三方工具提供有關設計項目的額外信息。例如,某些邏輯綜合工具可以從注釋中讀取綜合的約束信息。

          4)大小寫敏感性

          Verilog是一種大小寫敏感的硬件描述語言。其中,它的所有系統關鍵字都是小寫的。

          5)標識符及保留字

          Verilog代碼中用來定義語言結構名稱的字符稱為標識符,包括變量名、端口名、模塊名等等。標識符可以由字母、數字、下劃線以及美元符($)來表示。但是標識符的第一個字符只能是字母、數字或者下劃線,不能為美元符,這是因為以美元符開始的標識符和系統任務的保留字沖突。

          和其他許多編程語言類似,Verilog也有許多保留字(或稱為關鍵字),用戶定義的標識符不能夠和保留字相同。Verilog的保留字均為小寫。變量類型中的wire、reg、integer等、表示過程的initial、always等,以及所有其他的系統任務、編譯指令,都是關鍵字??梢圆殚喒俜轿墨I以完整的關鍵字的列表。

          1.4 FPGA開發環境簡介

          系統電路的軟件設計可采用工具軟件Quartus Ⅱ,用該工具軟件所支持的語言——硬件描述語言,以文本的方式進行編程輸入。在編程時分別對控制、計數、鎖存、譯碼等電路模塊進行Verilog文本描述,使每個電路模塊以及器件都以文本的形式出現,然后通過編譯、波形分析、仿真、調試來完善每個器件的功能。單個器件制作完成后,然后將它們生成庫文件,并產生相應的符號,最后用語言將各個已生成庫文件的器件的各個端口連接在一起,從而形成了系統主電路的軟件結構。在連接器件時,也可以采用圖形輸入方式,即在圖形輸入界面中調出先制作好的庫文件器件符號,再將每個器件符號的各端口直接連線,從而構成系統主電路。在上述工作的基礎上,再進行波形分析、仿真調試便完成整個軟件設計。

          二、現場可編程門陣列(FPGA)簡介

          2.1 可編程邏輯器件簡介

          可編程邏輯器件(PLD——Programmable Logic Devices)是一種由用戶編程以實現某種邏輯功能的新型邏輯器件。它誕生于20世紀70年代,在20世紀80年代以后,隨著集成電路技術和計算機技術的發展而迅速發展起來的??删幊踢壿嬈骷詥柺酪詠?,PLD經歷了從PROM、PLA、PAL、GAL到FPGA、ispLSI等高密度PLD的發展過程。在此期間,PLD的集成度、速度不斷提高,功能不斷增強,結構趨于更合理,使用變得更靈活方便。PLD的出現打破了由中小型通用型集成電路和大規模專用集成電路壟斷的局面。與中小規模通用型集成電路相比,用PLD實現數字系統,有研制周期短、先期投資少、無風險、修改邏輯設計方便、小批量生產成本低等優勢。

          隨著可編程邏輯器件性能價格比的不斷提高,EDA開發軟件的不斷完善,現代電子系統的設計將越來越多地使用可編程邏輯器件,特別是大規??删幊踢壿嬈骷?。如果說一個電子系統可以像積木堆積起來的話,那么現在構成許多電子系統僅僅需要3種標準的積木塊——微處理器、存儲器和可編程邏輯器件,甚至只需一塊大規??删幊踢壿嬈骷?。PAL(Programmable Array Logic)器件是20世紀70年代末期出現的一種低密度、一次性可編程邏輯器件。GAL(Generic Array Logic)器件是繼PAL器件之后,在20世紀80年代中期推出的一種低密度可編程邏輯器件。它在結構上采用了輸出邏輯宏單元(OLMC——Output Logic Macro Cell)結構形式,在工藝上吸收EEPROM的浮柵技術,從而使GAL器件具有可擦除、可重新編程、數據可長期保存的結構特點。CPLD(Complex Programmable Logic Device)是萬門以上的復雜可編程邏輯器件,采用CMOS EPROM、EEPROM、快閃存儲器和SRAM等編程技術,從而構成高密度、高速度和低功耗的可編程邏輯器件。

          2.2 現場可編程門陣列(FPGA)

          FPGA是現場可編程門陣列(Field Programmable Gate Array)的簡稱。FPGA器件及其開發系統是開發大規模數字集成電路的新技術。它利用計算機輔助設計,繪制出實現用戶邏輯的原理圖、編輯布爾方程或用硬件描述語言等方式作為設計輸入;然后經一系列轉換程序、自動布局布線、模擬仿真的過程;最后生成配置FPGA器件的數據文件,對FPGA器件初始化。這樣就實現了滿足用戶要求的專用集成電路,真正達到了用戶自行設計、自行研制和自行生產集成電路的目的。

          FPGA是一種半定制的集成電路,其特點是直接面向用戶,具有極大的靈活性和通用性,開發效率高,硬件測試和實現快捷,工作可靠性好而且技術維護簡單。

          FPGA相對于CPLD而言,其結構特點在于FPGA是基于查找表look-up-table的。查找表(look-up-table)簡稱為LUT,LUT本質上是一個RAM。FPGA使用4輸入的LUT,所以每一個LUT 可以看成一個有4位地址線的16*1的RAM。當用戶通過原理圖或HDL語言描述了一個邏輯電路后,PLD/FPGA開發軟件會自動計算邏輯電路的所有可能的結果,并把結果事先寫入RAM,這樣,每輸入一個信號進行邏輯運算就等于輸入一個地址進行查表,找出地址對應的內容,然后輸出即可。

          2.2.1 FPGA的器件結構與工作原理

          FPGA(Field Programmable Gate Array)即現場可編程邏輯陣列,是大規??删幊碳呻娐返闹髁髌骷?。FPGA一般由三種可編程電路和一個用于存放編程數據的SRAM(靜態隨機存儲器)組成,這三種可編程電路是:可編程邏輯陣列LAB(Logic Array Block),輸入輸出模塊IOB(I/O Block)和互連資源IR(Interconnect Resource)。FPGA可編程邏輯形成的方法是基于查找表LUT(Look Up Table)結構的,LUT是可編程的最小邏輯構成單元。

          1)可編程邏輯陣列LAB

          可編程邏輯陣列是由一系列相鄰的邏輯單元LE(Logic Element)構成的,每個LAB包括八個邏輯單元LE、相連的進位鏈和級聯鏈,LAB控制信號和LAB局部互連。LAB的構成、ACEK系列芯片的“粗粒度(coarse-grained)”結構,有利于EDA軟件進行布局布線,優化器件的利用進而提高整個數字系統的性。

          其中的邏輯單元LE是一種基于查找表的函數發生器。它能夠實現4輸入1輸出的任意邏輯函數。每個LE包含一個4輸入的查找表、一個帶有同步使能的可編程觸發器、一個進位鏈和一個級聯鏈。每個LE有兩個輸出分別可以驅動局部互連和快速通道互連。LE有兩個輸出驅動內部互連,一個是驅動局部互連輸出,另一個驅動行或列的快速通道Fast Track的互連輸出,這兩個輸出可以單獨控制。因此在一個邏輯單元LE中的觸發器和查找表能夠用來完成不相關的功能,從而提高LE的資源利用率。

          在ACEK系列芯片的結構中還提供了兩種專用的高速數據通道,用于連接相鄰的LE,但不占用局部互連通路,它們是進位鏈和級聯鏈。進位鏈用來支持高速計數器和加法器,它提供了LE之間的快速向前進位功能。來自低位的進位信號經進位鏈向前直接送到高位,同時反饋入查找表和進位鏈的下一段。這種特點使得ACEK結構能夠實現高速計數器、加法器和寬位比較器。級聯鏈可以用來實現多輸入數的邏輯函數。相鄰的查找表并行地完成部分邏輯功能,級聯鏈把中間結果拼接起來。進位鏈和級聯鏈的使用有利于提高器件的工作速度,但是大量使用進位鏈和級聯鏈會限制布局布線的靈活性,導致資源的浪費。因此在設計過程應該權衡考慮,在FPGA芯片資源利用和工作速度之間尋求平衡。

          2)輸入/輸出模塊IOB

          ACEK器件的I/O引腳是由一些I/O單元驅動的。IOE(I/O Element)位于快速通道的行和列末端,包含一個雙向的緩沖器和一個寄存器。這個寄存器可以用作需要快速建立時間的外部數據輸入寄存器,也可以作為需要快速“時鐘到輸出”性能的數據輸出寄存器。IOE可以配置成輸入、輸出或雙向口。

          ACEK器件中的IOE具有許多特性,支持JTAG編程、三態緩沖和漏極開路輸出等等。每個IOE的時鐘、清零、時鐘使能和輸出使能的控制均由I/O控制信號網絡提供,采用高速驅動以減小通過器件的時間偏差。此外,ACEK器件還提供了若干專用輸入引腳,這些引腳用來驅動IOE寄存器的控制端,使用了專用的布線通道,以便具有比快速通道更短的延遲和更小的偏差。

          3)互連資源IR

          可編程的互連資源包括各種長度的金屬連線線段和一些可編程的連線開關,它們將各個邏輯陣列之間、及其與IO模塊之間互相連接起來,構成各種功能復雜的系統。

          在ACEK中互連結構是通過快速通道(Fast Track)實現的。Fast Track遍布于整個ACEK器件,是一系列水平和垂直走向的連續式布線通道。每一行的LAB都有一個專用的“行互連”,“行互連”可以驅動。I/O引腳或饋送到器件中的其LAB;“列互連”連接各行,也能驅動I/O引腳。這種布線結構能夠有效提高布線效率,使得即使非常復雜的設計也能夠測定其延時性。

          4)嵌入式陣列塊EAB

          嵌入式陣列塊EAB是在輸入輸出口上帶有寄存器的靈活的RAM塊,是由一系列嵌式RAM單元組成的。EAB的邏輯功能是在配置期間,用只讀模式對EAB編程產生一個大型查找表來實現的。采用查找表實現組合邏輯比一般算法快,EAB的快速時間通道使得這一先進性能進一步得到加強。當要實現存儲器功能時,每個EAB提供2048比特位,每一個EAB是一個獨立的結構,它具有共同的輸入、互連和控制信號。每個EAB含有一個行互連饋端,EAB的輸出能夠同時驅動行互連通道和列互連通道。這一特性增加了EAB的可利用布線資源。因此,EAB可以非常方便地用于實現一些規模不大的RAM、ROM、FIFO等功能模塊;同時在實現地址譯碼器、狀態機、微控制器等復雜邏輯時也具備了一定優勢。

          2.2.2 基于EDA平臺的FPGA開發流程

          一個完整、典型的EDA設計流程即是自頂向下設計方法的具體實施過程,也是EDA軟件本身的組成結構。在實踐中進一步了解支持這一個設計流程的諸多設計工具,有利于排解設計中的具體問題,提高設計質量。

          1)設計輸入

          基于EDA軟件平臺的FPGA開發流程,一般有兩種設計輸入方式:圖形輸入和硬件描述語言文本輸入。下面將重點介紹采用Verilog硬件描述語言進行設計輸入方法,這是我們在設計開發過程中采用的主要方法。Verilog作為電子工程主流硬件描述語言,是EDA技術的重要組成部分。它于1983年由美國國防部創建,由IEEE協會進一步發展并在1987年成為IEEE國際標準。自IEEE協會公布了Verilog標準版本(IEEE Std 1076)之后,各大EDA公司相繼推出支持Verilog語言的開發環境。從此Verilog作為硬件描述語言的業界標準之一,在電子設計領域得到廣泛應用,并逐步成為事實上的通用硬件描述語言。

          Verilog語言具有很強的電路建模能力,具有良好的電路行為描述和系統描述的能力,能從多個層次對數字系統進行建模和描述;Verilog語言還具有與硬件電路無關和設計平臺無關的特性,并且在語言易讀性和層次化結構化設計方面表現了強大的生命力和應用潛力。這些特性使得Verilog語言在支持自頂向下的EDA設計流程方面顯得游刃有余。因此,采用Verilog進行電子系統設計可以讓設計者專心致力于其功能的實現,而不需要對不影響功能的與工藝相關的問題花費過多的時間和精力,提高了設計效率和可靠性。

          采用Verilog文本設計輸入與傳統的計算機軟件語言編輯輸入基本上一樣,就是使用Verilog描述數字系統的功能,進行文本編輯輸入。事實上,純粹的Verilog設計輸入方法仍然是最基本、最有效和最普遍的設計輸入方法。

          2)設計綜合

          在電子設計領域“綜合”的概念可以表示為:將行為和功能層次表達的電子系統轉化為低層次的便于具體實現的模塊組合裝配而成的過程。事實上,設計過程通常從高層次的行為描述開始,直到最底層的結構描述結束,每一個步驟都是上一個層次的綜合轉換。在FPGA設計開發過程中,整個綜合過程就是設計者在EDA軟件平臺上編輯輸入Verilog文本,依據給定電路結構組件和約束控制條件進行編譯、優化、轉換和綜合,最終獲得門級電路甚至更底層的電路描述網表文件。因此設計綜合的過程就是將軟件設計的Verilog描述文本與硬件結構掛鉤,是軟件描述轉化為硬件電路的關鍵步驟,是文本描述與硬件實現之間的一座橋梁。

          3)結構適配

          適配器也稱為結構綜合器,它的功能是將綜合器產生的網表文件配置于指定的FPGA目標器件中,使之產生最終的下載文件。適配過程中所選定的FPGA目標器件必須屬于綜合器原來指定的目標器件系列,這是因為適配器的適配對象是直接與器件的結構細節相對應的。邏輯綜合通過后必須利用適配器將綜合后的網表文件針對某一具體目標器件進行邏輯映射操作,其中包括底層器件配置、邏輯分割、邏輯優化、邏輯布局布線操作。適配完成后可以利用適配所產生的仿真文件作精確的時序仿真,同時產生可用于編程下載文件。

          4)功能仿真和時序仿真

          在編程下載前必須利用EDA工具對配置生成的結果進行模擬分析,這就是所謂的仿真。仿真的過程就是讓計算機根據一定的算法和一定的仿真庫對EDA設計進行模擬,以驗證設計,排除錯誤。仿真包括功能仿真和時序仿真。

          功能仿真:不經過綜合和適配階段,在設計項目編譯后直接進入門級仿真器進行模擬測試。主要用于測試設計項目的邏輯功能,用以了解其實現的功能是否滿足設計要求,在功能仿真的過程不涉及任何具體器件的硬件特性。

          時序仿真:當設計項目通過功能仿真初步確定滿足設計要求后,需要綁定具體器件進行時序仿真。時序仿真就是接近真實器件運行特性的仿真,仿真文件包含了器件硬件特性參數,因而仿真精度高。

          5)編程下載

          把適配后生成的下載或配置文件,通過編程器或編程電纜向FPGA進行下載,以便進行硬件調試和驗證,從而實現滿足設計要求的電子系統。

          三、整體設計原理介紹

          FPGA具有運算速度快,編程簡易等優點,它是在PAL、GAL、CPLD等可編程器件的基礎上進一步發展的產物。它還是作為專用集成電路領域中的一種半定制電路而出現的,既解決了定制電路的不足,又克服了原有可編程器件門電路數有限的缺點。所以我采用FPGA原理設計了本次計算器,主要包括數碼管部分,按鍵電路部分以及運算器部分,設計方案基本原理如下。

          3.1 數碼管顯示

          數碼管的顯示分為兩種,靜態顯示和動態顯示,在這里我們使用的是動態顯示。動態顯示的特點是將所有位數碼管的段選并聯在一起,由位選線控制是哪一位數碼管是有效的。這樣一來,就沒有為每一位數碼管配置一個鎖存器的必要,從而就會大大簡化了硬件電路。選亮的數碼管采用動態掃描顯示。所謂的動態掃描顯示就是輪流向各位數碼管送出字形碼和相應的位選,利用發光管的余輝和人眼視覺暫留作用,使人的感覺好像各位數碼管在同時都在顯示。動態的顯示的亮度要比靜態的顯示略差了一些,因而我們在選擇需要的限流電阻應小于靜態顯示電路中的。

          3.2 按鍵部分原理

          我們采用了4*4矩陣鍵盤掃描在鍵盤中按鍵數量較多時,為了減少I/O口的占用,通常將按鍵排列成矩陣形式。在矩陣式鍵盤中,每條水平線和垂直線在交叉處不直接連通,而是通過一個按鍵加以連接。這樣,一個端口就可以構成4*4=16個按鍵,比之直接將端口線的應用于鍵盤多出了一倍,而且線數越多,區別越明顯,比如再多加一條線就可以構成20鍵的鍵盤,而直接用端口線則只能多出一鍵(9鍵)。由此可見,我們在需要的鍵數比較多時,采用矩陣法來做鍵盤是合理的。

          矩陣式結構的鍵盤顯然比直接法要復雜一些,識別也要復雜一些,列線通過了電阻連接正電源,并將行線所接的I/O口作為輸出端,而列線所接的I/O口則作為輸入。這樣,當按鍵沒有按下時,所有的輸入端都是高電平,代表無鍵按下。行線輸出是低電平,一旦有鍵按下,則輸入線就會被拉低,這樣,通過讀入輸入線的狀態就可得知是否有鍵按下了。下面我們介紹行掃描法。

          行掃描法又稱為逐行(或列)掃描查詢法,是一種最常用的按鍵識別方法,介紹過程如下。

          首先判斷鍵盤中有無鍵按下:將全部行線置低電平,然后檢測列線的狀態。只要有一列的電平為低,則表示鍵盤中有鍵被按下,而且閉合的鍵位于低電平線與4根行線相交叉的4個按鍵之中。若所有的列線均是高電平,則鍵盤中無鍵按下。

          其次判斷閉合鍵所存在的位置:在確認有鍵按下后,即可進入確定具體閉合鍵的過程。其方法是:依次將行線置為低電平,就是在置某根行線為低電平時,其它線為高電平。在確定某根行線位置為低電平后,再逐行檢測各列線的電平狀態。若某列為低,則該列線與置為低電平的行線交叉處的按鍵就是閉合的按鍵。

          3.3 狀態機原理

          運算部分我們主要應用了狀態機進行運算和存儲的,主要是有限狀態機,下面對有限狀態機進行簡單的介紹。

          狀態機簡寫為FSM(Finite State Machine),主要分為2大類:第一類,若輸出只和狀態有關而與輸入無關,則稱為Moore狀態機;第二類,輸出不僅和狀態有關而且和輸入有關系,則稱為Mealy狀態機。要特別注意的是,因為Mealy狀態機和輸入有關,輸出會受到輸入的干擾,所以可能會產生毛刺(Glitch)現象,使用時應當注意。事實上現在市面上有很多EDA工具可以很方便的將狀態圖的描述轉換成可以綜合的Verilog程序代碼。

          關于狀態機的一個極度確切的描述是它是一個有向圖形,由一組節點和一組相應的轉移函數組成。狀態機通過響應一系列事件而“運行”。每個事件都在屬于“當前”節點的轉移函數的控制范圍內,其中函數的范圍是節點的一個子集。函數返回“下一個”(也許是同一個)節點。這些節點中至少有一個必須是終態。當到達終態,狀態機停止。

          包含一組狀態集(states)、一個起始狀態(start state)、一組輸入符號集(alphabet)、一個映射輸入符號和當前狀態到下一狀態的轉換函數(transition function)的計算模型。當輸入符號串,模型隨即進入起始狀態。它要改變到新的狀態,依賴于轉換函數。在有限狀態機中,會有有許多變量,例如,狀態機有很多與動作(actions)轉換(Mealy機)或狀態(摩爾機)關聯的動作,多重起始狀態,基于沒有輸入符號的轉換,或者指定符號和狀態(非定有限狀態機)的多個轉換,指派給接收狀態(識別者)的一個或多個狀態,等等。

          傳統應用程序的控制流程基本是順序的:遵循事先設定的邏輯,從頭到尾地執行。很少有事件能改變標準執行流程;而且這些事件主要涉及異常情況?!懊钚袑嵱贸绦颉笔沁@種傳統應用程序的典型例子。

          另一類應用程序由外部發生的事件來驅動——換言之,事件在應用程序之外生成,無法由應用程序或程序員來控制。具體需要執行的代碼取決于接收到的事件,或者它相對于其他事件的抵達時間。所以,控制流程既不能是順序的,也不能是事先設定好的,因為它要依賴于外部事件。事件驅動的GUI應用程序是這種應用程序的典型例子,它們由命令和選擇(也就是用戶造成的事件)來驅動。

          Web應用程序由提交的表單和用戶請求的網頁來驅動,它們也可劃歸到上述類別。但是,GUI應用程序對于接收到的事件仍有一定程度的控制,因為這些事件要依賴于向用戶顯示的窗口和控件,而窗口和控件是由程序員控制的。Web應用程序則不然,因為一旦用戶采取不在預料之中的操作(比如使用瀏覽器的歷史記錄、手工輸入鏈接以及模擬一次表單提交等等),就很容易打亂設計好的應用程序邏輯。

          顯然,必須采取不同的技術來處理這些情況。它能處理任何順序的事件,并能提供有意義的響應,即使這些事件發生的順序和預計的不同。有限狀態機正是為了滿足這方面的要求而設計的。

          有限狀態機是一種概念性機器,它能采取某種操作來響應一個外部事件。具體采取的操作不僅能取決于接收到的事件,還能取決于各個事件的相對發生順序。之所以能夠做到這一點,是因為機器能跟蹤一個內部狀態,它會在收到事件后進行更新。為一個事件而響應的行動不僅取決于事件本身,還取決于機器的內部狀態。另外,采取的行動還會決定并更新機器的狀態。這樣一來,任何邏輯都可建模成一系列事件/狀態組合。

          狀態機可歸納為4個要素,即現態、條件、動作、次態。這樣的歸納,主要是出于對狀態機的內在因果關系的考慮?!艾F態”和“條件”是因,“動作”和“次態”是果。

          有限狀態機用于描述電路模型的時序行為,所有的輸入都可以看成是模型的激勵,所有的輸出可以看成是模型對激勵的響應。CLK提供時間基準。

          微信圖片_20220811141833.png

          圖3-1 抽象時序電路的行為模型

          Figure 3-1 abstract behavior model of sequential circuits

          時序電路模型可以表示為:R=F(t),這里F(t)是模型行為的描述。當電路的輸出僅僅與狀態時間有關時候,所描述的模型為摩爾型狀態機;當電路的輸出不僅與時間有關,也與當前的輸入信號有關時,稱為米利型狀態機。

          四、計算器設計的電路部分

          4.1  FPGA的最小系統

          FPGA最小系統是可以使FPGA正常工作的最簡單的系統。它的外圍電路盡量最少,只包括FPGA必要的控制電路。一般我們所說的FPGA的最小系統主要包括:FPGA芯片,下載電路,外部時鐘,復位電路和電源。

          4.1.1  電源時鐘復位

          我們采用了低電平復位電路,電路圖如下

          微信圖片_20220811141901.png

          圖4-1 復位電路

          Figure 4-1 reset circuit

          4.1.2  JTAG接口

          JTAG最初是用來對芯片進行測試的,JTAG的基本原理是在器件內部定義一個TAP(Test Access Port;測試訪問口)通過專用的JTAG測試工具對內部節點進行測試。JTAG測試允許多個器件通過JTAG接口串聯在一起,形成一個JTAG鏈,能實現對各個器件分別測試。如今,JTAG接口還常用于實現ISP(In——System Programmer,在系統編程),對FLASH等器件進行編程。

          JTAG編程方式是在線編程,傳統生產流程中先對芯片進行預編程然后再裝到板上,簡化的流程為先固定器件到電路板上,再用JTAG編程,從而大大加快工程進度。JTAG接口可對DSP芯片內部的所有部件進行編程。

          JTAG(Joint Test Action Group,聯合測試行動小組)是一種國際標準測試協議,主要用于芯片內部測試及對系統進行仿真、調試,JTAG技術是一種嵌入式調試技術,它在芯片內部封裝了專門的測試電路TAP(Test Access Port,測試訪問口),通過專用的JTAG測試工具對內部節點進行測試。

          如今大多數比較復雜的器件都支持JTAG協議,如ARM、DSP、FPGA器件等。標準的JTAG接口是4線:TMS、TCK、TDI、TDO,分別為測試模式選擇、測試時鐘、測試數據輸入和測試數據輸出。如今JTAG接口的連接有兩種標準,即14針接口和20針接口。

          支持邊界掃描的邏輯元器件與測試相關的所有外部通信都采用串行通信方式,允許測試指令及相關的測試數據串行送給元器件,然后允許把測試指令的執行結果從元器件中串行讀出。為了完成這樣的功能,邊界掃描技術包含了一個與元器件的每個引腳相接,包含在邊界掃描寄存器單元中的寄存器鏈,這樣元器件的邊界信號可以用掃描測試原理進行控制和觀察,這也是邊界掃描的含義。

          微信圖片_20220811141958.png

          圖4-2 JTAG配置接口電路

          Figure 4-2 the JTAG configuration interface circuit

          4.1.3  FLASH

          閃存存儲器是一類非易失性存儲器,即使在供電電源關閉后仍能保持片內信息。數字電路中經常需要使用大容量存儲器,串行Flash存儲速度快,體積小,功耗低,在FPGA中設計中發揮的作用也越來越大,廣泛應用于實現系統及功能驗證。FPGA的靈活性和串行Flash的體積小的特點相結合,具有靈活性強實用性強等特點。我們的Flash程序存儲電路如圖4-3所示。

          微信圖片_20220811142021.png

          圖4-3 Flash程序存儲電路

          Figure 4-3 Flash program memory circuit

          4.2  數碼管電路

          我們應用的LED數碼管段數為7段,就是7個發光二極管,任意一個阿拉伯數字0-9都是可以通過亮滅組合來實現的。LED數碼管根據LED的接法不同分為共陰和共陽兩類,共陰極數碼管公共端接地,共陽極數碼管公共端接電源,另一個非公共端的引腳留給用戶的I/O直接控制了,在這里我們采用共陽極數碼管。我們在這個應用中,把公共端連接到了FPGA的I/O引腳上,這就是我們數碼管的片選信號。如果我們FPGA的I/O引腳輸出低電平0,那么這個數碼管就是可以顯示數字的,如果我們這個I/O輸出高電平1。那么即使不管數碼管的段選端輸出0還是1,都沒有辦法將這7個發光二極管的任意一個點亮,這也達到了關閉數碼管顯示的效果。一旦這樣,這個數碼管的公共端被當作數碼管片選引腳使用了,雖然不是名副其實的“片選”,也是可以達到我們想要的效果。了解LED的這些特性,對編程是很重要的,不同類型的數碼管,除了它們的硬件電路有差異外,方法也是不同的。

          微信圖片_20220811142038.png

          圖4-4 數碼管電路圖

          Figure 4-4 digital tube circuit diagram

          4.3  按鍵輸入電路

          我們要實現一個計算器,首先需要有計算器的輸入信號,通常是使用連接在FPGA的GPIO接口上的pushbutton作為輸入信號。簡易計算器的輸入信號比較少,只需要數字和運算符號。我們可以將其各自使用一個按鍵來表示,然后都連接在FPGA的通用接口上。此時,我們便引入了矩陣鍵盤構造計算器的輸入。

          我們采用的是4*4矩陣鍵盤電路,矩陣鍵盤又稱行列式鍵盤,它是用4條I/O線作為行線,4條I/O線作為列線組成的鍵盤。在行線和列線的每一個交叉點上,設置一個按鍵。這樣鍵盤中的按鍵的個數就是4*4=16個。這種行列式鍵盤結構能夠有效地提高單片機系統中I/O口的利用率。其中的ROW0,ROW1,ROW2,ROW3,以及COL0,COL1,COL2,COL3信號分別連接到了FPGA上,使用一種獨特的方法,這8個信號能夠表示出16個按鍵各自按下的狀態。即這16個按鍵只占用了FPGA的8個管腳,而使用每個按鍵都連接到了FPGA的輸入管腳需要占用16個管腳。

          行列矩陣采用了掃描的形式,其8個連接到FPGA上的信號是4個輸入4個輸出的。我們一般使用的其他按鍵方法都是將按鍵產生的信號作為輸入。在這里,ROW0-ROW3為FPGA的輸入信號,COL0-COL3是FPGA的輸出信號;電路上,ROW0-ROW3還要連接一個上拉電阻。

          如果左上方第一個按鍵被按下,ROW0和COL0連接在一起;如果不按下,兩個信號則沒有連接。16個按鍵表示16種連接的關系,在沒有按鍵按下的時候,輸出的COL信號就懸空了,輸入的4個ROW信號收到上拉電阻的影響都是高電平1。如果說連接COL3與ROW2的按鍵被按下,那么FPGA的輸入信號ROL2就等于輸出信號COL3的值,其他的ROW輸入信號則全部為1。

          我們在FPGA內部使輸出的COL0信號為0而其他的COL是1,那么不管其他的列上對應的按鍵都怎么按下,都有輸入的FPGA的ROW為全1;僅僅當第一列的4個按鍵中有一個按下時,對應的行值為0,其余3個的行值為1,這樣的話第一列所對應的按鍵就唯一確定下來了。在這樣的輸出狀態下(COL[3:0]=1110),4個輸入只能確定出來第一列的4個按鍵。要是按下的不是第一行的4個按鍵,那么輸入值ROW[3:0]為1111,表示第一列沒有按鍵被按下。于是我們開始掃描第二列,就是令COL[3:0]輸出1101,然后查看ROW上是否有值為0。如果此列上仍然沒有掃描到,就繼續掃描下一列。

          對于一組數碼管動態掃描顯示需要由兩組信號來控制:一組是輸出口輸出的字形代碼,用來控制顯示的字形,稱為段碼;另一組是位輸出口輸出的控制信號,用來選擇第幾位數碼管的工作,稱為位碼。

          由于各位數碼管的段線并聯,段碼的輸出對各位數碼管來說都是相同的。因此,同一時刻如果各位數碼管的位選線都處于選通狀態的話,位數碼管將顯示相同的字符。若要各位數碼管能夠顯示出與本位相應的字符,就必須采用掃描顯示方式。即在某一時刻,只讓某一位的選線處于導通狀態,而其他各位的位選線處于關閉狀態。同時,段線上輸出相應位要顯示字符的字形碼。這樣同一時刻,只有選通的那一位顯示出字符,而其它各位則是熄滅的,如此循環下去,就可以使各位數碼管顯示出將要顯示的字符。

          雖然這些字符是在不同時刻出現的,而且同一時刻只有一位顯示,其它各位熄滅,但是由于數碼管具有余輝特性和人眼有視覺暫留現象,只要每位數碼管顯示間隔足夠短,給人眼的視覺印象就會是連續穩定地顯示。

          微信圖片_20220811142056.png

          圖4-5鍵盤電路

          Figure 4-5 The keyboard circuit

          4.4  電源電路設計

          我們使用的是LM1117,LM1117是一個低壓差電壓調節器系列。其壓差在1.2V輸出,負載電流為800mA時為1.2V。它與國家半導體的工業標準器件LM317有相同的管腳排列。LM1117有可調電壓的版本,通過2個外部電阻可實現1.25~13.8V輸出電壓范圍。另外還有5個固定電壓輸出(1.8V、2.5V、2.85V、3.3V和5V)的型號。

          LM1117提供電流限制和熱保護。電路包含1個齊納調節的帶隙參考電壓以確保輸出電壓的精度在±1%以內。LM1117系列具有LLP、TO-263、SOT-223、TO-220和TO-252 D-PAK封裝。輸出端需要一個至少10?F的鉭電容來改善瞬態響應和穩定性。

          微信截圖_20220811142119.png

          微信截圖_20220811142208.png


          五、總體代碼設計

          系統總體設計框圖如圖5-1所示。此設計由計算部分、存儲部分、顯示部分和輸入部分組成。

        微信截圖_20220811142237.png

          5.1  計算器的輸入部分

          計算器輸入部分的設計最主要的是按鍵譯碼電路的設計和實現。計算器的輸入部分是由0—9十個數字按鍵、加減乘除四則運算的運算符按鍵、一個等號按鍵和一個清零按鍵組成的,設計所要做的是對按鍵信息進行譯碼,使其在計算器內部可以使用。這里使用的是4*4鍵盤矩陣作為輸入。數字按鍵譯碼電路的主體部分Verilog語言描述如下。

        微信截圖_20220811142310.png

          在輸入鍵盤矩陣驅動模塊中,我們有三個輸入,四個輸出,其中clk為主時鐘輸入,rst_n為復位信號,row為4*4鍵盤的行輸入信號,輸出col列信號,data為鍵入的數字(0-15),valid為數字的脈沖信號,clk_1k為給計算模塊輸出的時鐘。

          5.2  計算器的運算和存儲部分

          存儲部分用狀態機和寄存器來實現,我們輸入的數字應用了移位拼接的原理,若第一個輸入的是數字鍵,則保存下來,第二次輸入還是輸入的是數字鍵時,第一個數值左移變為十位,第二次的為個位,第三次若還是數字鍵,那么第一個數值將變為百位,第二個為十位,第三個為個位,以此類推,直到有符號鍵輸入。

          當進行第一次計算時,第一個數字存放在num1里面。按下運算符以后,第二個數字放在bin_data里面。當再按下運算符號或者等號時,第一次計算的結果將存放在ans里面,同時reg清零,等待下一個數字的輸入。進行第二次運算時,將num1里面的結果與reg里面新輸入的數字進行運算,再將運算結果存放在num1里面,直到最后按下等號按鍵的時候,顯示最終的運算結果。

          程序框圖如下:

         微信截圖_20220811142430.png

        微信截圖_20220811142447.png

          計算部分驅動模塊如圖5-7所示,共有4個輸入部分,一個輸出。其中clk為時鐘,flag為數字脈沖信號,rst_n為復位,key_data為按鍵數據輸入,bin_data為二進制中間數據輸出。

          微信圖片_20220811142507.png

          圖5-7 計算部分驅動模塊

          Figure 5-7 calculation part of the driver module

          5.3  計算器的顯示部分

          顯示部分是系統的輸出部分,用于顯示按鍵值及計算結果,由于數字系統的數據運算都是二進制的,而輸出表達式都是BCD碼,為了滿足BCD碼的譯碼顯示,最方便的方法就是利用譯碼程序在FPGA中實現。本文采用的是共陽極7段數碼管,顯示數字時需要將對應管腳置為低電平,輸出時,從左到右,按從高到低位的順序依次接g、f、e、d、c、b、a,小數點為h,為最高位。七段譯碼器的基本結構如圖5-8所示。

          微信截圖_20220811142552.png

          微信截圖_20220811142620.png

          圖5-11為數碼管顯示驅動模塊,一共三個輸入,兩個輸出,其中clk為時鐘,rst_n為復位,data_in數據輸入,sel為位選信號,seg為段選信號。

          六、仿真驗證設計

          6.1  ModelSim簡介

          在仿真設計時,用到了Mentor公司的Modelsim,這是一款硬件描述語言仿真軟件,該款軟件不單單能提供十分友好的仿真環境,而且它也是我們業界第一個也是僅此一個的單內核支持VHDL和Verilog語言混合仿真的軟件。它采用直接優化的編譯技術、Tcl/Tk技術、和單一內核仿真技術,從而達到令人編譯仿真速度快的效果,而且編譯代碼和整個平臺沒有關系,這樣就更容易保護IP核,它是FPGA/ASIC設計的首選仿真軟件。

          Modelsim有不同版本,例如:SE、PE、LE和OEM,其中最高級的版本是SE,而集成在 Actel、Atmel以及Lattice等FPGA廠商設計工具中的都是其OEM版本。

          Modelsim SE支持PC、UNIX和LINUX的混合平臺;能給出十分全面到位以及高性能的驗證功能;全面支持業界設定的廣泛標準;同時Mentor Graphics公司提供了整個行業最出色的技術支持與服務。

          Modelsim的主要特點有:

          1)支持單內核的VHDL和Verilog混合在一起進行仿真處理;

          2)具有源代碼模版、助手以及項目管理功能;

          3)匯聚了性能考核、波形參考、代碼覆蓋、數據流Chase X、Signal Spy、虛擬對象Virtual Object、Assertion窗口、Memory窗口、源碼窗口顯示信號值、信號條件斷點等眾多調試功能;

          4)C和Tcl/Tk接口,C調試;

          5)能夠實現對System C的直接支持功能,同時可以和HDL任意混合使用;

          6)能夠實現System Verilog的設計功能;

          7)可以做到對系統級描述語言進行最全面的支持;

          8)可以單獨或同時進行行為(behavioral)、RTL級、和門級(gate-level)的代碼。

          9)能夠實現RTL和門級優化,編譯仿真速率非???,跨平臺跨版本的仿真。

          6.2  模塊仿真驗證分析

          FPGA設計流程包括設計輸入,仿真,綜合,生成,板級驗證等很多階段。在整個設計流程中,完成設計輸入并成功進行編譯僅僅能說明設計符合一定的語法規范,并不能說明設計功能的正確性,這時,我們就需要通過仿真對設計進行驗證。我們主要進行的是功能仿真,又叫邏輯仿真,是指在不考慮器件延時和布線延時的理想情況下對源代碼進行邏輯功能驗證;而時序仿真是在布局布線后進行。我們仿真是為了保證設計的正確性。

          6.2.1  矩陣按鍵模塊

          矩陣鍵盤測試程序流程框圖如下:

          微信圖片_20220811142708.png

          圖6-1 按鍵測試程序流程圖

          Figure 6-1 key testing program flow chart

          首先,我們要先進行按鍵檢測,判斷是否有按鍵閉合,如果沒有說明沒有按鍵鍵入,那么返回就是我們常說的消抖,重新進行按鍵檢測,直到有按鍵閉合。接下來會有10ms的延遲,保存鍵值;再繼續判斷按鍵是否松開,如果是則又會產生10ms的延遲,否則返回判斷直到按鍵有松開為止,最后返回鍵值。

          我們需要編譯一個模擬鍵盤定義data0-15,然后模擬輸入給FPGA一個行信號,FPGA接收行信號,同時輸出給模擬鍵盤一個列信號,如果輸出的列信號不存在低電平,那么行信號為4‘b1111,代表輸入的按鍵不在本列上,繼續掃描下一列直到找到相應的行信號為止。部分代碼如圖所示。

          微信圖片_20220811142720.png

          圖6-2 鍵盤掃描部分代碼

          Figure 6-2 keyboard scan code

          圖6-3為鍵盤掃描仿真圖,當我們按下1時,數據顯示1,按下10顯示10,按下2顯示的是2,按下15顯示15,仿真結果有效,程序編譯正確。

          微信圖片_20220811142736.png

          圖6-3 鍵盤掃描仿真

          Figure 6-3 keyboard scanning simulation

          6.2.2  計算仿真舉例

          加法計算舉例,首先pnumber輸入1,data_in輸入也為1,掃描結果為1;然后輸入10;pnumber輸入為2,data_in輸入為2,掃描結果為2;最后按鍵“=”,顯示結果即為3。仿真顯示結果正確,說明我們的編譯代碼沒有問題,計算有效,計算器結果可信。

          微信圖片_20220811142750.png

          圖6-4 1+2=3程序仿真圖

          Figure 6-4 1+2=3 process simulation diagram

          七、結論

          本次電子計算器的設計是基于FPGA設計的,計算器基本上可以實現的加減乘除的功能。系統的計算部分、存儲部分、顯示部分和輸入部分四個部分都可以完成設計要求,輸入部分采用鍵盤矩陣原理,存儲部分用狀態機來實現,并進行了仿真。實現了防消抖的要求,計算結果較精確。達到了預期的要求目標。

          附錄:設計主體源代碼

          二進制轉BCD代碼:

          module bin2bcd_12bit(bin, bcd);

          input [19:0] bin;

          output reg [23:0] bcd;

          always @ (*)

          begin

          bcd[3:0] = bin%10;

          bcd[7:4] = bin/10%10;

          bcd[11:8] = bin/100%10;

          bcd [15:12] = bin/1000%10;

          bcd[19:16] = bin/10000%10;

          bcd[23:20] = bin/100000%10;

          end

          endmodule

          計算模塊代碼:

          module calculator (clk, rst_n, flag, key_data, bin_data);

          input clk;

          input rst_n;

          input flag;

          input [3:0] key_data;

          output reg [19:0] bin_data;

          reg [1:0] state;

          reg [19:0] num1;

          reg [3:0] opcode;

          always @ (posedge clk or negedge rst_n)

          begin

          if (!rst_n)

          begin

          state <= 0;

          num1 <= 0;

          bin_data <= 0;

          opcode <= 0;

          end

          else

          begin

          case (state)

          0 : begin

          if (flag)

          begin

          if (key_data < 10)

          begin

          bin_data <= bin_data * 10 + key_data;

          end

          else

          begin

          if (key_data == 14)

          begin

          state <= 0;

          end

          else

          begin

          opcode <= key_data;

          state <= 1;

          num1 <= bin_data;

          bin_data <= 0;

          end

          end

          end

          else

          begin

          state <= 0;

          end

          end

          1 : begin

          if (flag)

          begin

          if (key_data < 10)

          begin

          bin_data <= bin_data * 10 + key_data;

          end

          else

          begin

          if (key_data == 14)

          begin

          case (opcode)

          10 :  begin bin_data <= num1 + bin_data; state <= 2; end

          11 :  begin bin_data <= num1 - bin_data; state <= 2; end

          12 :  begin bin_data <= num1 * bin_data; state <= 2; end

          13 :   begin bin_data <= num1 / bin_data; state <= 2; end

          default : bin_data <= 0;

          endcase

          end

          else

          begin

          state <= 1;

          end

          end

          end

          else

          begin

          state <= 1;

          end

          end

          2 : begin

          if (flag)

          begin

          if (key_data < 10)

          begin

          bin_data <= {16'd0,key_data};

          state <= 0;

          end

          else

          begin

          if (key_data == 14)

          begin

          state <= 2;

          end

          else

          begin

          num1 <= bin_data;

          opcode <= key_data;

          bin_data <= 0;

          state <=1;

          end

          end

          end

          else

          begin

          state <= 2;

          end

          end

          endcase

          end

          end

          endmodule

          輸入部分代碼:

          module key_board (clk, rst_n, row, col, data, valid, clk_1k);

          input clk;

          input rst_n;

          input [3:0] row;

          output reg [3:0] col;

          output reg [3:0] data;

          output reg valid;

          output reg clk_1k;

          reg [14:0] cnt;

          parameter T1ms = 24999;

          always @ (posedge clk or negedge rst_n)

          begin

          if (!rst_n)

          begin

          clk_1k <= 1'b1;

          cnt <= 15'd0;

          end

          else

          begin

          if (cnt < T1ms)

          begin

          cnt <= cnt + 15'd1;

          end

          else

          begin

          cnt <= 15'd0;

          clk_1k <= ~clk_1k;

          end

          end

          end

          reg [7:0] row_col;

          reg [1:0] state;

          reg [4:0] count;

          always @ (posedge clk_1k or negedge rst_n)

          begin

          if (!rst_n)

          begin

          col <= 4'b0000;

          row_col <= 8'd0;

          state <= 0;

          valid <= 0;

          count <= 0;

          end

          else

          begin

          case (state)

          0 : begin

          if (row == 4'b1111)

          begin

          col <= 4'b0000;

          end

          else

          begin

          state <= 1;

          end

          end

          1 : begin

          if (row == 4'b1111)

          begin

          state <= 0;

          count <= 0;

          end

          else

          begin

          if (count < 19)

          begin

          count <= count + 1;

          end

          else

          begin

          count <= 0;

          state <= 2;

          col <= 4'b0111;

          end

          end

          end

          2 : begin

          if (row == 4'b1111)

          begin

          col <= {col[2:0],col[3]};

          state <= 2;

          end

          else

          begin

          row_col <= {row,col};

          state <= 3;

          valid <= 1;

          end

          end

          3 : begin

          if (row == 4'b1111)

          begin

          state <= 0;

          valid <= 0;

          end

          else

          begin

          valid <= 0;

          state <= 3;

          end

          end

          default : state <= 0;

          endcase

          end

          end

          always @ (*)

          begin

          case (row_col)

          8'b0111_0111 : data = 4'hf;

          8'b0111_1011 : data = 4'he;

          8'b0111_1101 : data = 4'hd;

          8'b0111_1110 : data = 4'hc;

          8'b1011_0111 : data = 4'hb;

          8'b1011_1011 : data = 4'ha;

          8'b1011_1101 : data = 4'h9;

          8'b1011_1110 : data = 4'h8;

          8'b1101_0111 : data = 4'h7;

          8'b1101_1011 : data = 4'h6;

          8'b1101_1101 : data = 4'h5;

          8'b1101_1110 : data = 4'h4;

          8'b1110_0111 : data = 4'h3;

          8'b1110_1011 : data = 4'h2;

          8'b1110_1101 : data = 4'h1;

          8'b1110_1110 : data = 4'h0;

          default : data = 4'h0;

          endcase

          end

          endmodule

          數碼管頂層代碼:

          module seven_seg (clk, rst_n, data_in, sel, seg);

          input clk;

          input rst_n;

          input [23:0] data_in;

          output [2:0] sel;

          output [7:0] seg;

          wire clk_1k;

          freq freq_dut(

          .clk(clk),

          .rst_n(rst_n),

          .clk_1k(clk_1k)

          );

          sel_seg_encode sel_seg_encode_dut(

          .clk(clk_1k),

          .rst_n(rst_n),

          .data_in(data_in),

          .sel(sel),

          .seg(seg)

          );

          endmodule

          分頻部分代碼:

          module freq (clk, rst_n, clk_1k);

          input clk;

          input rst_n;

          output reg clk_1k;

          reg [14:0] cnt;

          always @ (posedge clk or negedge rst_n)

          begin

          if (!rst_n)

          begin

          clk_1k <= 1;

          cnt <= 0;

          end

          else

          begin

          if (cnt < 24_999)

          begin

          cnt <= cnt + 1;

          end

          else

          begin

          cnt <= 0;

          clk_1k <= ~clk_1k;

          end

          end

          end

          endmodule

          位選段選連接代碼:

          module sel_seg_encode (clk, rst_n, data_in, sel, seg);

          input clk;

          input rst_n;

          input [23:0] data_in;

          output [2:0] sel;

          output [7:0] seg;

          wire [3:0] num;

          seg_encode seg_encode_dut(

          .rst_n(rst_n),

          .num(num),

          .seg(seg)

          );

          sel_encode sel_encode_dut(

          .clk(clk),

          .rst_n(rst_n),

          .data_in(data_in),

          .num(num),

          .sel(sel)

          );

          endmodule

          段選部分代碼:

          module seg_encode (rst_n, num, seg);

          input rst_n;

          input [3:0] num;

          output reg [7:0] seg;

          always @ (*)

          begin

          if (!rst_n)

          begin

          seg = 8'b0000_0000;

          end

          else

          begin

          case (num)

          0 : seg = 8'b1100_0000;

          1 : seg = 8'b1111_1001;

          2 : seg = 8'b1010_0100;

          3 : seg = 8'b1011_0000;

          4 : seg = 8'b1001_1001;

          5 : seg = 8'b1001_0010;

          6 : seg = 8'b1000_0010;

          7 : seg = 8'b1111_1000;

          8 : seg = 8'b1000_0000;

          9 : seg = 8'b1001_0000;

          default : seg = 8'b0000_0000;

          endcase

          end

          end

          endmodule

          位選部分代碼:

          module sel_encode (clk, rst_n, data_in, num, sel);

          input clk;

          input rst_n;

          input [23:0] data_in;

          output reg [3:0] num;

          output reg [2:0] sel;

          always @ (posedge clk or negedge rst_n)

          begin

          if (!rst_n)

          begin

          sel <= 0;

          end

          else

          begin

          if (sel < 5)

          begin

          sel <= sel + 1;

          end

          else

          begin

          sel <= 0;

          end

          end

          end

          always @ (*)

          begin

          if (!rst_n)

          begin

          num = 0;

          end

          else

          begin

          case (sel)

          0 : num = data_in[23:20];

          1 : num = data_in[19:16];

          2 : num = data_in[15:12];

          3 : num = data_in[11:8];

          4 : num = data_in[7:4];

          5 : num = data_in[3:0];

          default : num = 0;

          endcase

          end

          end

          endmodule

            更多信息可以來這里獲取==>>電子技術應用-AET<<

        微信圖片_20210517164139.jpg

        本站內容除特別聲明的原創文章之外,轉載內容只為傳遞更多信息,并不代表本網站贊同其觀點。轉載的所有的文章、圖片、音/視頻文件等資料的版權歸版權所有權人所有。本站采用的非本站原創文章及圖片等內容無法一一聯系確認版權者。如涉及作品內容、版權和其它問題,請及時通過電子郵件或電話通知我們,以便迅速采取適當措施,避免給雙方造成不必要的經濟損失。聯系電話:010-82306116;郵箱:aet@chinaaet.com。
        国产野外无码激情理论片

        1. <menuitem id="edugg"><ins id="edugg"><delect id="edugg"></delect></ins></menuitem> <progress id="edugg"></progress>

          <samp id="edugg"><ins id="edugg"></ins></samp>
          <samp id="edugg"></samp>
            <menuitem id="edugg"></menuitem>
            <dl id="edugg"></dl>