開 放 電 腦 計 畫 -- 計 算 機 硬 體 結 構 2014 年 7 月 出 版 作 者 : 陳 鍾 誠 ( 創 作 共 用 : 姓 名 標 示 相 同 方 式 分 享 授 權 )
開 放 電 腦 計 畫 -- 計 算 機 硬 體 結 構 前 言 序 授 權 聲 明 開 放 電 腦 計 畫 簡 介 硬 體 : 計 算 機 結 構 軟 體 : 系 統 程 式 結 語 參 考 文 獻 電 腦 硬 體 架 構 電 腦 的 結 構 CPU0 處 理 器 CPU0 的 指 令 集 CPU0 指 令 格 式 狀 態 暫 存 器 位 元 組 順 序 中 斷 程 序 CPU0 的 組 合 語 言 與 機 器 碼 參 考 文 獻 硬 體 描 述 語 言 -- Verilog Verilog 基 礎 區 塊 式 設 計 流 程 式 設 計 結 語 參 考 文 獻 組 合 邏 輯 (Combinatorial Logic) 簡 介 加 法 器 32 位 元 加 法 器 前 瞻 進 位 加 法 器 (Carry Lookahead Adder) 結 語 參 考 文 獻 算 術 邏 輯 單 元 ALU 的 設 計 加 減 器 採 用 CASE 語 法 設 計 ALU 模 組 完 整 的 ALU 設 計 ( 含 測 試 程 式 ) 結 語 參 考 文 獻 記 憶 單 元 (Memory Unit) 時 序 邏 輯 (Sequential Logic)
正 反 器 ( 閂 鎖 器 ) SR 正 反 器 有 enable 的 正 反 器 閘 級 延 遲 (Gate Delay) 利 用 閘 級 延 遲 製 作 脈 波 變 化 偵 測 器 (Pulse Transition Detector, PTD) 使 用 脈 衝 偵 測 電 路 製 作 邊 緣 觸 發 正 反 器 使 用 脈 衝 偵 測 電 路 設 計 邊 緣 觸 發 暫 存 器 使 用 脈 衝 偵 測 電 路 製 作 計 數 電 路 暫 存 器 單 元 記 憶 體 結 語 控 制 單 元 簡 介 流 程 式 設 計 區 塊 式 設 計 微 處 理 器 (Micro Processor) MCU0 的 迷 你 版 -- mcu0m MCU0 的 區 塊 式 設 計 -- MCU0bm.v MCU0 完 整 版 輸 出 入 單 元 (I/O) 前 言 BUS ( 總 線, 匯 流 排 ) 同 步 匯 流 排 (Synchronous BUS) 異 步 匯 流 排 (Asynchronous BUS) 匯 流 排 仲 裁 (BUS arbitery) 循 序 與 平 行 輸 出 入 (Serial vs. Parallel) 常 見 的 輸 出 入 協 定 MCU0 的 輸 出 入 -- 輪 詢 篇 記 憶 系 統 (Storage) 記 憶 體 階 層 (Memory Hierarchy) 快 取 記 憶 體 (Cache) 高 階 處 理 器 (Processor) 哈 佛 架 構 (Harvard Architecture) 流 水 線 架 構 (Pipeline) CPU0 迷 你 版 - CPU0m CPU0 完 整 版 -- cpu0s 速 度 議 題 乘 法 與 除 法 浮 點 數 運 算 繪 圖 加 速 功 能 (Graphics) 平 行 處 理 (Parallel) 結 語
附 錄 本 書 內 容 與 相 關 資 源
前 言 序 本 書 是 開 放 電 腦 計 畫 的 硬 體 部 份, 描 述 如 何 設 計 電 腦 硬 體 的 方 法, 透 過 這 本 書, 我 們 希 望 讓 計 算 機 結 構 這 門 課 程, 變 成 是 可 以 動 手 實 作 的 我 們 相 信, 透 過 實 作 的 訓 練, 您 將 對 理 論 會 有 更 深 刻 的 體 會, 而 這 些 體 會, 將 會 進 一 步 讓 您 更 瞭 解 現 代 電 腦 工 業 的 結 構 是 如 何 建 構 出 來 的 授 權 聲 明 本 書 許 多 資 料 修 改 自 維 基 百 科, 採 用 創 作 共 用 : 姓 名 標 示 相 同 方 式 分 享 授 權, 若 您 想 要 修 改 本 書 產 生 衍 生 著 作 時, 至 少 應 該 遵 守 下 列 授 權 條 件 : 1. 標 示 原 作 者 姓 名 ( 包 含 該 文 章 作 者, 若 有 來 自 維 基 百 科 的 部 份 也 請 一 併 標 示 ) 2. 採 用 創 作 共 用 : 姓 名 標 示 相 同 方 式 分 享 的 方 式 公 開 衍 生 著 作 另 外 當 本 書 中 有 文 章 或 素 材 並 非 採 用 姓 名 標 示 相 同 方 式 分 享 時, 將 會 在 該 文 章 或 素 材 後 面 標 示 其 授 權, 此 時 該 文 章 將 以 該 標 示 的 方 式 授 權 釋 出, 請 修 改 者 注 意 這 些 授 權 標 示, 以 避 免 產 生 侵 權 糾 紛 例 如 有 些 文 章 可 能 不 希 望 被 作 為 商 業 性 使 用, 此 時 就 可 能 會 採 用 創 作 共 用 : 姓 名 標 示 非 商 業 性 相 同 方 式 分 享 的 授 權, 此 時 您 就 不 應 當 將 該 文 章 用 於 商 業 用 途 上 最 後 若 讀 者 有 需 要 轉 貼 或 修 改 這 些 文 章 使 用, 請 遵 守 創 作 共 用 的 精 神, 讓 大 家 都 可 以 在 開 放 原 始 碼 的 基 礎 上 逐 步 改 進 這 些 作 品
開 放 電 腦 計 畫 簡 介 如 果 您 是 資 工 系 畢 業 的 學 生, 必 然 會 上 過 計 算 機 結 構 編 譯 器 作 業 系 統 系 統 程 式 等 等 課 程, 這 些 課 程 都 是 設 計 出 一 台 電 腦 所 必 需 的 基 本 課 程 但 是 如 果 有 人 問 您 您 是 否 會 設 計 電 腦 呢?, 相 信 大 部 分 人 的 回 答 應 該 是 : 我 不 會, 也 沒 有 設 計 過 光 是 設 計 一 個 作 業 系 統, 就 得 花 上 十 年 的 工 夫, 遑 論 還 要 自 己 設 計 CPU 匯 流 排 組 譯 器 編 譯 器 作 業 系 統 等 等 因 此, 我 們 都 曾 經 有 過 這 樣 的 夢 想, 然 後 在 年 紀 越 大, 越 來 越 瞭 解 整 個 工 業 結 構 之 後, 我 們 就 放 棄 了 這 樣 一 個 夢 想, 因 為 我 們 必 須 與 現 實 妥 協 但 是, 身 為 一 個 大 學 教 師, 我 有 責 任 教 導 學 生, 告 訴 他 們 電 腦 是 怎 麼 做 出 來 的, 因 此 我 不 自 量 力 的 提 出 了 這 樣 一 個 計 畫, 那 就 是 開 放 電 腦 計 畫, 我 們 將 以 千 里 之 行 始 於 足 下 的 精 神, 設 計 出 一 台 全 世 界 最 簡 單 且 清 楚 的 電 腦, 包 含 軟 體 與 硬 體 從 2007 年 我 開 始 寫 系 統 程 式 這 本 書 以 來, 就 有 一 個 想 法 逐 漸 在 內 心 發 酵, 這 個 想 法 就 是 : 我 想 從 CPU 設 計 組 譯 器 虛 擬 機 編 譯 器 到 作 業 系 統, 自 己 打 造 一 台 電 腦, 於 是 開 放 電 腦 計 畫 就 誕 生 了! 那 麼 開 放 電 腦 計 畫 的 產 品 會 是 什 麼 呢? 應 該 有 些 人 會 認 為 是 一 套 自 行 編 寫 的 軟 硬 體 程 式, 當 然 這 部 份 是 包 含 在 開 放 電 腦 計 畫 當 中 的 但 是 更 重 要 的 事 情 是, 我 們 希 望 透 過 開 放 電 腦 計 畫 讓 學 生 能 夠 學 會 整 個 電 腦 的 軟 硬 體 設 計 方 式, 並 且 透 過 這 個 踏 腳 石 瞭 解 整 個 電 腦 軟 硬 體 工 業, 進 而 能 夠 達 到 以 理 論 指 導 實 務 以 實 務 驗 證 理 論 的 目 標 為 了 達 成 這 個 目 標, 我 們 將 開 放 電 腦 計 畫 分 成 三 個 階 段, 也 就 是 簡 單 設 計 ( 程 式 ) => 理 論 闡 述 ( 書 籍 ) => 開 源 實 作 ( 工 業 軟 硬 體 與 流 程 ), 整 體 的 構 想 說 明 如 下 : 1. 簡 單 設 計 ( 程 式 ): 採 用 Verilog + C 設 計 CPU 組 譯 器 編 譯 器 作 業 系 統 等 軟 硬 體, 遵 循 KISS (Keep It Simple and Stupid) 原 則, 不 考 慮 效 能 與 商 業 競 爭 力 等 問 題, 甚 至 在 實 用 性 上 進 行 了 不 少 妥 協, 一 律 採 用 容 易 理 解 為 最 高 指 導 原 則, 目 的 是 清 楚 的 展 現 整 個 軟 硬 體 系 統 的 架 構 2. 理 論 闡 述 ( 書 籍 ): 但 是 要 瞭 解 像 處 理 器 系 統 軟 體 編 譯 器 作 業 系 統 這 些 領 域, 只 有 程 式 是 不 夠 的 因 為 程 式 通 常 不 容 易 懂, 而 且 對 於 沒 有 背 景 知 識 的 人 而 言, 往 往 難 如 天 書 所 以 我 們 將 撰 寫 一 系 列 書 籍, 用 來 說 明 上 述 簡 單 程 式 的 設 計 原 理, 然 後 開 始 進 入 計 算 機 結 構 編 譯 器 作 業 系 統 系 統 程 式 的 理 論 體 系 中, 導 引 出 進 一 步 的 設 計 可 能 性 與 工 業 考 量 等 議 題 3. 開 源 實 作 ( 工 業 ): 一 但 有 了 前 述 的 理 論 與 實 作 基 礎 之 後, 我 們 就 會 採 用 開 放 原 始 碼 來 進 行 案 例 研 究 舉 例 而 言 在 計 算 機 結 構 上 我 們 會 以 ARM 為 實 務 核 心 編 譯 器 領 域 則 以 gcc, LLVM 為 研 究 標 的, 作 業 系 統 上 則 會 對 FreeRTOS Linux 等 進 行 案 例 研 究, 虛 擬 機 上 則 會 以 QEMU V8 等 開 源 案 例 為 研 究 對 象
圖 開 放 電 腦 計 畫 地 圖 根 據 以 上 規 劃, 本 書 乃 為 一 系 列 書 籍 中 的 一 本, 完 整 的 書 籍 架 構 如 下 : 開 放 電 腦 計 畫 書 籍 簡 易 程 式 工 業 實 作 系 統 程 式 as0, vm0, cc0, os0 gcc/llvm 計 算 機 結 構 mcu0, cpu0 ARM/OpenRISC 編 譯 器 c 0c, j0c g cc/llvm 作 業 系 統 os0, XINU, MINIX FreeRTOS, Linux 這 些 書 籍 分 別 描 述 不 同 的 面 向, 其 涵 蓋 範 圍 如 下 圖 所 示 :
圖 開 放 電 腦 計 畫 書 籍 圖 硬 體 : 計 算 機 結 構 在 硬 體 方 面, 我 們 將 自 行 設 計 兩 款 處 理 器, 一 款 是 用 來 展 示 簡 單 微 處 理 器 設 計 原 理 的 16 位 元 微 控 制 器 MCU0, 而 另 一 款 則 是 用 來 展 示 高 階 處 理 器 設 計 原 理 的 32 位 元 處 理 器 CPU0 透 過 MCU0, 我 們 希 望 展 示 一 顆 最 簡 易 微 處 理 器 的 設 計 方 法, 我 們 將 採 用 流 程 式 與 區 塊 式 的 方 法 分 別 實 作 一 遍, 讓 讀 者 可 以 分 別 從 硬 體 人 與 軟 體 人 的 角 度 去 體 會 處 理 器 的 設 計 方 式 由 於 流 程 式 的 方 法 比 較 簡 單, 因 此 我 們 會 先 用 此 法 進 行 設 計, 當 讀 者 理 解 何 謂 微 處 理 器 之 後, 在 將 同 樣 的 功 能 改 用 區 塊 式 的 方 法 實 作 一 遍, 這 樣 應 該 就 能 逐 漸 由 易 至 難 由 淺 入 深 了 在 MCU0 當 中, 我 們 採 用 CPU 與 記 憶 體 合 一 的 設 計 方 式, 這 種 方 式 比 較 像 系 統 單 晶 片 (SOC) 的 設 計 方 法, 其 記 憶 體 容 量 較 小, 因 此 可 以 直 接 用 Verilog 陣 列 宣 告 放 入 FPGA 當 中 使 用, 不 需 考 慮 外 部 DRAM 存 取 速 度 較 慢 的 問 題, 也 不 用 考 慮 記 憶 階 層 的 速 度 問 題, 因 此 設 計 起 來 會 相 對 容 易 許 多 接 著, 我 們 將 再 度 設 計 一 個 32 位 元 的 處 理 器 -- CPU0 並 透 過 CPU0 來 討 論 當 CPU 速 度 比 DRAM 記 憶 體 快 上 許 多 的 時 候, 如 何 能 透 過 快 取 (cache) 與 記 憶 體 管 理 單 元 (MMU) 達 到 又 快 又 大 的 目 的, 並 且 討 論 如 何 透 過 流 水 線 架 構 (Pipeline) 達 到 加 速 的 目 的, 這 些 都 屬 於 高 階 處 理 器 所 需 要 討 論 的 問 題 軟 體 : 系 統 程 式
有 了 MCU0 與 CPU0 等 硬 體 之 後, 我 們 就 可 以 建 構 運 作 於 這 些 硬 體 之 上 的 軟 體 了, 這 些 軟 體 包 含 組 譯 器 虛 擬 機 編 譯 器 作 業 系 統 等 等 我 們 已 經 分 別 用 C 與 JavaSript 建 構 出 簡 易 的 組 譯 器 虛 擬 機 編 譯 器 工 具 了, 讓 我 們 先 說 明 一 下 在 CPU0 上 這 些 程 式 的 使 用 方 法, 以 下 示 範 是 採 用 node.js+javascript 實 作 的 工 具 版 本, 因 此 必 須 安 裝 node.js 才 能 執 行 組 合 語 言 (Assembly Language) 接 著 讓 我 們 從 組 合 語 言 的 角 度, 來 看 看 CPU0 處 理 器 的 設 計, 以 下 是 一 個 可 以 計 算 1+2+...+10 的 程 式, 計 算 完 成 之 後 會 透 過 呼 叫 軟 體 中 斷 SWI 程 序 ( 類 似 DOS 時 代 的 INT 中 斷 ), 在 螢 幕 上 印 出 下 列 訊 息 1+...+10=55 以 下 的 檔 案 sum.as0 正 是 完 成 這 樣 功 能 的 一 個 CPU0 組 合 語 言 程 式 檔 案 :sum.as0 LD R1, sum ; R1 = sum = 0 LD R2, i ; R2 = i = 1 LDI R3, 10 ; R3 = 10 FOR: CMP R2, R3 ; if (R2 > R3) JGT EXIT ; goto EXIT ADD R1, R1, R2 ; R1 = R1 + R2 (sum = sum + i) ADDI R2, R2, 1 ; R2 = R2 + 1 ( i = i + 1) JMP FOR ; goto FOR EXIT: ST R1, sum ; sum = R1 ST R2, i ; i = R2 LD R9, msgptr ; R9= pointer(msg) = &msg SWI 3 ; SWI 3 : 印 出 R9 (=&msg) 中 的 字 串 MOV R9, R1 ; R9 = R1 = sum SWI 4 ; SWI 4 : 印 出 R9 (=R1=sum) 中 的 整 數 RET ; return 返 回 上 一 層 呼 叫 函 數 i: RESW 1 ; int i sum: WORD 0 ; int sum=0 msg: BYTE "1+...+10=", 0 ; char *msg = "sum=" msgptr: WORD msg ; char &msgptr = &msg 組 譯 器 (Assembler) 我 們 可 以 用 以 下 指 令 呼 叫 組 譯 器 AS0 對 上 述 檔 案 進 行 組 譯 :
node as0 sum.as0 sum.ob0 上 述 的 程 式 經 過 組 譯 之 後, 會 輸 出 組 譯 報 表, 如 下 所 示 sum.as0 的 組 譯 報 表 0000 LD R1,sum L 00 001F003C 0004 LD R2,i L 00 002F0034 0008 LDI R3,10 L 08 0830000A 000C FOR CMP R2,R3 A 10 10230000 0010 JGT EXIT J 23 2300000C 0014 ADD R1,R1,R2 A 13 13112000 0018 ADDI R2,R2,1 A 1B 1B220001 001C JMP FOR J 26 26FFFFEC 0020 EXIT ST R1,sum L 01 011F001C 0024 ST R2,i L 01 012F0014 0028 LD R9,msgptr L 00 009F0022 002C SWI 3 J 2A 2A000003 0030 MOV R9,R1 A 12 12910000 0034 SWI 2 J 2A 2A000002 0038 RET J 2C 2C000000 003C i RESW 1 D F0 00000000 0040 sum WORD 0 D F2 00000000 0044 msg BYTE "1+...+10=",0 D F3 312B2E2E2E2B31303D00 004E msgptr WORD msg D F2 00000044 最 後 組 譯 器 AS0 會 輸 出 機 器 碼 到 目 的 檔 sum.ob0 當 中, 其 內 容 如 下 所 示 sum.as0 的 機 器 碼 ( 以 16 進 位 顯 示 ) 001F003C 002F0034 0830000A 10230000 2300000C 13112000 1B220001 26FFFFEC 011F001C 012F0014 009F0022 2A000003 12910000 2A000002 2C000000 00000000 00000000 312B2E2E 2E2B3130 3D000000 0044 虛 擬 機 (Virtual Machine) 如 果 我 們 用 虛 擬 機 VM0 去 執 行 上 述 的 目 的 檔 sum.ob0, 會 看 到 程 式 的 執 行 結 果, 是 在 螢 幕 上 列 印 出 1+...+10=55, 以 下 是 我 們 的 操 作 過 程
1+...+10=55 編 譯 器 (Compiler) 當 然 一 個 完 整 的 現 代 電 腦 應 該 包 含 比 組 譯 器 更 高 階 的 工 具, 不 只 支 援 組 合 語 言, 還 要 支 援 高 階 語 言 因 此 我 們 設 計 了 一 個 稱 為 J0 的 高 階 語 言, 語 法 有 點 像 JavaScript, 但 卻 是 經 過 簡 化 的 版 本 然 後 我 們 又 設 計 了 一 個 可 以 用 來 編 譯 J0 語 言 的 編 譯 器, 稱 為 J0C (J0 Compiler), 可 以 用 來 將 J0 語 言 編 譯 成 中 間 碼, 也 可 以 直 接 將 中 間 碼 轉 換 為 CPU0 的 組 合 語 言 以 下 是 一 個 J0 語 言 的 範 例, 檔 案 :sum.j0 s = sum(10); return s; function sum(n) { s = 0; i=1; while (i<=10) { s = s + i; i++; } return s; } 當 我 們 使 用 j0c 編 譯 器 將 上 述 程 式 編 譯 之 後, 會 輸 出 兩 個 檔 案, 一 個 是 sum.ir, 是 編 譯 器 中 間 格 式 (Intermediate Representation, 虛 擬 碼 pcode) 的 輸 出 檔, 其 內 容 如 下 : D:\Dropbox\Public\web\oc\code>node j0c sum arg 10 call T1 sum = s T1 return s sum function param n = s 0 = i 1 L1 <= T2 i 10
L2 if0 T2 L2 + T3 s i = s T3 ++ i goto L1 return s f 另 一 個 是 將 上 述 中 間 格 式 轉 換 成 轉 換 成 CPU0 組 合 語 言 之 後 的 結 果, 如 下 所 示 : sum L1 else1 POP n LDI R1 0 ST R1 s LDI R1 1 ST R1 i LD R1 i LDI R2 10 LDI R3 0 CMP R1 R2 JLE else1 LDI R3 1 ST R3 T1 LDI R1 T1 CMP R1 0 JEQ L2 LD R1 s LD R2 i ADD R3 R1 R2 ST R3 T2 LDI R1 T2 ST R1 s LD R1 i ADDI R1 R1 1 ST R1 i
L2 JMP L1 LD R1 s RET LDI R1 10 PUSH R1 CALL sum ST R1 T3 LDI R1 T3 ST R1 s s WORD 0 i WORD 0 T1 WORD 0 T2 WORD 0 T3 WORD 0 上 述 由 j0c 所 編 譯 產 生 的 組 合 語 言, 感 覺 相 對 冗 長, 是 因 為 這 個 編 譯 器 是 最 簡 版 本, 完 全 沒 有 做 任 何 優 化 動 作, 甚 至 連 暫 存 器 都 是 每 次 重 新 載 入 的, 所 以 效 率 並 不 會 很 好 作 業 系 統 (Operating System) 當 然 囉! 一 個 完 整 的 電 腦 還 必 須 要 有 作 業 系 統, 不 過 如 果 是 嵌 入 式 系 統 的 話, 沒 有 作 業 系 統 也 沒 關 係, 只 要 將 全 部 的 程 式 連 結 在 一 起, 就 可 以 形 成 一 台 電 腦 了, 目 前 開 放 電 腦 計 畫 的 作 業 系 統 還 在 研 究 開 發 當 中, 希 望 很 快 就 能 提 供 大 家 一 個 最 簡 單 的 作 業 系 統 版 本 目 前 我 們 已 經 寫 了 一 個 可 以 進 行 兩 個 行 程 切 換 Task Switching 範 例, 接 著 我 們 將 參 考 UNIXv6, L4 等 作 業 系 統, 以 建 構 更 完 整 的 簡 易 作 業 系 統 結 語 當 然 即 使 我 們 從 CPU 硬 體 一 路 設 計 到 組 譯 器 虛 擬 機 編 譯 器 作 業 系 統 等, 未 來 仍 然 有 更 多 領 域 等 待 我 們 去 探 索, 例 如 網 路 模 組 TCP/IP Ethernet 無 線 RF 的 硬 體 模 組 繪 圖 卡 OpenGL... 等 等, 希 望 我 們 能 夠 用 最 簡 單 的 話 語, 將 這 些 電 腦 的 原 理 說 明 清 楚, 並 用 簡 單 的 方 式 實 作 得 更 完 整 參 考 文 獻 陳 鍾 誠 的 網 站 / 免 費 電 子 書 :Verilog 電 路 設 計 系 統 程 式 陳 鍾 誠 著, 旗 標 出 版 社. JavaScript (6) Node.js 命 令 列 程 式 設 計
電 腦 硬 體 架 構 電 腦 的 結 構 傳 統 的 電 腦 架 構, 最 經 典 的 模 型 是 由 數 學 大 師 馮 紐 曼 (Von Neumann) 所 描 述 的, 因 此 稱 為 馮 紐 曼 架 構, 如 以 下 兩 個 圖 片 所 示 : 圖 馮 紐 曼 架 構 (1) 圖 片 來 源 : http://en.wikipedia.org/wiki/file:von_neumann_architecture.svg 圖 馮 紐 曼 架 構 (2) 圖 片 來 源 : http://en.wikipedia.org/wiki/file:von_neumann_architecture.svg 在 早 期 晶 片 線 路 成 本 還 算 高 的 時 候, 最 常 見 的 馮 紐 曼 架 構 電 腦, 是 採 用 單 匯 流 排 的 架 構, 如 下 圖 所 示 :
圖 單 一 匯 流 排 的 馮 紐 曼 架 構 圖 片 來 源 : http://en.wikipedia.org/wiki/file:computer_system_bus.svg 但 是 自 從 RISC 精 簡 指 令 集 電 腦 出 現, 由 於 pipeline 必 須 讓 指 令 與 資 料 可 以 同 時 被 存 取, 很 多 電 腦 改 採 以 下 指 令 與 資 料 分 開 的 哈 佛 架 構 (Harvard Architecture): 圖 指 令 與 資 料 匯 流 排 分 開 的 哈 佛 架 構 圖 片 來 源 : http://en.wikipedia.org/wiki/file:harvard_architecture.svg 必 須 注 意 的 是, 雖 然 哈 佛 架 構 當 中 的 指 令 與 資 料 是 完 全 分 開 的, 但 是 如 果 真 的 將 指 令 與 資 料 個 記 憶 體 整 個 分 開 的 話, 會 需 要 兩 套 記 憶 體 與 匯 流 排, 這 會 讓 CPU 的 接 腳 數 大 增, 也 會 讓 記 憶 體 運 用 沒 效 率, 因 此 在 實 務 上, 通 常 是 將 快 取 記 憶 體 分 成 兩 邊, 一 邊 是 指 令 快 取, 一 邊 是 資 料 快 取, 這 樣 就 只 有 內 部 匯 流 排 需 要 分 開 成 兩 套, 而 CPU 對 外 則 只 要 一 套 匯 流 排 與 記 憶 體, 這 種 架 構 可 以 看 成 哈 佛 架 構 的 一 種 變 形 CPU0 處 理 器 CPU0 是 一 個 簡 易 的 32 位 元 單 匯 流 排 處 理 器, 其 架 構 如 下 圖 所 示, 包 含 R0..R15, IR, MAR, MDR 等 暫 存 器, 其 中 IR 是 指 令 暫 存 器, R0 是 一 個 永 遠 為 常 數 0 的 唯 讀 暫 存 器,R15 是 程 式 計 數 器 (Program Counter : PC),R14 是 連 結 暫 存 器 (Link Register : LR), R13 是 堆 疊 指 標 暫 存 器 (Stack Pointer : SP), 而 R12 是 狀 態 暫 存
器 (Status Word : SW) 圖 CPU0 的 架 構 圖 CPU0 的 指 令 集 CPU0 包 含 載 入 儲 存 運 算 指 令 跳 躍 指 令 堆 疊 指 令 等 四 大 類 指 令, 以 下 表 格 是 CPU0 的 指 令 編 碼 表, 記 載 了 CPU0 的 指 令 集 與 每 個 指 令 的 編 碼 格 式 指 令 OP 說 明 語 法 語 意 L LD 00 載 入 word LD Ra, [Rb+Cx] Ra=[Rb+Cx] L ST 01 儲 存 word ST Ra, [Rb+Cx] Ra=[Rb+Cx] L LDB 02 載 入 byte LDB Ra, [Rb+Cx] Ra=(byte)[Rb+Cx] L STB 03 儲 存 byte STB Ra, [Rb+Cx] Ra=(byte)[Rb+Cx] A LDR 04 LD 的 暫 存 器 版 LDR Ra, [Rb+Rc] Ra=[Rb+Rc] A STR 05 ST 的 暫 存 器 版 STR Ra, [Rb+Rc] Ra=[Rb+Rc] A LBR 06 LDB 的 暫 存 器 版 LBR Ra, [Rb+Rc] Ra=(byte)[Rb+Rc] A SBR 07 STB 的 暫 存 器 版 SBR Ra, [Rb+Rc] Ra=(byte)[Rb+Rc] L LDI 08 載 入 常 數 LDI Ra, Cx Ra=Cx A CMP 10 比 較 CMP Ra, Rb SW=Ra >=< Rb A MOV 12 移 動 MOV Ra, Rb Ra=Rb
A ADD 13 加 法 ADD Ra, Rb, Rc Ra=Rb+Rc A SUB 14 減 法 SUB Ra, Rb, Rc Ra=Rb-Rc A MUL 15 乘 法 MUL Ra, Rb, Rc Ra=Rb*Rc A DIV 16 除 法 DIV Ra, Rb, Rc Ra=Rb/Rc A AND 18 邏 輯 AND AND Ra, Rb, Rc Ra=Rb and Rc A OR 19 邏 輯 OR OR Ra, Rb, Rc Ra=Rb or Rc A XOR 1A 邏 輯 XOR XOR Ra, Rb, Rc Ra=Rb xor Rc A ADDI 1B 常 數 加 法 ADDI Ra, Rb, Cx Ra=Rb + Cx A ROL 1C 向 左 旋 轉 ROL Ra, Rb, Cx Ra=Rb rol Cx A ROR 1D 向 右 旋 轉 ROR Ra, Rb, Cx Ra=Rb ror Cx A SHL 1E 向 左 移 位 SHL Ra, Rb, Cx Ra=Rb << Cx A SHR 1F 向 右 移 位 SHR Ra, Rb, Cx Ra=Rb >> Cx J JEQ 20 跳 躍 ( 相 等 ) JEQ Cx if SW(=) PC=PC+Cx J JNE 21 跳 躍 ( 不 相 等 ) JNE Cx if SW(!=) PC=PC+Cx J JLT 22 跳 躍 (<) JLT Cx if SW(<) PC=PC+Cx J JGT 23 跳 躍 (>) JGT Cx if SW(>) PC=PC+Cx J JLE 24 跳 躍 (<=) JLE Cx if SW(<=) PC=PC+Cx J JGE 25 跳 躍 (>=) JGE Cx if SW(>=) PC=PC+Cx J JMP 26 跳 躍 ( 無 條 件 ) JMP Cx PC=PC+Cx J SWI 2A 軟 體 中 斷 SWI Cx LR=PC; PC=Cx; INT=1 J CALL 2B 跳 到 副 程 式 CALL Cx LR=PC; PC=PC+Cx J RET 2C 返 回 RET PC=LR J IRET 2D 中 斷 返 回 IRET PC=LR; INT=0 A PUSH 30 推 入 word PUSH Ra SP-=4; [SP]=Ra; A POP 31 彈 出 word POP Ra Ra=[SP]; SP+=4; A PUSHB 32 推 入 byte PUSHB Ra SP--; [SP]=Ra; (byte)
A POPB 33 彈 出 byte POPB Ra Ra=[SP]; SP++; (byte) CPU0 指 令 格 式 CPU0 所 有 指 令 長 度 均 為 32 位 元, 這 些 指 令 也 可 根 據 編 碼 方 式 分 成 三 種 不 同 的 格 式, 分 別 是 A 型 J 型 與 L 型 大 部 分 的 運 算 指 令 屬 於 A (Arithmatic) 型, 而 載 入 儲 存 指 令 通 常 屬 於 L (Load & Store) 型, 跳 躍 指 令 則 通 常 屬 於 J (Jump) 型, 這 三 種 型 態 的 指 令 格 式 如 下 圖 所 示 圖 CPU0 的 指 令 格 式 狀 態 暫 存 器 R12 狀 態 暫 存 器 (Status Word : SW) 是 用 來 儲 存 CPU 的 狀 態 值, 這 些 狀 態 是 許 多 旗 標 的 組 合 例 如, 零 旗 標 (Zero, 簡 寫 為 Z) 代 表 比 較 的 結 果 為 0, 負 旗 標 (Negative, 簡 寫 為 N) 代 表 比 較 的 結 果 為 負 值, 另 外 常 見 的 旗 標 還 有 進 位 旗 標 (Carry, 簡 寫 為 C), 溢 位 旗 標 (Overflow, 簡 寫 為 V) 等 等 下 圖 顯 示 了 CPU0 的 狀 態 暫 存 器 格 式, 最 前 面 的 四 個 位 元 N Z C V 所 代 表 的, 正 是 上 述 的 幾 個 旗 標 值 圖 CPU0 中 狀 態 暫 存 器 SW 的 結 構 條 件 旗 標 的 N Z 旗 標 值 可 以 用 來 代 表 比 較 結 果 是 大 於 (>) 等 於 (=) 還 是 小 於 (<), 當 執 行 CMP Ra, Rb 動 作 後, 會 有 下 列 三 種 可 能 的 情 形 1. 若 Ra > Rb, 則 N=0, Z=0 2. 若 Ra < Rb, 則 N=1, Z=0 3. 若 Ra = Rb, 則 N=0, Z=1 如 此, 用 來 進 行 條 件 跳 躍 的 JGT JGE JLT JLE JEQ JNE 指 令, 就 可 以 根 據 SW 暫 存 器 當 中 的 N Z 等 旗 標 決 定 是 否 進 行 跳 躍 SW 中 還 包 含 中 斷 控 制 旗 標 I (Interrupt) 與 T (Trap), 用 以 控 制 中 斷 的 啟 動 與 禁 止 等 行 為, 假 如 將 I 旗 標 設 定
為 0, 則 CPU0 將 禁 止 所 有 種 類 的 中 斷, 也 就 是 對 任 何 中 斷 都 不 會 起 反 應 但 如 果 只 是 將 T 旗 標 設 定 為 0, 則 只 會 禁 止 軟 體 中 斷 指 令 SWI (Software Interrupt), 不 會 禁 止 由 硬 體 觸 發 的 中 斷 SW 中 還 儲 存 有 處 理 器 模 式 的 欄 位,M=0 時 為 使 用 者 模 式 (user mode) 與 M=1 時 為 特 權 模 式 (super mode) 等, 這 在 作 業 系 統 的 設 計 上 經 常 被 用 來 製 作 安 全 保 護 功 能 在 使 用 者 模 式 當 中, 任 何 設 定 狀 態 暫 存 器 R12 的 動 作 都 會 被 視 為 是 非 法 的, 這 是 為 了 進 行 保 護 功 能 的 緣 故 但 是 在 特 權 模 式 中, 允 許 進 行 任 何 動 作, 包 含 設 定 中 斷 旗 標 與 處 理 器 模 式 等 位 元, 通 常 作 業 系 統 會 使 用 特 權 模 式 (M=1), 而 一 般 程 式 只 能 處 於 使 用 者 模 式 (M=0) 位 元 組 順 序 CPU0 採 用 大 者 優 先 (Big Endian) 的 位 元 組 順 序 (Byte Ordering), 因 此 代 表 值 越 大 的 位 元 組 會 在 記 憶 體 的 前 面 ( 低 位 址 處 ), 代 表 值 小 者 會 在 高 位 址 處 由 於 CPU0 是 32 位 元 的 電 腦, 因 此, 一 個 字 組 (Word) 占 用 4 個 位 元 組 (Byte), 因 此, 像 LD R1, [100] 這 樣 的 指 令, 其 實 是 將 記 憶 體 100-103 中 的 字 組 取 出, 存 入 到 暫 存 器 R1 當 中 LDB 與 STB 等 指 令, 其 中 的 B 是 指 Byte, 因 此,LDB R1, [100] 會 將 記 憶 體 100 中 的 byte 取 出, 載 入 到 R1 當 中 但 是, 由 於 R1 的 大 小 是 32 bits, 相 當 於 4 個 byte, 此 時,LDB 與 STB 指 令 到 底 是 存 取 四 個 byte 當 中 的 哪 一 個 byte 呢? 這 個 問 題 的 答 案 是 byte 3, 也 就 是 最 後 的 一 個 byte 中 斷 程 序 CPU0 的 中 斷 為 不 可 重 入 式 中 斷, 其 中 斷 分 為 軟 體 中 斷 SWI (Trap) 與 硬 體 中 斷 HWI (Interrupt) 兩 類 硬 體 中 斷 發 生 時, 中 段 代 號 INT_ADDR 會 從 中 段 線 路 傳 入, 此 時 執 行 下 列 動 作 : 1. LR=PC; INT=1 2. PC=INT_ADDR 軟 體 中 斷 SWI Cx 發 生 時, 會 執 行 下 列 動 作 : 1. LR=PC; INT=1 2. PC=Cx; 中 斷 最 後 可 以 使 用 IRET 返 回, 返 回 前 會 設 定 允 許 中 斷 狀 態 1. PC=LR; INT=0 CPU0 的 組 合 語 言 與 機 器 碼 接 著 讓 我 們 從 組 合 語 言 的 角 度, 來 看 看 CPU0 處 理 器 的 設 計, 以 下 是 一 個 可 以 計 算 1+2+...+10 的 程 式, 計 算 完 成 之 後 會 透 過 呼 叫 軟 體 中 斷 SWI 程 序 ( 類 似 DOS 時 代 的 INT 中 斷 ), 在 螢 幕 上 印 出 下 列 訊 息 1+...+10=55 以 下 的 檔 案 sum.as0 正 是 完 成 這 樣 功 能 的 一 個 CPU0 組 合 語 言 程 式
檔 案 :sum.as0 LD R1, sum ; R1 = sum = 0 LD R2, i ; R2 = i = 1 LDI R3, 10 ; R3 = 10 FOR: CMP R2, R3 ; if (R2 > R3) JGT EXIT ; goto EXIT ADD R1, R1, R2 ; R1 = R1 + R2 (sum = sum + i) ADDI R2, R2, 1 ; R2 = R2 + 1 ( i = i + 1) JMP FOR ; goto FOR EXIT: ST R1, sum ; sum = R1 ST R2, i ; i = R2 LD R9, msgptr ; R9= pointer(msg) = &msg SWI 3 ; SWI 3 : 印 出 R9 (=&msg) 中 的 字 串 MOV R9, R1 ; R9 = R1 = sum SWI 4 ; SWI 4 : 印 出 R9 (=R1=sum) 中 的 整 數 RET ; return 返 回 上 一 層 呼 叫 函 數 i: RESW 1 ; int i sum: WORD 0 ; int sum=0 msg: BYTE "1+...+10=", 0 ; char *msg = "sum=" msgptr: WORD msg ; char &msgptr = &msg 我 們 可 以 用 以 下 指 令 呼 叫 組 譯 器 AS0 對 上 述 檔 案 進 行 組 譯 : node as0 sum.as0 sum.ob0 上 述 的 程 式 經 過 組 譯 之 後, 會 輸 出 組 譯 報 表, 如 下 所 示 sum.as0 的 組 譯 報 表 0000 LD R1,sum L 00 001F003C 0004 LD R2,i L 00 002F0034 0008 LDI R3,10 L 08 0830000A 000C FOR CMP R2,R3 A 10 10230000 0010 JGT EXIT J 23 2300000C 0014 ADD R1,R1,R2 A 13 13112000 0018 ADDI R2,R2,1 A 1B 1B220001 001C JMP FOR J 26 26FFFFEC 0020 EXIT ST R1,sum L 01 011F001C 0024 ST R2,i L 01 012F0014
0028 LD R9,msgptr L 00 009F0022 002C SWI 3 J 2A 2A000003 0030 MOV R9,R1 A 12 12910000 0034 SWI 2 J 2A 2A000002 0038 RET J 2C 2C000000 003C i RESW 1 D F0 00000000 0040 sum WORD 0 D F2 00000000 0044 msg BYTE "1+...+10=",0 D F3 312B2E2E2E2B31303D00 004E msgptr WORD msg D F2 00000044 最 後 組 譯 器 AS0 會 輸 出 機 器 碼 到 目 的 檔 sum.ob0 當 中, 其 內 容 如 下 所 示 sum.as0 的 機 器 碼 ( 以 16 進 位 顯 示 ) 001F003C 002F0034 0830000A 10230000 2300000C 13112000 1B220001 26FFFFEC 011F001C 012F0014 009F0022 2A000003 12910000 2A000002 2C000000 00000000 00000000 312B2E2E 2E2B3130 3D000000 0044 如 果 我 們 用 虛 擬 機 VM0 去 執 行 上 述 的 目 的 檔 sum.ob0, 會 看 到 程 式 的 執 行 結 果, 是 在 螢 幕 上 列 印 出 1+...+10=55, 以 下 是 我 們 的 操 作 過 程 1+...+10=55 參 考 文 獻 系 統 程 式 ( 陳 鍾 誠 著, 旗 標 出 版 社 ) -- http://sp1.wikidot.com/main JavaScript (6) Node.js 命 令 列 程 式 設 計
硬 體 描 述 語 言 -- Verilog Verilog 與 VHDL 都 是 用 來 設 計 數 位 電 路 的 硬 體 描 述 語 言, 但 VHDL 在 1983 年 被 提 出 後,1987 防 部 和 IEEE 確 定 為 標 準 的 硬 體 描 述 語 言 年 被 美 國 國 Verilog 是 由 Gateway Design Automation 公 司 於 1984 年 開 始 發 展 的, Cadence Design Systems 公 司 於 1990 年 購 併 了 Gateway 公 司,Cadence 隨 後 將 Verilog 提 交 到 Open Verilog International 成 為 開 放 公 用 標 準,1995 年 Verilog 被 IEEE 認 可 成 為 IEEE 1364-1995 標 準, 簡 稱 為 Verilog-95 此 一 標 準 於 2001 年 更 新 後 成 為 Verilog- 2001 相 較 於 VHDL 而 言,Verilog 的 語 法 較 為 簡 潔, 因 此 經 常 被 專 業 的 數 位 電 路 設 計 者 採 用, 而 VHDL 的 使 用 族 群 則 有 較 多 的 初 學 者 當 我 們 想 學 習 數 位 電 路 設 計 時, 經 常 會 難 以 選 擇 要 用 哪 一 種 語 言, 因 為 VHDL 的 書 籍 與 教 材 似 乎 比 Verilog 多 一 些, 但 是 Verilog 的 高 階 設 計 電 路 ( 像 是 開 放 原 始 碼 CPU 等 ) 則 比 VHDL 多 很 多 筆 者 是 為 了 要 設 計 CPU 而 學 習 數 位 電 路 設 計 的, 因 此 決 定 學 習 Verilog 語 言, 而 非 VHDL 語 言 雖 然 筆 者 也 學 過 VHDL 語 言, 但 後 來 發 現 Verilog 相 當 好, 相 對 而 言 語 法 簡 潔 了 許 多, 因 此 筆 者 比 較 偏 好 Verilog 語 言 在 本 文 中, 我 們 將 介 紹 Verilog 的 基 本 語 法, 並 且 採 用 Icarus 作 為 主 要 開 發 測 試 工 具, 以 便 讓 讀 者 能 很 快 的 進 入 Verilog 硬 體 設 計 的 領 域 Verilog 基 礎 Verilog 的 基 本 型 態 在 一 般 的 程 式 語 言 當 中, 資 料 的 最 基 本 型 態 通 常 是 位 元 (bit), 但 是 在 Verilog 這 種 硬 體 描 述 語 言 當 中, 我 們 必 須 有 面 向 硬 體 的 思 考 方 式, 因 此 最 基 本 的 型 態 從 位 元 轉 換 為 線 路 (wire) 一 條 線 路 的 可 能 值, 除 了 0 與 1 之 外, 還 有 可 能 是 未 定 值 X, 以 及 高 阻 抗 Z, 如 下 表 所 示 : 值 意 義 說 明 0 低 電 位 布 林 代 數 中 的 假 值 1 高 電 位 布 林 代 數 中 的 真 值 Z 高 阻 抗 三 態 緩 衝 器 的 輸 出, 高 阻 抗 斷 線 X 未 定 值 像 是 線 路 未 初 始 化 之 前, 以 及 有 0,1 兩 者 衝 突 的 線 路 值, 或 者 是 輸 入 為 Z 的 輸 出 值 其 中 的 0 對 應 到 低 電 位 1 對 應 到 高 電 位, 這 是 比 較 容 易 理 解 的 部 分, 但 是 未 定 值 X 與 高 阻 抗 Z 各 代 表 甚 麼 意 義 呢? 對 於 一 條 沒 有 阻 抗 的 線 路 而 言, 假 如 我 們 在 某 點 對 該 線 路 輸 出 1, 另 一 點 對 該 線 路 輸 出 0, 那 麼 這 條 線 路 到 底 應 該 是 高 電 位 還 是 低 電 位 呢?
圖 造 成 未 定 值 X 的 情 況 對 於 這 種 衝 突 的 情 況,Verilog 採 用 X 來 代 表 該 線 路 的 值 而 高 阻 抗, 則 基 本 上 是 代 表 斷 線, 您 可 以 想 像 該 線 路 如 果 是 非 導 體, 例 如 塑 膠 木 頭 開 關 開 路 或 者 是 處 於 高 阻 抗 情 況 的 半 導 體 等, 就 會 使 用 者 種 Z 值 來 代 表 根 據 這 樣 的 四 種 線 路 狀 態, 一 個 原 本 簡 易 的 AND 閘, 在 數 位 邏 輯 中 只 要 用 2*2 的 真 值 表 就 能 表 示 了, 但 在 Verilog 當 中 則 有 4*4 種 可 能 的 情 況, 以 下 是 Verilog 中 各 種 運 算 (AND, OR, XOR, XNOR) 在 這 四 種 型 態 上 的 真 值 表 定 義 : AND 0 1 X Z (&) 0 0 0 0 0 1 0 1 X X X 0 X X X Z 0 X X X OR ( ) 0 1 X Z 0 0 1 X X 1 1 1 1 1 X X 1 X X Z X 1 X X XOR(^) 0 1 X Z 0 0 1 X X 1 1 0 X X X X X X X Z 1 X X X XNOR(^~) 0 1 X Z 0 1 0 X X 1 0 1 X X X X X X X Z X X X X 在 Verilog 當 中, 如 果 我 們 要 宣 告 一 條 線 路, 只 要 用 下 列 語 法 就 可 以 了 : wire w1; 如 果 我 們 想 一 次 宣 告 很 多 條 線 路, 那 麼 我 們 可 以 用 很 多 個 變 數 描 述 :
wire w, x, y, z; 但 是 如 果 我 們 想 宣 告 一 整 個 排 線 ( 例 如 匯 流 排 ), 那 我 們 就 可 以 用 下 列 的 陣 列 語 法 : wire [31:0] bus; 如 果 想 要 一 次 宣 告 很 多 組 排 線, 那 我 們 就 可 以 用 下 列 的 陣 列 群 語 法 : wire [31:0] bus [0:3]; 當 然 除 了 線 路 之 外,Verilog 還 有 可 以 穩 定 儲 存 位 元 的 型 態, 稱 為 reg ( 暫 存 器 ),reg 可 以 用 來 儲 存 位 元, 而 非 像 線 路 一 樣 只 是 一 種 連 接 方 式 而 已, 以 下 是 一 些 reg 的 宣 告 方 式 : reg w; reg x, y, z; reg [31:0] r1; reg [31:0] R [0:15]; // 宣 告 一 位 元 的 暫 存 器 變 數 w // 宣 告 三 個 一 位 元 的 暫 存 器 變 數 x, y, z // 宣 告 32 位 元 的 暫 存 器 r1 // 宣 告 16 個 32 位 元 的 暫 存 器 群 組 R[0..15] 在 Verilog 中,wire 與 reg 是 比 較 常 用 的 基 本 型 態, 另 外 還 有 一 些 較 不 常 用 的 基 本 型 態, 像 是 tri ( 三 態 線 路 ) trireg ( 三 態 暫 存 器 ) integer ( 整 數 ) 等, 在 此 我 們 先 不 進 行 介 紹 Icarus : Verilog 的 編 譯 執 行 工 具 Icarus 是 由 Stephen Williams 所 設 計 的 Verilog 開 發 工 具, 採 用 GPL 授 權 協 議, 並 且 可 以 在 Linux, BSD, OS X, MS Windows 等 環 境 下 執 行 Icarus 支 援 Verilog 的 IEEE 1995 IEEE 2001 和 IEEE 2005 三 種 標 準 語 法, 也 支 援 部 分 的 SystemVerilog 語 法, 其 官 方 網 站 網 址 如 下 : http://iverilog.icarus.com/ 如 果 您 是 MS Windows 的 使 用 者, 可 以 從 以 下 網 址 中 下 載 Icarus 的 MS Windows 版 本, 其 安 裝 非 常 容 易 : http://bleyer.org/icarus/ 範 例 1:XOR3 的 電 路 module xor3(input a, b, c, output abc); wire ab; xor g1(ab, a, b); xor g2(abc, c, ab); module module xor3test;
reg a, b, c; wire abc; xor3 g(a,b,c, abc); initial begin a = 0; b = 0; c = 0; always #50 begin a = a+1; $monitor("%4dns monitor: a=%d b=%d c=%d a^b^c=%d", $stime, a, b, c, abc ); always #100 begin b = b+1; always #200 begin c = c+1; initial #2000 $finish; module Icarus 執 行 結 果 D:\ccc101\icarus\ccc>iverilog -o xor3test xor3test.v D:\ccc101\icarus\ccc>vvp xor3test 50ns monitor: a=1 b=0 c=0 a^b^c=1 100ns monitor: a=0 b=1 c=0 a^b^c=1 150ns monitor: a=1 b=1 c=0 a^b^c=0
200ns monitor: a=0 b=0 c=1 a^b^c=1 250ns monitor: a=1 b=0 c=1 a^b^c=0 300ns monitor: a=0 b=1 c=1 a^b^c=0 350ns monitor: a=1 b=1 c=1 a^b^c=1 400ns monitor: a=0 b=0 c=0 a^b^c=0 450ns monitor: a=1 b=0 c=0 a^b^c=1 500ns monitor: a=0 b=1 c=0 a^b^c=1 550ns monitor: a=1 b=1 c=0 a^b^c=0 600ns monitor: a=0 b=0 c=1 a^b^c=1 650ns monitor: a=1 b=0 c=1 a^b^c=0 700ns monitor: a=0 b=1 c=1 a^b^c=0 750ns monitor: a=1 b=1 c=1 a^b^c=1 800ns monitor: a=0 b=0 c=0 a^b^c=0 850ns monitor: a=1 b=0 c=0 a^b^c=1 900ns monitor: a=0 b=1 c=0 a^b^c=1 950ns monitor: a=1 b=1 c=0 a^b^c=0 1000ns monitor: a=0 b=0 c=1 a^b^c=1 1050ns monitor: a=1 b=0 c=1 a^b^c=0 1100ns monitor: a=0 b=1 c=1 a^b^c=0 1150ns monitor: a=1 b=1 c=1 a^b^c=1 1200ns monitor: a=0 b=0 c=0 a^b^c=0 1250ns monitor: a=1 b=0 c=0 a^b^c=1 1300ns monitor: a=0 b=1 c=0 a^b^c=1 1350ns monitor: a=1 b=1 c=0 a^b^c=0 1400ns monitor: a=0 b=0 c=1 a^b^c=1 1450ns monitor: a=1 b=0 c=1 a^b^c=0 1500ns monitor: a=0 b=1 c=1 a^b^c=0 1550ns monitor: a=1 b=1 c=1 a^b^c=1 1600ns monitor: a=0 b=0 c=0 a^b^c=0 1650ns monitor: a=1 b=0 c=0 a^b^c=1 1700ns monitor: a=0 b=1 c=0 a^b^c=1 1750ns monitor: a=1 b=1 c=0 a^b^c=0 1800ns monitor: a=0 b=0 c=1 a^b^c=1 1850ns monitor: a=1 b=0 c=1 a^b^c=0 1900ns monitor: a=0 b=1 c=1 a^b^c=0 1950ns monitor: a=1 b=1 c=1 a^b^c=1 2000ns monitor: a=0 b=0 c=0 a^b^c=0
仔 細 觀 察 上 述 輸 出 結 果, 您 會 發 現 這 個 結 果 與 真 值 表 的 內 容 完 全 一 致, 因 此 驗 證 了 該 設 計 的 正 確 性! 透 過 這 種 方 式, 您 就 可 以 用 Verilog 設 計 電 路 的 程 式, 然 後 用 Icarus 編 譯 並 驗 證 電 路 是 否 正 確 區 塊 式 設 計 閘 級 的 線 路 設 計 方 法 Verilog 既 然 是 硬 體 描 述 語 言, 那 當 然 會 有 邏 輯 閘 的 表 示 法,Verilog 提 供 的 邏 輯 閘 有 and, nand, or, nor, xor, xnor, not 等 元 件, 因 此 您 可 以 用 下 列 Verilog 程 式 描 述 一 個 全 加 器 : module fulladder (input a, b, c_in, output sum, c_out); wire s1, c1, c2; xor g1(s1, a, b); xor g2(sum, s1, c_in); and g3(c1, a,b); and g4(c2, s1, c_in) ; or g5(c_out, c2, c1) ; module 上 述 程 式 所 對 應 的 電 路 如 下 圖 所 示 : 全 加 器 電 路 圖 這 些 邏 輯 閘 並 不 受 限 於 兩 個 輸 入, 也 可 以 是 多 個 輸 入 的, 例 如 以 下 範 例 中 的 g 閘, 就 一 次 將 三 個 輸 入 a, b, c_in 進 行 xor 運 算, 產 生 輸 出 sum 的 結 果 xor g(sum, a, b, c_in);
範 例 2: 全 加 器 的 閘 級 設 計 傳 統 的 數 位 邏 輯 課 程 當 中, 我 們 通 常 會 用 邏 輯 閘 的 組 合 方 式, 來 設 計 出 所 要 的 電 路, 以 下 我 們 就 用 全 加 器 當 範 例, 說 明 如 何 用 閘 級 的 語 法, 在 Verilog 當 中 設 計 數 位 電 路 全 加 器 總 共 有 3 個 輸 入 (a, b, c_in), 兩 個 輸 出 值 (sum, c_out), 其 真 值 表 如 下 所 示 : a b c_in c_out sum 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1 0 1 1 1 0 1 0 0 0 1 1 0 1 1 0 1 1 0 1 0 1 1 1 1 1 根 據 這 個 真 值 表, 我 們 可 以 用 卡 諾 圖 得 到 化 簡 後 的 電 路 ( 但 必 須 注 意 的 是, 卡 諾 圖 化 簡 出 來 的 電 路 只 有 AND, OR, NOT, 沒 有 XOR), 然 後 根 據 化 簡 後 的 算 式 繪 製 電 路 圖 ( 在 此 範 例 中,c_out 可 以 採 用 卡 諾 圖 化 簡 出 來, 但 sum 使 用 的 並 非 化 簡 的 結 果, 而 是 以 經 驗 得 到 的 XOR 組 合 式 ) 當 您 完 成 邏 輯 運 算 式 設 計 之 後, 就 可 以 用 TinyCAD 這 個 軟 體, 繪 製 出 全 加 器 的 電 路 如 下 圖 所 示 : 用 TinyCAD 繪 製 的 全 加 器 電 路 圖 接 著 我 們 可 以 按 照 以 上 的 線 路, 根 據 Verilog 的 語 法, 設 計 出 對 應 元 件 與 測 試 程 式 如 下 所 示 :
程 式 :fulladder.v // 以 下 為 全 加 器 模 組 的 定 義 module fulladder (input a, b, c_in, output sum, c_out); wire s1, c1, c2; xor g1(s1, a, b); xor g2(sum, s1, c_in); and g3(c1, a,b); and g4(c2, s1, c_in) ; or g5(c_out, c2, c1) ; module // 以 下 為 測 試 程 式 module main; reg a, b, c_in; wire sum, c_out; fulladder fa1(a, b, c_in, sum, c_out); initial begin a = 0; b = 0; c_in = 0; $monitor("%04dns monitor: a=%d b=%d c_in=%d c_out=%d sum=%d", $stime, a, b, c_in, c_out, sum); #1000 $finish; always #50 c_in = c_in+1; always #100 b = b+1; always #200 a = a+1; module 然 後 我 們 就 可 以 利 用 Icarus 進 行 編 譯 與 測 試, 看 看 fulladder.v 的 模 擬 執 行 結 果 是 否 正 確 編 譯 執 行 結 果 :
D:\Dropbox\Public\pmag\201306\code>iverilog -o fulladder fulladder.v D:\Dropbox\Public\pmag\201306\code>vvp fulladder 0000ns monitor: a=0 b=0 c_in=0 c_out=0 sum=0 0050ns monitor: a=0 b=0 c_in=1 c_out=0 sum=1 0100ns monitor: a=0 b=1 c_in=0 c_out=0 sum=1 0150ns monitor: a=0 b=1 c_in=1 c_out=1 sum=0 0200ns monitor: a=1 b=0 c_in=0 c_out=0 sum=1 0250ns monitor: a=1 b=0 c_in=1 c_out=1 sum=0 0300ns monitor: a=1 b=1 c_in=0 c_out=1 sum=0 0350ns monitor: a=1 b=1 c_in=1 c_out=1 sum=1 0400ns monitor: a=0 b=0 c_in=0 c_out=0 sum=0 0450ns monitor: a=0 b=0 c_in=1 c_out=0 sum=1 0500ns monitor: a=0 b=1 c_in=0 c_out=0 sum=1 0550ns monitor: a=0 b=1 c_in=1 c_out=1 sum=0 0600ns monitor: a=1 b=0 c_in=0 c_out=0 sum=1 0650ns monitor: a=1 b=0 c_in=1 c_out=1 sum=0 0700ns monitor: a=1 b=1 c_in=0 c_out=1 sum=0 0750ns monitor: a=1 b=1 c_in=1 c_out=1 sum=1 0800ns monitor: a=0 b=0 c_in=0 c_out=0 sum=0 0850ns monitor: a=0 b=0 c_in=1 c_out=0 sum=1 0900ns monitor: a=0 b=1 c_in=0 c_out=0 sum=1 0950ns monitor: a=0 b=1 c_in=1 c_out=1 sum=0 1000ns monitor: a=1 b=0 c_in=0 c_out=0 sum=1 習 題 1: 請 證 明 nand 閘 是 全 能 的 ( 提 示 : 只 要 用 nand 做 出 and, or, not 就 行 了 ) 習 題 2: 請 用 卡 諾 圖 去 化 簡 全 加 器 的 Cout 電 路 習 題 3: 請 寫 一 個 Verilog 程 式 用 nand 兜 出 or 電 路, 並 測 試 之 區 塊 式 設 計 的 注 意 事 項 當 您 採 用 區 塊 式 設 計 時, 有 一 些 常 見 的 初 學 者 錯 誤 必 須 注 意, 列 舉 如 下 : 1. assign 的 指 定 是 針 對 線 路, 而 暫 存 器 的 指 定 必 須 放 在 always 或 initial 區 塊 裏 因 此, 下 列 程 式 中 在 assign 指 定 reg 型 態 的 變 數 o1 = i1 是 錯 的, 必 須 將 reg 去 掉, 或 者 改 放 到 always 區 塊 裏
圖 在 assign 裏 只 能 指 定 wire 型 態 的 變 數, 不 能 指 定 reg 型 態 的 2. 相 反 的,always 區 塊 裏 的 指 定, 只 能 針 對 reg 型 態 的 變 數 進 行, 不 能 套 用 在 線 路 (wire: 含 input, output, inout) 型 態 的 變 數 上 因 此 下 列 程 式 當 中 若 沒 有 將 o1 加 上 reg 型 態, 則 會 發 生 錯 誤 圖 在 always 裏 才 能 指 定 reg 型 態 的 變 數 3. 上 述 的 參 數 output reg o1 其 實 是 一 種 將 暫 存 器 與 線 路 同 時 宣 告 的 縮 寫, 事 實 上 我 們 可 以 將 該 宣 告 拆 開 成 相 同 名 稱 的 兩 部 份 (output o1, reg o1) 或 者 甚 至 乾 脆 使 用 不 同 的 名 稱 來 宣 告 暫 存 器, 然 後 再 透 過 assign 將 線 路 與 暫 存 器 綁 在 一 起, 這 三 種 寫 法 的 意 義 其 實 都 是 相 同 的, 請 參 考 下 圖 :
圖 同 時 具 備 wire + reg 的 宣 告 方 式 4. assign 裏 面 除 了 指 定 敘 述 = 與 基 本 運 算 (&!^...) 之 外, 還 可 以 用 (cond)?case1:case2; 的 這 種 語 法 舉 例 而 言, 以 下 是 微 控 制 器 mcu0 裏 控 制 單 元 的 範 例, 您 可 以 看 到 類 似 層 次 性 if else 的 語 法 也 可 用 這 種 方 式 在 aluop 這 個 語 句 上 實 現 module control(input [3:0] op, input z, output mw, aw, pcmux, sww, outpu t [3:0] aluop); assign mw=(op==mcu0.st); assign aw=(op==mcu0.ld op==mcu0.add); assign sww=(op==mcu0.cmp); assign pcmux=(op==mcu0.jmp (op==mcu0.jeq && z)); assign aluop=(op==mcu0.ld)?alu0.apass: (op==mcu0.cmp)?alu0.cmp: (op==mcu0.add)?alu0.add:alu0.zero; module 流 程 式 設 計 所 謂 RTL 是 Register Transfer Language 的 縮 寫, 也 就 是 暫 存 器 轉 換 語 言, 這 種 寫 法 與 C, Java 等 高 階 語 言 非 常 相 似, 因 此 讓 程 式 人 也 有 機 會 透 過 Verilog 設 計 自 己 的 硬 體 舉 例 而 言, 在 數 位 邏 輯 當 中, 多 工 器 是 一 個 很 有 用 的 電 路, 假 如 我 們 想 設 計 一 個 二 選 一 的 多 工 器, 那 麼 我 們 可 以 很 直 覺 得 用 以 下 的 RTL 寫 法, 去 完 成 這 樣 的 電 路 設 計 module mux(f, a, b, sel);
output f; input a, b, sel; reg f; // reg 型 態 會 記 住 某 些 值, 直 到 被 某 個 assign 指 定 改 變 為 止 always @(a or b or sel) // 當 任 何 變 數 改 變 的 時 候, 會 執 行 內 部 區 塊 if (sel) f = a; // Always 內 部 的 區 塊 採 用 imperative 程 式 語 言 的 寫 法 else f = b; module 對 於 上 述 程 式, 您 還 可 以 進 一 步 的 將 參 數 部 分 化 簡, 將 型 態 寫 入 到 參 數 中, 成 為 以 下 的 形 式 : module mux(output reg f, input a, b, sel); always @(a or b or sel) // 當 任 何 變 數 改 變 的 時 候, 會 執 行 內 部 區 塊 if (sel) f = a; // Always 內 部 的 區 塊 採 用 imperative 程 式 語 言 的 寫 法 else f = b; module 在 verilog 當 中,if, case 等 陳 述 一 定 要 放 在 always 或 initial 的 理 面,always @(cond) 代 表 在 cond 的 條 件 之 下 要 執 行 該 區 塊, 例 如 上 述 的 always @(a or b or sel) 則 是 在 a, b, 或 sel 有 改 變 的 時 後, 就 必 須 執 行 裏 面 的 動 作 有 時 我 們 只 希 望 在 波 型 的 正 邊 緣 或 負 邊 緣 時, 才 執 行 某 些 動 作, 這 時 候 就 可 以 用 posedge 或 negedge 這 兩 個 修 飾 詞, 例 如 以 下 的 程 式 : always @(posedge clock) begin // 當 clock 時 脈 在 正 邊 緣 時 才 執 行 f = a; 而 initial 則 通 常 是 在 測 試 程 式 test bench 當 中 使 用 的, 在 一 開 始 初 始 化 的 時 後, 可 以 透 過 initial 設 定 初 值, 例 如 以 下 的 程 式 : initial begin clock = 0 Verilog 程 式 的 許 多 地 方, 都 可 以 用 #delay 指 定 時 間 延 遲, 例 如 #50 就 是 延 遲 50 單 位 的 時 間 ( 通 常 一 單 位 時 間 是 一 奈 秒 ns) 舉 例 而 言, 假 如 我 們 想 要 每 個 50 奈 秒 讓 clock 變 化 一 次, 那 麼 我 們 就 可 以 用 下 列 寫 法 達 到 目 的 : always #50 begin clock = ~clock; // 將 clock 反 相 (0 變 1 1 變 0)
以 上 的 延 遲 也 可 以 寫 在 裡 面, 而 不 是 直 接 寫 在 always 後 面, 例 如 改 用 以 下 寫 法, 也 能 得 到 相 同 的 結 果 always begin #50; clock = ~clock; // 將 clock 反 相 (0 變 1 1 變 0) 範 例 3: 計 數 器 的 RTL 設 計 接 著 讓 我 們 用 一 個 整 合 的 計 數 器 範 例, 來 示 範 這 些 語 法 的 實 際 用 途, 以 下 是 我 們 的 程 式 內 容 檔 案 :counter.v // 定 義 計 數 器 模 組 counter, 包 含 重 置 reset, 時 脈 clock 與 暫 存 器 count module counter(input reset, clock, output reg [7:0] count); always @(reset) // 當 reset 有 任 何 改 變 時 if (reset) count = 0; // 如 果 reset 是 1, 就 將 count 重 置 為 0 always @(posedge clock) begin // 在 clock 時 脈 的 正 邊 緣 時 count = count + 1; // 將 count 加 1 module module main; // 測 試 主 程 式 開 始 wire [7:0] i; // i: 計 數 器 的 輸 出 值 reg reset, clock; // reset: 重 置 訊 號, clock: 時 脈 // 宣 告 一 個 counter 模 組 c0 計 數 器 的 值 透 過 線 路 i 輸 出, 以 便 觀 察 counter c0(reset, clock, i); initial begin $display("%4dns: reset=%d clock=%d i=%d", $stime, reset, clock, i); // 印 出 0ns: reset=x clock=x i= x #10 reset = 1; clock=0; // 10ns 之 後, 將 啟 動 重 置 訊 號, 並 將 clock 初 值 設 為 0 $display("%4dns: reset=%d clock=%d i=%d", $stime, reset, clock, i); // 印 出 10ns: reset=1 clock=0 i= x #10 reset = 0; // 又 經 過 10ns 之 後, 重 置 完 畢, 將 reset 歸 零 $display("%4dns: reset=%d clock=%d i=%d", $stime, reset, clock, i); //
印 出 20ns: reset=0 clock=0 i= 0 #500 $finish; // 再 經 過 500ns 之 後, 結 束 程 式 always #40 begin // 延 遲 40ns 之 後, 開 始 作 下 列 動 作 clock=~clock; // 將 時 脈 反 轉 (0 變 1 1 變 0) #10; // 再 延 遲 10ns 之 後 $display("%4dns: reset=%d clock=%d i=%d", $stime, reset, clock, i); // 印 出 reset, clock 與 i 等 變 數 值 module 在 上 述 程 式 中,$display() 函 數 可 以 用 來 顯 示 變 數 的 內 容, 其 作 用 就 像 C 語 言 的 printf() 一 樣 不 過 由 於 Verilog 設 計 的 是 硬 體, 因 此 像 $display() 這 樣 前 面 有 錢 字 $ 符 號 的 指 令, 其 實 是 不 會 被 合 成 為 電 路 的, 只 是 方 便 除 錯 時 使 用 而 已 以 下 是 我 們 用 icarus 軟 體 編 譯 並 執 行 上 述 程 式 的 過 程 與 輸 出 結 果 : D:\Dropbox\Public\pmag\201307\code>iverilog -o counter counter.v D:\Dropbox\Public\pmag\201307\code>vvp counter 0ns: reset=x clock=x i= x 10ns: reset=1 clock=0 i= x 20ns: reset=0 clock=0 i= 0 50ns: reset=0 clock=1 i= 1 100ns: reset=0 clock=0 i= 1 150ns: reset=0 clock=1 i= 2 200ns: reset=0 clock=0 i= 2 250ns: reset=0 clock=1 i= 3 300ns: reset=0 clock=0 i= 3 350ns: reset=0 clock=1 i= 4 400ns: reset=0 clock=0 i= 4 450ns: reset=0 clock=1 i= 5 500ns: reset=0 clock=0 i= 5 您 可 以 看 到, 在 一 開 始 的 時 候 以 下 的 initial 區 塊 會 被 執 行, 但 由 於 此 時 reset, clock, i 都 尚 未 被 賦 值, 所 以 第 一 個 $display() 印 出 了 代 表 未 定 值 的 x 符 號 initial begin
$display("%4dns: reset=%d clock=%d i=%d", $stime, reset, clock, i); // 印 出 0ns: reset=x clock=x i= x #10 reset = 1; clock=0; // 10ns 之 後, 將 啟 動 重 置 訊 號, 並 將 clock 初 值 設 為 0 $display("%4dns: reset=%d clock=%d i=%d", $stime, reset, clock, i); // 印 出 10ns: reset=1 clock=0 i= x #10 reset = 0; // 又 經 過 10ns 之 後, 重 置 完 畢, 將 reset 歸 零 $display("%4dns: reset=%d clock=%d i=%d", $stime, reset, clock, i); // 印 出 20ns: reset=0 clock=0 i= 0 #500 $finish; // 再 經 過 500ns 之 後, 結 束 程 式 接 著 #10 reset = 1; clock=0 指 令 在 延 遲 10ns 後, 執 行 reset=1; clock=0, 於 是 後 來 的 $display() 就 印 出 了 10ns: reset=1 clock=0 i= x 的 結 果 但 是 就 在 reset 被 設 為 1 的 時 候, 由 於 reset 的 值 有 所 改 變, 因 此 下 列 模 組 中 的 always @(reset) 被 觸 發 了, 於 是 開 始 執 行 if (reset) count = 0 這 個 陳 述, 將 count 暫 存 器 設 定 為 0 module counter(input reset, clock, output reg [7:0] count); always @(reset) // 當 reset 有 任 何 改 變 時 if (reset) count = 0; // 如 果 reset 是 1, 就 將 count 重 置 為 0 always @(posedge clock) begin // 在 clock 時 脈 的 正 邊 緣 時 count = count + 1; // 將 count 加 1 module 然 後 #10 reset = 0 指 令 又 在 延 遲 10ns 後 執 行 了 reset = 0, 之 後 再 用 $display() 時, 由 於 count 已 經 被 設 定 為 0, 所 以 此 時 印 出 的 結 果 為 20ns: reset=0 clock=0 i= 0 initial 區 塊 的 最 後 一 個 陳 述,#500 $finish, 會 在 520ns 的 時 候 才 執 行, 執 行 時 $finish 會 將 整 個 測 試 程 式 結 束 但 在 程 式 結 束 之 前, 以 下 的 程 式 會 在 延 遲 40ns 之 後, 開 始 將 clock 反 相, 然 後 再 等 待 10ns 之 後 用 $display() 印 出 變 數 內 容, 因 此 整 個 區 塊 每 50ns (=40ns+10ns) 會 被 執 行 一 次 always #40 begin // 延 遲 40ns 之 後, 開 始 作 下 列 動 作 clock=~clock; // 將 時 脈 反 轉 (0 變 1 1 變 0) #10; // 再 延 遲 10ns 之 後 $display("%4dns: reset=%d clock=%d i=%d", $stime, reset, clock, i); // 印 出 reset, clock 與 i 等 變 數 值
所 以 您 才 會 看 到 像 下 面 的 輸 出 結 果, 如 果 仔 細 觀 察, 會 發 現 clock 每 50ns 變 換 一 次, 符 合 上 述 的 程 式 邏 輯, 而 且 每 當 clock 從 0 變 成 1 的 正 邊 緣, 就 會 觸 發 counter 模 組, 讓 count 變 數 加 1, 並 且 透 過 線 路 i 的 輸 出 被 我 們 觀 察 到 50ns: reset=0 clock=1 i= 1 100ns: reset=0 clock=0 i= 1 150ns: reset=0 clock=1 i= 2 200ns: reset=0 clock=0 i= 2 250ns: reset=0 clock=1 i= 3 300ns: reset=0 clock=0 i= 3 ( 註 : 或 許 您 有 注 意 到 上 期 當 中 我 們 用 $monitor() 來 觀 察 全 加 器 的 輸 出,$display() 與 $monitor() 的 語 法 幾 乎 一 模 一 樣, 但 是 $display() 是 顯 示 該 時 間 點 的 變 數 內 容, 而 $monitor() 則 會 在 受 觀 察 的 變 數 有 改 變 時 就 列 印 變 數 內 容, 兩 者 的 的 功 能 有 明 顯 的 差 異 ) 阻 塞 vs. 非 阻 塞 (Blocking vs. Nonblocking) 您 可 能 會 注 意 到 在 Verilog 當 中 有 兩 種 指 定 方 式, 一 種 用 = 表 示, 另 一 種 用 <= 表 示, 這 兩 種 指 定 方 法 看 來 很 類 似, 但 意 義 上 卻 有 很 細 緻 的 差 異, 一 般 Verilog 初 學 者 往 往 分 不 清 楚, 因 而 造 成 很 多 程 式 上 的 錯 誤 基 本 上 = 指 令 是 阻 塞 式 的 (Blocking), 因 此 程 式 會 按 照 循 序 的 方 式, 一 個 指 令 接 著 一 個 指 令 執 行, 就 像 C 語 言 裏 的 a=b, b=c 這 樣,b=c 會 在 a=b 執 行 完 之 後 才 執 行, 以 下 是 一 個 範 例 但 是 <= 指 令 卻 是 非 阻 塞 式 的 (Nonblocking), 所 以 程 式 會 採 用 平 行 的 方 式 執 行 舉 例 而 言, 像 是 a<=b, b<=c 會 同 時 執 行 兩 者, 所 以 a 會 取 得 上 一 輪 的 b 值, 而 b 則 會 取 得 上 一 輪 的 c 值 Blocking 的 語 法 (=) 通 常 用 在 組 合 電 路 上, 也 就 是 always @(*) 語 句 裏 面, 而 Nonblocking 的 語 法 (<=) 通 常 用 在 採 用 邊 緣 觸 發 的 循 序 電 路 上, 也 就 是 always @(posedge clock) 的 語 句 裏 面 且 讓 我 們 用 幾 個 範 例 來 說 明 blocking = 與 Nonblocking <= 的 差 別 範 例 1: 阻 塞 式 (Blocking =) 非 阻 塞 式 (Nonblocking <=) always @(a or b or c) begin a=0; b=a; c=b; always @(posedge clock or reset) begi n a<=0; b<=a; c<=b; 結 果 :a=0; b= 上 一 輪 的 a 值 ; c= 上 一 輪 的 b 值 合 成 電 路 :
結 果 : a=b=c=0; 合 成 電 路 : 注 意 : 通 常 blocking assignment = 會 用 在 always @(*) 語 句 裏 面 注 意 : 通 常 nonblocking assignment <= 會 用 在 always @(posedge clock or reset) 語 句 裏 面 結 語 有 些 人 說 在 設 計 Verilog 程 式 的 時 候, 必 須 先 心 中 有 電 路, 才 能 夠 設 計 的 出 來 但 是 從 我 這 樣 一 個 程 式 人 的 角 度 看 來, 並 非 如 此, 採 用 流 程 式 的 寫 法 也 可 以 設 計 得 出 Verilog 程 式, 不 需 一 定 要 有 電 路 圖 當 然 採 用 流 程 式 寫 法 的 話, 如 果 是 用 blocking 的 = 方 式, 那 麼 可 能 會 造 成 很 長 的 鏈 狀 結 構, 這 或 許 會 讓 電 路 效 能 變 差 但 是 由 於 流 程 式 寫 法 簡 單 又 清 楚, 因 此 程 式 碼 往 往 比 區 塊 式 寫 法 短, 而 且 更 容 易 懂, 這 是 流 程 式 寫 法 的 好 處 當 然 如 果 兩 種 寫 法 都 會, 那 是 最 好 的 了, 我 們 將 在 後 續 的 章 節 當 中 陸 續 用 完 整 的 案 例 示 範 如 何 用 這 兩 種 寫 法 分 別 撰 寫 開 放 電 腦 計 畫 中 的 處 理 器, 以 便 讓 讀 者 能 深 入 體 會 兩 種 寫 法 的 好 處 與 缺 點 參 考 文 獻 ( 筆 記 ) dispaly() strobe() monitor() fwrite() 與 blocking / nonblocking 的 關 係
組 合 邏 輯 (Combinatorial Logic) 簡 介 在 數 位 電 路 當 中, 邏 輯 電 路 通 常 被 分 為 兩 類, 一 類 是 沒 有 回 饋 線 路 (No feedback) 的 組 合 邏 輯 電 路 (Combinatorial Logic), 另 一 類 是 有 回 饋 線 路 的 循 序 邏 輯 電 路 (Sequential Logic) 組 合 邏 輯 的 線 路 只 是 將 輸 入 訊 號 轉 換 成 輸 出 訊 號, 像 是 加 法 器 多 工 器 等 都 是 組 合 邏 輯 電 路 的 範 例, 由 於 中 間 不 會 暫 存, 因 此 無 法 記 憶 位 元 而 循 序 邏 輯 由 於 有 回 饋 線 路, 所 以 可 以 製 作 出 像 Flip-Flop,Latch 等 記 憶 單 元, 可 以 記 憶 位 元 在 本 文 中, 我 們 將 先 專 注 在 組 合 邏 輯 上, 看 看 如 何 用 基 本 的 閘 級 寫 法, 寫 出 像 多 工 器 加 法 器 減 法 器 等 組 成 CPU 的 基 礎 電 路 元 件 加 法 器 接 著 讓 我 們 用 先 前 已 經 示 範 過 的 全 加 器 範 例, 一 個 一 個 連 接 成 四 位 元 的 加 法 器, 電 路 圖 如 下 所 示 圖 用 4 個 全 加 器 組 成 4 位 元 加 法 器 上 圖 寫 成 Verilog 就 變 成 以 下 adder4 模 組 的 程 式 內 容 module adder4(input signed [3:0] a, input signed [3:0] b, input c_in, ou tput signed [3:0] sum, output c_out); wire [3:0] c; fulladder fa1(a[0],b[0], c_in, sum[0], c[1]) ; fulladder fa2(a[1],b[1], c[1], sum[1], c[2]) ; fulladder fa3(a[2],b[2], c[2], sum[2], c[3]) ; fulladder fa4(a[3],b[3], c[3], sum[3], c_out) ; module 以 下 是 完 整 的 4 位 元 加 法 器 之 Verilog 程 式
檔 案 :adder4.v module fulladder (input a, b, c_in, output sum, c_out); wire s1, c1, c2; xor g1(s1, a, b); xor g2(sum, s1, c_in); and g3(c1, a,b); and g4(c2, s1, c_in) ; xor g5(c_out, c2, c1) ; module module adder4(input signed [3:0] a, input signed [3:0] b, input c_in, ou tput signed [3:0] sum, output c_out); wire [3:0] c; fulladder fa1(a[0],b[0], c_in, sum[0], c[1]) ; fulladder fa2(a[1],b[1], c[1], sum[1], c[2]) ; fulladder fa3(a[2],b[2], c[2], sum[2], c[3]) ; fulladder fa4(a[3],b[3], c[3], sum[3], c_out) ; module module main; reg signed [3:0] a; reg signed [3:0] b; wire signed [3:0] sum; wire c_out; adder4 DUT (a, b, 1'b0, sum, c_out); initial begin a = 4'b0101; b = 4'b0000;
always #50 begin b=b+1; $monitor("%dns monitor: a=%d b=%d sum=%d", $stime, a, b, sum); initial #2000 $finish; module 執 行 結 果 D:\ccc101\icarus\ccc>iverilog -o sadd4 sadd4.v D:\ccc101\icarus\ccc>vvp sadd4 50ns monitor: a= 5 b= 1 sum= 6 100ns monitor: a= 5 b= 2 sum= 7 150ns monitor: a= 5 b= 3 sum=-8 200ns monitor: a= 5 b= 4 sum=-7 250ns monitor: a= 5 b= 5 sum=-6 300ns monitor: a= 5 b= 6 sum=-5 350ns monitor: a= 5 b= 7 sum=-4 400ns monitor: a= 5 b=-8 sum=-3 450ns monitor: a= 5 b=-7 sum=-2 500ns monitor: a= 5 b=-6 sum=-1 550ns monitor: a= 5 b=-5 sum= 0 600ns monitor: a= 5 b=-4 sum= 1 650ns monitor: a= 5 b=-3 sum= 2 700ns monitor: a= 5 b=-2 sum= 3 750ns monitor: a= 5 b=-1 sum= 4 800ns monitor: a= 5 b= 0 sum= 5 850ns monitor: a= 5 b= 1 sum= 6 900ns monitor: a= 5 b= 2 sum= 7 950ns monitor: a= 5 b= 3 sum=-8 1000ns monitor: a= 5 b= 4 sum=-7 1050ns monitor: a= 5 b= 5 sum=-6 1100ns monitor: a= 5 b= 6 sum=-5 1150ns monitor: a= 5 b= 7 sum=-4 1200ns monitor: a= 5 b=-8 sum=-3
1250ns monitor: a= 5 b=-7 sum=-2 1300ns monitor: a= 5 b=-6 sum=-1 1350ns monitor: a= 5 b=-5 sum= 0 1400ns monitor: a= 5 b=-4 sum= 1 1450ns monitor: a= 5 b=-3 sum= 2 1500ns monitor: a= 5 b=-2 sum= 3 1550ns monitor: a= 5 b=-1 sum= 4 1600ns monitor: a= 5 b= 0 sum= 5 1650ns monitor: a= 5 b= 1 sum= 6 1700ns monitor: a= 5 b= 2 sum= 7 1750ns monitor: a= 5 b= 3 sum=-8 1800ns monitor: a= 5 b= 4 sum=-7 1850ns monitor: a= 5 b= 5 sum=-6 1900ns monitor: a= 5 b= 6 sum=-5 1950ns monitor: a= 5 b= 7 sum=-4 2000ns monitor: a= 5 b=-8 sum=-3 在 上 述 執 行 結 果 中, 您 可 以 看 到 在 沒 有 溢 位 的 情 況 下,sum = a+b, 但 是 一 但 加 總 值 超 過 7 之 後, 那 就 會 變 成 負 值, 這 也 正 是 有 號 二 補 數 表 示 法 溢 位 時 會 產 生 的 結 果 32 位 元 加 法 器 當 然 上 述 的 四 位 元 加 法 器 的 範 圍, 只 能 從 -8 到 +7, 這 個 範 圍 實 在 太 小 了, 並 不 具 備 任 何 實 用 性, 但 是 萬 事 起 頭 難, 只 要 您 能 夠 做 出 四 位 元 加 法 器, 那 麼 就 可 以 利 用 這 個 元 件 進 行 串 接, 將 8 個 四 位 元 加 法 器 串 接 起 來, 立 刻 得 到 了 一 組 32 位 元 的 加 法 器, 以 下 是 這 個 32 位 元 加 法 器 的 模 組 定 義 module adder32(input signed [31:0] a, input signed [31:0] b, input c_in, output signed [31:0] sum, output c_out); wire [7:0] c; adder4 a0(a[3:0],b[3:0], c_in, sum[3:0], c[0]) ; adder4 a1(a[7:4],b[7:4], c[0], sum[7:4], c[1]) ; adder4 a2(a[11:8],b[11:8], c[1], sum[11:8], c[2]) ; adder4 a3(a[15:12],b[15:12], c[2], sum[15:12],c[3]) ; adder4 a4(a[19:16],b[19:16], c[3], sum[19:16],c[4]) ; adder4 a5(a[23:20],b[23:20], c[4], sum[23:20],c[5]) ; adder4 a6(a[27:24],b[27:24], c[5], sum[27:24],c[6]) ; adder4 a7(a[31:28],b[31:28], c[6], sum[31:28],c_out);
module 有 了 這 個 模 組 之 後, 您 就 可 以 寫 出 下 列 完 整 的 程 式, 以 測 試 驗 證 該 32 位 元 加 法 器 是 否 正 確 了 module fulladder (input a, b, c_in, output sum, c_out); wire s1, c1, c2; xor g1(s1, a, b); xor g2(sum, s1, c_in); and g3(c1, a,b); and g4(c2, s1, c_in) ; xor g5(c_out, c2, c1) ; module module adder4(input signed [3:0] a, input signed [3:0] b, input c_in, ou tput signed [3:0] sum, output c_out); wire [3:0] c; fulladder fa1(a[0],b[0], c_in, sum[0], c[1]) ; fulladder fa2(a[1],b[1], c[1], sum[1], c[2]) ; fulladder fa3(a[2],b[2], c[2], sum[2], c[3]) ; fulladder fa4(a[3],b[3], c[3], sum[3], c_out) ; module module adder32(input signed [31:0] a, input signed [31:0] b, input c_in, output signed [31:0] sum, output c_out); wire [7:0] c; adder4 a0(a[3:0],b[3:0], c_in, sum[3:0], c[0]) ; adder4 a1(a[7:4],b[7:4], c[0], sum[7:4], c[1]) ; adder4 a2(a[11:8],b[11:8], c[1], sum[11:8], c[2]) ; adder4 a3(a[15:12],b[15:12], c[2], sum[15:12],c[3]) ; adder4 a4(a[19:16],b[19:16], c[3], sum[19:16],c[4]) ; adder4 a5(a[23:20],b[23:20], c[4], sum[23:20],c[5]) ; adder4 a6(a[27:24],b[27:24], c[5], sum[27:24],c[6]) ; adder4 a7(a[31:28],b[31:28], c[6], sum[31:28],c_out);
module module main; reg signed [31:0] a; reg signed [31:0] b; wire signed [31:0] sum; wire c_out; adder32 DUT (a, b, 0, sum, c_out); initial begin a = 60000000; b = 3789621; $monitor("%dns monitor: a=%d b=%d sum=%d", $stime, a, b, sum); always #50 begin b=b-1000000; initial #500 $finish; module 然 後 我 們 就 可 以 用 icarus 進 行 測 試, 以 下 是 測 試 結 果 : D:\Dropbox\Public\web\oc\code>iverilog -o adder32 adder32.v D:\Dropbox\Public\web\oc\code>vvp adder32 0ns monitor: a= 60000000 b= 3789621 sum= 63789621 50ns monitor: a= 60000000 b= 2789621 sum= 62789621 100ns monitor: a= 60000000 b= 1789621 sum= 61789621 150ns monitor: a= 60000000 b= 789621 sum= 60789621 200ns monitor: a= 60000000 b= -210379 sum= 59789621 250ns monitor: a= 60000000 b= -1210379 sum= 58789621
300ns monitor: a= 60000000 b= -2210379 sum= 57789621 350ns monitor: a= 60000000 b= -3210379 sum= 56789621 400ns monitor: a= 60000000 b= -4210379 sum= 55789621 450ns monitor: a= 60000000 b= -5210379 sum= 54789621 500ns monitor: a= 60000000 b= -6210379 sum= 53789621 您 可 以 看 到 sum 的 確 是 a+b 的 結 果, 因 此 這 個 32 位 元 加 法 器 的 初 步 驗 證 是 正 確 的 前 瞻 進 位 加 法 器 (Carry Lookahead Adder) 圖 4 位 元 前 瞻 進 位 加 法 器 圖 片 來 源 : http://en.wikipedia.org/wiki/file:4-bit_carry_lookahead_adder.svg 檔 案 :cladder4.v module cladder4(output [3:0] S, output Cout,PG,GG, input [3:0] A,B, inpu t Cin); wire [3:0] G,P,C; assign G = A & B; //Generate assign P = A ^ B; //Propagate assign C[0] = Cin; assign C[1] = G[0] (P[0]&C[0]); assign C[2] = G[1] (P[1]&G[0]) (P[1]&P[0]&C[0]); assign C[3] = G[2] (P[2]&G[1]) (P[2]&P[1]&G[0]) (P[2]&P[1]&P[0]& C[0]); assign Cout = G[3] (P[3]&G[2]) (P[3]&P[2]&G[1]) (P[3]&P[2]&P[1]& G[0]) (P[3]&P[2]&P[1]&P[0]&C[0]);
assign S = P ^ C; assign PG = P[3] & P[2] & P[1] & P[0]; assign GG = G[3] (P[3]&G[2]) (P[3]&P[2]&G[1]) (P[3]&P[2]&P[1]&G[ 0]); module module main; reg signed [3:0] a; reg signed [3:0] b; wire signed [3:0] sum; wire c_out; cladder4 DUT (sum, cout, pg, gg, a, b, 0); initial begin a = 5; b = -3; $monitor("%dns monitor: a=%d b=%d sum=%d", $stime, a, b, sum); module 執 行 結 果 D:\Dropbox\Public\web\oc\code>iverilog -o cladder4 cladder4.v D:\Dropbox\Public\web\oc\code>vvp cladder4 0ns monitor: a= 5 b=-3 sum= 2 結 語 在 本 文 中, 我 們 大 致 將 CPU 設 計 當 中 最 重 要 的 組 合 邏 輯 電 路, 也 就 是 多 工 器 加 法 器 減 法 器 的 設 計 原 理 說 明 完 畢 了, 希 望 透 過 Verilog 的 實 作 方 式, 能 讓 讀 者 更 瞭 解 數 位 電 路 的 設 計 原 理, 並 且 為 接 下 來 所 要 介 紹 的 處 理 器 設 計 進 行 鋪 路 的 工 作 參 考 文 獻
LSU EE 3755 -- Spring 2002 -- Computer Organization : Verilog Notes 7 -- Integer Multiply and Divide 陳 鍾 誠 的 網 站 :Verilog 電 路 設 計 -- 多 工 器 陳 鍾 誠 的 網 站 :Verilog 電 路 設 計 -- 4 位 元 加 法 器 陳 鍾 誠 的 網 站 :Verilog 電 路 設 計 -- 加 減 器 Wikipedia:Adder Wikipedia:Adder subtractor Wikipedia:Multiplexer 本 文 由 陳 鍾 誠 取 材 ( 主 要 為 圖 片 ) 並 修 改 自 維 基 百 科
算 術 邏 輯 單 元 ALU 的 設 計 在 上 一 章 中, 我 們 探 討 了 組 合 邏 輯 電 路 的 設 計 方 式, 採 用 閘 級 的 拉 線 方 式 設 計 了 多 工 器 與 加 法 器 等 元 件, 在 本 章 當 中, 我 們 將 從 加 法 器 再 度 往 上, 探 討 如 何 設 計 一 個 ALU 單 元 加 減 器 我 們 只 要 把 加 法 器, 加 上 一 組 控 制 的 互 斥 或 閘, 並 控 制 輸 入 進 位 與 否, 就 可 以 成 為 加 減 器 了, 這 是 因 為 我 們 採 用 了 二 補 數 的 關 係 二 補 數 讓 我 們 可 以 很 容 易 的 延 伸 加 法 器 電 路 就 能 做 出 減 法 器 我 們 可 以 在 運 算 元 B 之 前 加 上 2 選 1 多 工 器 或 XOR 閘 來 控 制 B 是 否 應 該 取 補 數, 並 且 運 用 OP 控 制 線 路 來 進 行 控 制, 以 下 是 採 用 2 選 1 多 工 器 的 電 路 做 法 圖 圖 採 用 2 選 1 多 工 器 控 制 的 加 減 器 電 路 另 一 種 更 簡 單 的 做 法 是 採 用 XOR 閘 去 控 制 B 是 否 要 取 補 數, 如 下 圖 所 示 :
圖 採 用 XOR 控 制 的 加 減 器 電 路 清 楚 了 電 路 圖 的 布 局 之 後, 讓 我 們 來 看 看 如 何 用 Verilog 實 做 加 減 器 吧! 關 鍵 部 分 的 程 式 如 下 所 示, 這 個 模 組 就 對 應 到 上 述 的 採 用 XOR 控 制 的 加 減 器 電 路 之 圖 形 module addsub4(input op, input signed [3:0] a, input signed [3:0] b, output signed [3:0] sum, output c_out); wire [3:0] bop; xor4 x1(b, {op,op,op,op}, bop); adder4 a1(a, bop, op, sum, c_out); module 接 著 讓 我 們 來 看 看 完 整 的 加 減 器 程 式 與 測 試 結 果 檔 案 :addsub4.v module fulladder (input a, b, c_in, output sum, c_out); wire s1, c1, c2; xor g1(s1, a, b); xor g2(sum, s1, c_in); and g3(c1, a,b); and g4(c2, s1, c_in) ; xor g5(c_out, c2, c1) ; module module adder4(input signed [3:0] a, input signed [3:0] b, input c_in, wire [3:0] c; output signed [3:0] sum, output c_out); fulladder fa1(a[0],b[0], c_in, sum[0], c[1]) ; fulladder fa2(a[1],b[1], c[1], sum[1], c[2]) ; fulladder fa3(a[2],b[2], c[2], sum[2], c[3]) ; fulladder fa4(a[3],b[3], c[3], sum[3], c_out) ;
module module xor4(input [3:0] a, input [3:0] b, output [3:0] y); assign y = a ^ b; module module addsub4(input op, input signed [3:0] a, input signed [3:0] b, output signed [3:0] sum, output c_out); wire [3:0] bop; xor4 x1(b, {op,op,op,op}, bop); adder4 a1(a, bop, op, sum, c_out); module module main; reg signed [3:0] a; reg signed [3:0] b; wire signed [3:0] sum; reg op; wire c_out; addsub4 DUT (op, a, b, sum, c_out); initial begin a = 4'b0101; b = 4'b0000; op = 1'b0; always #50 begin op=op+1; $monitor("%dns monitor: op=%d a=%d b=%d sum=%d", $stime, op, a, b, sum) ;
always #100 begin b=b+1; initial #2000 $finish; module 執 行 結 果 : D:\ccc101\icarus\ccc>iverilog -o addsub4 addsub4.v D:\ccc101\icarus\ccc>vvp addsub4 50ns monitor: op=1 a= 5 b= 0 sum= 5 100ns monitor: op=0 a= 5 b= 1 sum= 6 150ns monitor: op=1 a= 5 b= 1 sum= 4 200ns monitor: op=0 a= 5 b= 2 sum= 7 250ns monitor: op=1 a= 5 b= 2 sum= 3 300ns monitor: op=0 a= 5 b= 3 sum=-8 350ns monitor: op=1 a= 5 b= 3 sum= 2 400ns monitor: op=0 a= 5 b= 4 sum=-7 450ns monitor: op=1 a= 5 b= 4 sum= 1 500ns monitor: op=0 a= 5 b= 5 sum=-6 550ns monitor: op=1 a= 5 b= 5 sum= 0 600ns monitor: op=0 a= 5 b= 6 sum=-5 650ns monitor: op=1 a= 5 b= 6 sum=-1 700ns monitor: op=0 a= 5 b= 7 sum=-4 750ns monitor: op=1 a= 5 b= 7 sum=-2 800ns monitor: op=0 a= 5 b=-8 sum=-3 850ns monitor: op=1 a= 5 b=-8 sum=-3 900ns monitor: op=0 a= 5 b=-7 sum=-2 950ns monitor: op=1 a= 5 b=-7 sum=-4 1000ns monitor: op=0 a= 5 b=-6 sum=-1 1050ns monitor: op=1 a= 5 b=-6 sum=-5 1100ns monitor: op=0 a= 5 b=-5 sum= 0 1150ns monitor: op=1 a= 5 b=-5 sum=-6 1200ns monitor: op=0 a= 5 b=-4 sum= 1
1250ns monitor: op=1 a= 5 b=-4 sum=-7 1300ns monitor: op=0 a= 5 b=-3 sum= 2 1350ns monitor: op=1 a= 5 b=-3 sum=-8 1400ns monitor: op=0 a= 5 b=-2 sum= 3 1450ns monitor: op=1 a= 5 b=-2 sum= 7 1500ns monitor: op=0 a= 5 b=-1 sum= 4 1550ns monitor: op=1 a= 5 b=-1 sum= 6 1600ns monitor: op=0 a= 5 b= 0 sum= 5 1650ns monitor: op=1 a= 5 b= 0 sum= 5 1700ns monitor: op=0 a= 5 b= 1 sum= 6 1750ns monitor: op=1 a= 5 b= 1 sum= 4 1800ns monitor: op=0 a= 5 b= 2 sum= 7 1850ns monitor: op=1 a= 5 b= 2 sum= 3 1900ns monitor: op=0 a= 5 b= 3 sum=-8 1950ns monitor: op=1 a= 5 b= 3 sum= 2 2000ns monitor: op=0 a= 5 b= 4 sum=-7 在 上 述 結 果 中, 您 可 以 看 到 當 op=0 時, 電 路 所 作 的 是 加 法 運 算, 例 如 :200ns monitor: op=0 a= 5 b= 2 sum= 7 而 當 op=1 時, 電 路 所 做 的 是 減 法 運 算, 例 如 :250ns monitor: op=1 a= 5 b= 2 sum= 3 採 用 CASE 語 法 設 計 ALU 模 組 其 實 在 Verilog 當 中, 我 們 並 不 需 要 自 行 設 計 加 法 器, 因 為 Verilog 提 供 了 高 階 的 +, -, *, / 等 基 本 運 算, 可 以 讓 我 們 直 接 使 用, 更 方 便 的 是, 只 要 搭 配 case 語 句, 我 們 就 可 以 很 輕 易 的 設 計 出 一 個 ALU 單 元 了 以 下 是 一 個 簡 易 的 ALU 單 元 之 程 式 碼, // 輸 入 a, b 後 會 執 行 op 所 指 定 的 運 算, 然 後 將 結 果 放 在 暫 存 器 y 當 中 module alu(input [7:0] a, input [7:0] b, input [2:0] op, output reg [7:0 ] y); always@(a or b or op) begin // 當 a, b 或 op 有 改 變 時, 就 進 入 此 區 塊 執 行 case(op) // 根 據 op 決 定 要 執 行 何 種 運 算 3'b000: y = a + b; // op=000, 執 行 加 法 3'b001: y = a - b; // op=000, 執 行 減 法 3'b010: y = a * b; // op=000, 執 行 乘 法 3'b011: y = a / b; // op=000, 執 行 除 法 3'b100: y = a & b; // op=000, 執 行 AND 3'b101: y = a b; // op=000, 執 行 OR 3'b110: y = ~a; // op=000, 執 行 NOT
3'b111: y = a ^ b; // op=000, 執 行 XOR case module Verilog 語 法 的 注 意 事 項 上 述 這 種 寫 法 感 覺 就 好 像 在 用 高 階 寫 程 式 一 樣, 這 讓 ALU 的 設 計 變 得 非 常 簡 單 但 是 仍 然 需 要 注 意 以 下 幾 點 與 高 階 語 言 不 同 之 處 : 注 意 事 項 1. always 語 句 的 用 法 case 等 陳 述 句 的 外 面 一 定 要 有 always 或 initial 語 句, 因 為 這 是 硬 體 線 路, 所 以 是 採 用 連 線 wiring 的 方 式,always 語 句 只 有 在 @(trigger) 中 間 的 trigger 觸 發 條 件 符 合 時 才 會 被 觸 發 當 trigger 中 的 變 數 有 任 何 改 變 的 時 候,always 語 句 就 會 被 觸 發, 像 是 always@(a or b or op) 就 代 表 當 (a, b, op) 當 中 任 何 一 個 有 改 變 的 時 候, 該 語 句 就 會 被 觸 發 有 時 我 們 可 以 在 always 語 句 當 中 加 上 posedge 的 條 件, 指 定 只 有 在 正 邊 緣 ( 上 昇 邊 緣 ) 時 觸 發 或 者 加 上 negedge 的 條 件, 指 定 只 有 在 負 邊 緣 ( 下 降 邊 緣 ) 的 時 候 觸 發, 例 如 我 們 可 以 常 常 在 Verilog 當 中 看 到 下 列 語 句 : always @(posedge clock) begin... 上 述 語 句 就 只 有 在 clock 這 一 條 線 路 的 電 波 上 昇 邊 緣 會 被 觸 發, 如 此 我 們 就 能 更 精 細 的 控 制 觸 發 的 動 作, 採 用 正 邊 緣 或 負 邊 緣 觸 發 的 方 式 注 意 事 項 2. 指 定 陳 述 的 左 項 之 限 制 在 上 述 程 式 中,a, b, op 被 宣 告 為 input ( 輸 入 線 路 ), 而 y 則 宣 告 為 output reg ( 輸 出 暫 存 器 ), 在 這 裏 必 須 注 意 的 是 y 不 能 只 宣 告 為 output 而 不 加 上 reg, 因 為 只 有 reg 型 態 的 變 數 才 能 被 放 在 always 區 塊 裡 的 等 號 左 方, 進 行 指 定 的 動 作 事 實 上 在 Verilog 當 中, 像 output reg [7:0] y 這 樣 的 宣 告, 其 實 也 可 以 用 比 較 繁 雜 的 兩 次 宣 告 方 式, 一 次 宣 告 output, 另 一 次 則 宣 告 reg, 如 下 所 示 : module alu(input [7:0] a, input [7:0] b, input [2:0] op, output [7:0] y) ; reg y; always@(a or b or op) begin... 甚 至, 您 也 可 以 將 該 變 數 分 開 為 兩 個 不 同 名 稱, 然 後 再 利 用 assign 的 方 式 指 定, 如 下 所 示 :
// 輸 入 a, b 後 會 執 行 op 所 指 定 的 運 算, 然 後 將 結 果 放 在 暫 存 器 y 當 中 module alu(input [7:0] a, input [7:0] b, input [2:0] op, output [7:0] y) ; reg ty; always@(a or b or op) begin // 當 a, b 或 op 有 改 變 時, 就 進 入 此 區 塊 執 行 case(op) // 根 據 op 決 定 要 執 行 何 種 運 算 3'b000: ty = a + b; // op=000, 執 行 加 法 3'b001: ty = a - b; // op=000, 執 行 減 法 3'b010: ty = a * b; // op=000, 執 行 乘 法 3'b011: ty = a / b; // op=000, 執 行 除 法 3'b100: ty = a & b; // op=000, 執 行 AND 3'b101: ty = a b; // op=000, 執 行 OR 3'b110: ty = ~a; // op=000, 執 行 NOT 3'b111: ty = a ^ b; // op=000, 執 行 XOR case $display("base 10 : %dns : op=%d a=%d b=%d y=%d", $stime, op, a, b, y); // 印 出 op, a, b, y 的 10 進 位 值 $display("base 2 : %dns : op=%b a=%b b=%b y=%b", $stime, op, a, b, y); // 印 出 op, a, b, y 的 2 進 位 值 assign y=ty; module 在 上 述 程 式 中, 由 於 只 有 reg 型 態 的 變 數 可 以 放 在 always 區 塊 內 的 等 號 左 邊, 因 此 我 們 必 須 用 reg 型 態 的 ty 去 儲 存 運 算 結 果 但 是 在 assign 指 令 的 等 號 左 邊, 則 不 需 要 是 暫 存 器 型 態 的 變 數, 也 可 以 是 線 路 型 態 的 變 數, 因 此 我 們 可 以 用 assign y=ty 這 樣 一 個 指 令 去 將 ty 的 暫 存 器 內 容 輸 出 事 實 上,assign 語 句 代 表 的 是 一 種 不 需 儲 存 的 立 即 輸 出 接 線, 因 此 我 們 才 能 將 output 型 態 的 變 數 寫 在 等 號 左 邊 啊! 完 整 的 ALU 設 計 ( 含 測 試 程 式 ) 瞭 解 了 這 些 Verilog 語 法 特 性 之 後, 我 們 就 可 以 搭 配 測 試 程 式, 對 這 個 ALU 模 組 進 行 測 試, 以 下 是 完 整 的 程 式 碼 : 檔 案 :alu.v // 輸 入 a, b 後 會 執 行 op 所 指 定 的 運 算, 然 後 將 結 果 放 在 暫 存 器 y 當 中 module alu(input [7:0] a, input [7:0] b, input [2:0] op, output reg [7:0
] y); always@(a or b or op) begin // 當 a, b 或 op 有 改 變 時, 就 進 入 此 區 塊 執 行 case(op) // 根 據 op 決 定 要 執 行 何 種 運 算 3'b000: y = a + b; // op=000, 執 行 加 法 3'b001: y = a - b; // op=000, 執 行 減 法 3'b010: y = a * b; // op=000, 執 行 乘 法 3'b011: y = a / b; // op=000, 執 行 除 法 3'b100: y = a & b; // op=000, 執 行 AND 3'b101: y = a b; // op=000, 執 行 OR 3'b110: y = ~a; // op=000, 執 行 NOT 3'b111: y = a ^ b; // op=000, 執 行 XOR case $display("base 10 : %dns : op=%d a=%d b=%d y=%d", $stime, op, a, b, y); // 印 出 op, a, b, y 的 10 進 位 值 $display("base 2 : %dns : op=%b a=%b b=%b y=%b", $stime, op, a, b, y); // 印 出 op, a, b, y 的 2 進 位 值 module module main; reg [7:0] a, b; wire [7:0] y; reg [2:0] op; // 測 試 程 式 開 始 // 宣 告 a, b 為 8 位 元 暫 存 器 // 宣 告 y 為 8 位 元 線 路 // 宣 告 op 為 3 位 元 暫 存 器 alu alu1(a, b, op, y); // 建 立 一 個 alu 單 元, 名 稱 為 alu1 initial begin // 測 試 程 式 的 初 始 化 動 作 a = 8'h07; // 設 定 a 為 數 值 7 b = 8'h03; // 設 定 b 為 數 值 3 op = 3'b000; // 設 定 op 的 初 始 值 為 000 always #50 begin // 每 個 50 奈 秒 就 作 下 列 動 作 op = op + 1; // 讓 op 的 值 加 1 initial #1000 $finish; // 時 間 到 1000 奈 秒 就 結 束
module 在 上 述 程 式 中, 為 了 更 清 楚 的 印 出 ALU 的 輸 出 結 果, 我 們 在 ALU 模 組 的 結 尾 放 入 以 下 的 兩 行 $display() 指 令, 以 便 同 時 顯 示 (op, a, b, y) 等 變 數 的 10 進 位 與 2 進 位 結 果 值, 方 便 讀 者 觀 察 $display("base 10 : %dns : op=%d a=%d b=%d y=%d", $stime, op, a, b, y); // 印 出 op, a, b, y 的 10 進 位 值 $display("base 2 : %dns : op=%b a=%b b=%b y=%b", $stime, op, a, b, y); // 印 出 op, a, b, y 的 2 進 位 值 測 試 執 行 結 果 上 述 程 式 的 執 行 測 試 結 果 如 下 : D:\Dropbox\Public\pmag\201310\code>iverilog -o alu alu.v D:\Dropbox\Public\pmag\201310\code>vvp alu base 10 : 0ns : op=0 a= 7 b= 3 y= 10 base 2 : 0ns : op=000 a=00000111 b=00000011 y=00001010 base 10 : 50ns : op=1 a= 7 b= 3 y= 4 base 2 : 50ns : op=001 a=00000111 b=00000011 y=00000100 base 10 : 100ns : op=2 a= 7 b= 3 y= 21 base 2 : 100ns : op=010 a=00000111 b=00000011 y=00010101 base 10 : 150ns : op=3 a= 7 b= 3 y= 2 base 2 : 150ns : op=011 a=00000111 b=00000011 y=00000010 base 10 : 200ns : op=4 a= 7 b= 3 y= 3 base 2 : 200ns : op=100 a=00000111 b=00000011 y=00000011 base 10 : 250ns : op=5 a= 7 b= 3 y= 7 base 2 : 250ns : op=101 a=00000111 b=00000011 y=00000111 base 10 : 300ns : op=6 a= 7 b= 3 y=248 base 2 : 300ns : op=110 a=00000111 b=00000011 y=11111000 base 10 : 350ns : op=7 a= 7 b= 3 y= 4 base 2 : 350ns : op=111 a=00000111 b=00000011 y=00000100 base 10 : 400ns : op=0 a= 7 b= 3 y= 10 base 2 : 400ns : op=000 a=00000111 b=00000011 y=00001010 base 10 : 450ns : op=1 a= 7 b= 3 y= 4 base 2 : 450ns : op=001 a=00000111 b=00000011 y=00000100 base 10 : 500ns : op=2 a= 7 b= 3 y= 21
base 2 : 500ns : op=010 a=00000111 b=00000011 y=00010101 base 10 : 550ns : op=3 a= 7 b= 3 y= 2 base 2 : 550ns : op=011 a=00000111 b=00000011 y=00000010 base 10 : 600ns : op=4 a= 7 b= 3 y= 3 base 2 : 600ns : op=100 a=00000111 b=00000011 y=00000011 base 10 : 650ns : op=5 a= 7 b= 3 y= 7 base 2 : 650ns : op=101 a=00000111 b=00000011 y=00000111 base 10 : 700ns : op=6 a= 7 b= 3 y=248 base 2 : 700ns : op=110 a=00000111 b=00000011 y=11111000 base 10 : 750ns : op=7 a= 7 b= 3 y= 4 base 2 : 750ns : op=111 a=00000111 b=00000011 y=00000100 base 10 : 800ns : op=0 a= 7 b= 3 y= 10 base 2 : 800ns : op=000 a=00000111 b=00000011 y=00001010 base 10 : 850ns : op=1 a= 7 b= 3 y= 4 base 2 : 850ns : op=001 a=00000111 b=00000011 y=00000100 base 10 : 900ns : op=2 a= 7 b= 3 y= 21 base 2 : 900ns : op=010 a=00000111 b=00000011 y=00010101 base 10 : 950ns : op=3 a= 7 b= 3 y= 2 base 2 : 950ns : op=011 a=00000111 b=00000011 y=00000010 base 10 : 1000ns : op=4 a= 7 b= 3 y= 3 執 行 結 果 分 析 您 可 以 看 到 一 開 始 0ns 時,op=0, 所 以 執 行 加 法, 得 到 y=a+b=7+3=10, 然 後 50ns 時 op=1, 所 以 執 行 減 法, 以 下 是 整 個 執 行 結 果 的 簡 化 列 表 : a b op y 7 3 0 (+) 10 7 3 1 (-) 4 7 3 2 (*) 21 7 3 3 (/) 2 00000111 00000011 4 (AND) 00000011 00000111 00000011 5 (OR) 00000111 00000111 00000011 6 (NOT) 11111000 00000111 00000011 6 (XOR) 00000100
透 過 上 述 的 測 試, 我 們 知 道 整 個 ALU 的 設 計 方 式 是 正 確 的 結 語 對 於 沒 有 學 過 硬 體 描 述 語 言 的 人 來 說, 通 常 會 認 為 要 設 計 一 個 ALU 單 元, 應 該 是 很 複 雜 的 但 是 從 上 述 的 程 式 當 中, 您 可 以 看 到 在 Verilog 當 中 設 計 ALU 其 實 是 很 簡 單 的, 只 要 用 10 行 左 右 的 程 式 碼, 甚 至 不 需 要 自 己 設 計 加 法 器 就 能 完 成 這 是 因 為 Verilog 將 +, -, *, / 等 運 算 內 建 在 語 言 當 中 了, 所 以 讓 整 個 程 式 的 撰 寫 只 要 透 過 一 個 case 語 句 就 能 做 完 了, 這 種 設 計 方 式 非 常 的 像 高 階 語 言, 讓 硬 體 的 設 計 變 得 更 加 的 容 易 了 事 實 上, 在 使 用 Verilog 設 計 像 CPU 這 樣 的 複 雜 元 件 時,ALU 或 暫 存 器 等 單 元 都 變 得 非 常 的 容 易 真 正 複 雜 的 其 實 是 控 制 單 元, 而 這 也 是 CPU 設 計 的 精 髓 之 所 在, 我 們 會 在 開 放 電 腦 計 劃 系 列 的 文 章 中, 完 成 CPU 與 控 制 單 元 的 設 計 參 考 文 獻 陳 鍾 誠 的 網 站 : 用 Verilog 設 計 ALU
時 序 邏 輯 (Sequential Logic) 記 憶 單 元 (Memory Unit) 組 合 邏 輯 (Combinatorial Logic) 是 一 種 沒 有 回 饋 線 路 的 數 位 電 路 系 統, 而 循 序 邏 輯 ( 時 序 邏 輯, Sequential Logic) 則 是 一 種 包 含 回 饋 線 路 的 系 統 舉 例 而 言, 在 下 圖 (a) 的 全 加 器 的 電 路 裏, 您 可 以 看 到 從 輸 入 線 路 一 路 輸 入 接 向 輸 出, 這 種 稱 為 組 合 邏 輯 電 路 而 在 下 圖 (b) 的 栓 鎖 器 ( 正 反 器 ) 線 路 裏, 線 路 從 輸 出 Q 又 拉 回 S 做 為 輸 入, 這 種 有 倒 勾 的 線 路 就 稱 為 循 序 邏 輯 電 路 暫 存 器 靜 態 記 憶 體 等 記 憶 單 元, 都 是 由 這 種 有 反 饋 電 路 的 時 序 邏 輯 所 構 成 的, 因 此 要 瞭 解 記 憶 單 元 之 前, 先 瞭 解 時 序 邏 輯 的 電 路 結 構 會 是 有 幫 助 的 正 反 器 ( 閂 鎖 器 ) 正 反 器 有 很 多 種 型 式, 以 下 是 來 自 維 基 百 科 的 一 些 正 反 器 說 明 與 範 例 參 考 :http://en.wikipedia.org/wiki/flip-flop_(electronics) 摘 自 維 基 百 科 : 正 反 器 ( 英 語 :Flip-flop, FF, 中 國 大 陸 譯 作 觸 發 器, 港 澳 譯 作 ), 學 名 雙 穩 態 多 諧 振 盪 器 (Bistable Multivibrator), 是 一 種 應 用 在 數 位 電 路 上 具 有 記 憶 功 能 的 循 序 邏 輯 元 件, 可 記 錄 二 進 位 制 數 位 訊 號 1 和 0 正 反 器 是 構 成 序 向 邏 輯 電 路 以 及 各 種 複 雜 數 位 系 統 的 基 本 邏 輯 單 元
圖 各 種 正 反 器 SR 正 反 器 正 反 器 是 可 以 用 來 儲 存 位 元, 是 循 序 電 路 的 基 礎, 以 下 是 一 個 用 NAND 閘 構 成 的 正 反 器 圖 NAND 閘 構 成 的 正 反 器 我 們 可 以 根 據 上 圖 實 作 出 對 應 的 Verilog 程 式 如 下 : 檔 案 :latch.v module latch(input Sbar, Rbar, output Q, Qbar); nand LS(Q, Sbar, Qbar); nand LR(Qbar, Rbar, Q); module module main; reg Sbar, Rbar; wire Q, Qbar;
latch latch1(sbar, Rbar, Q, Qbar); initial begin $monitor("%4dns monitor: Sbar=%d Rbar=%d Q=%d Qbar=%d", $stime, Sbar, R bar, Q, Qbar); $dumpfile("latch.vcd"); // 輸 出 給 GTK wave 顯 示 波 型 $dumpvars; always #50 begin Sbar = 0; Rbar = 1; #50; Sbar = 1; Rbar = 1; #50; Sbar = 1; Rbar = 0; #50; initial #500 $finish; module 執 行 結 果 : D:\verilog>iverilog -o latch latch.v D:\verilog>vvp latch VCD info: dumpfile latch.vcd opened for output. 0ns monitor: Sbar=x Rbar=x Q=x Qbar=x 50ns monitor: Sbar=0 Rbar=1 Q=1 Qbar=0 100ns monitor: Sbar=1 Rbar=1 Q=1 Qbar=0 150ns monitor: Sbar=1 Rbar=0 Q=0 Qbar=1 250ns monitor: Sbar=0 Rbar=1 Q=1 Qbar=0 300ns monitor: Sbar=1 Rbar=1 Q=1 Qbar=0 350ns monitor: Sbar=1 Rbar=0 Q=0 Qbar=1 450ns monitor: Sbar=0 Rbar=1 Q=1 Qbar=0 500ns monitor: Sbar=1 Rbar=1 Q=1 Qbar=0
圖 latch.vcd 的 顯 示 圖 形 有 enable 的 正 反 器 如 果 我 們 在 上 述 正 反 器 前 面 再 加 上 兩 個 NAND 閘 進 行 控 制, 就 可 以 形 成 一 組 有 enable 的 正 反 器, 以 下 是 該 正 反 器 的 圖 形 圖 有 enable 的 正 反 器 根 據 上 述 圖 形 我 們 可 以 設 計 出 以 下 的 Verilog 程 式 檔 案 :enlatch.v module latch(input Sbar, Rbar, output Q, Qbar); nand LS(Q, Sbar, Qbar); nand LR(Qbar, Rbar, Q); module module enlatch(input en, S, R, output Q, Qbar); nand ES(Senbar, en, S); nand ER(Renbar, en, R);
latch L1(Senbar, Renbar, Q, Qbar); module module main; reg S, en, R; wire Q, Qbar; enlatch enlatch1(en, S, R, Q, Qbar); initial begin $monitor("%4dns monitor: en=%d S=%d R=%d Q=%d Qbar=%d", $stime, en, S, R, Q, Qbar); $dumpfile("enlatch.vcd"); // 輸 出 給 GTK wave 顯 示 波 型 $dumpvars; always #50 begin en = 1; #50; S = 1; R = 0; #50; S = 0; R = 0; #50; S = 0; R = 1; #50 en = 0; #50; S = 1; R = 0; #50; S = 0; R = 0; #50; S = 0; R = 1; initial #1000 $finish;
module 執 行 結 果 D:\verilog>iverilog -o enlatch enlatch.v D:\verilog>vvp enlatch VCD info: dumpfile enlatch.vcd opened for output. 0ns monitor: en=x Sbar=x Rbar=x Q=x Qbar=x 50ns monitor: en=1 Sbar=x Rbar=x Q=x Qbar=x 100ns monitor: en=1 Sbar=1 Rbar=0 Q=1 Qbar=0 150ns monitor: en=1 Sbar=0 Rbar=0 Q=1 Qbar=0 200ns monitor: en=1 Sbar=0 Rbar=1 Q=0 Qbar=1 250ns monitor: en=0 Sbar=0 Rbar=1 Q=0 Qbar=1 300ns monitor: en=0 Sbar=1 Rbar=0 Q=0 Qbar=1 350ns monitor: en=0 Sbar=0 Rbar=0 Q=0 Qbar=1 400ns monitor: en=0 Sbar=0 Rbar=1 Q=0 Qbar=1 450ns monitor: en=1 Sbar=0 Rbar=1 Q=0 Qbar=1 500ns monitor: en=1 Sbar=1 Rbar=0 Q=1 Qbar=0 550ns monitor: en=1 Sbar=0 Rbar=0 Q=1 Qbar=0 600ns monitor: en=1 Sbar=0 Rbar=1 Q=0 Qbar=1 650ns monitor: en=0 Sbar=0 Rbar=1 Q=0 Qbar=1 700ns monitor: en=0 Sbar=1 Rbar=0 Q=0 Qbar=1 750ns monitor: en=0 Sbar=0 Rbar=0 Q=0 Qbar=1 800ns monitor: en=0 Sbar=0 Rbar=1 Q=0 Qbar=1 850ns monitor: en=1 Sbar=0 Rbar=1 Q=0 Qbar=1 900ns monitor: en=1 Sbar=1 Rbar=0 Q=1 Qbar=0 950ns monitor: en=1 Sbar=0 Rbar=0 Q=1 Qbar=0 1000ns monitor: en=1 Sbar=0 Rbar=1 Q=0 Qbar=1
圖 enlatch.vcd 的 顯 示 圖 形 閘 級 延 遲 (Gate Delay) 在 Verilog 模 型 下, 邏 輯 閘 預 設 是 沒 有 任 何 延 遲 的, 因 此 呈 現 出 來 永 遠 是 即 時 的 結 果, 但 現 實 世 界 的 電 路 總 是 有 少 許 延 遲 的, 每 經 過 一 個 閘 就 會 延 遲 一 點 點 的 時 間, 經 過 的 閘 數 越 多, 延 遲 也 就 會 越 久 參 考 : http://www.asic-world.com/verilog/gate3.html 為 了 模 擬 這 種 延 遲,Verilog 允 許 你 在 閘 上 面 附 加 延 遲 時 間 的 語 法, 您 可 以 分 別 指 定 最 小 延 遲 min, 典 型 延 遲 typical 與 最 大 延 遲 max 舉 例 而 言, 以 下 的 語 法 宣 告 了 一 個 not 閘, 其 中 的 #(1:3:5) 語 法 指 定 了 最 小 延 遲 min=1, 典 型 延 遲 typical=3, 最 大 延 遲 max=5 not #(1:3:5) n2(nclk2, clk); 假 如 您 不 想 分 別 指 定 這 三 種 延 遲, 也 可 以 只 指 定 一 個 延 遲 參 數, 這 樣 min, typical, max 三 者 都 會 設 定 為 該 數 值, 舉 例 而 言, 以 下 是 一 個 宣 告 延 遲 固 定 為 2 的 not 閘 not #2 n1(nclk1, clk); 為 了 說 明 這 種 延 遲 狀 況, 我 們 寫 了 一 個 範 例 程 式 delay.v 來 示 範 設 定 了 閘 級 延 遲 的 效 果, 請 參 考 下 列 程 式 : module main; reg clk; not #2 n1(nclk1, clk); not #(1:3:5) n2(nclk2, clk); initial begin
clk = 0; $monitor("%dns monitor: clk=%b nclk1=%d nclk2=%d", $stime, clk, nclk1, nclk2); $dumpfile("delay.vcd"); // 輸 出 給 GTK wave 顯 示 波 型 $dumpvars; always #10 begin clk = clk + 1; initial #100 $finish; module 執 行 結 果 : D:\Dropbox\Public\web\oc\code>iverilog -o delay delay.v delay.v:5: warning: choosing typ expression. D:\Dropbox\Public\web\oc\code>vvp delay VCD info: dumpfile delay.vcd opened for output. 0ns monitor: clk=0 nclk1=z nclk2=z 2ns monitor: clk=0 nclk1=1 nclk2=z 3ns monitor: clk=0 nclk1=1 nclk2=1 10ns monitor: clk=1 nclk1=1 nclk2=1 12ns monitor: clk=1 nclk1=0 nclk2=1 13ns monitor: clk=1 nclk1=0 nclk2=0 20ns monitor: clk=0 nclk1=0 nclk2=0 22ns monitor: clk=0 nclk1=1 nclk2=0 23ns monitor: clk=0 nclk1=1 nclk2=1 30ns monitor: clk=1 nclk1=1 nclk2=1 32ns monitor: clk=1 nclk1=0 nclk2=1 33ns monitor: clk=1 nclk1=0 nclk2=0 40ns monitor: clk=0 nclk1=0 nclk2=0 42ns monitor: clk=0 nclk1=1 nclk2=0 43ns monitor: clk=0 nclk1=1 nclk2=1 50ns monitor: clk=1 nclk1=1 nclk2=1
52ns monitor: clk=1 nclk1=0 nclk2=1 53ns monitor: clk=1 nclk1=0 nclk2=0 60ns monitor: clk=0 nclk1=0 nclk2=0 62ns monitor: clk=0 nclk1=1 nclk2=0 63ns monitor: clk=0 nclk1=1 nclk2=1 70ns monitor: clk=1 nclk1=1 nclk2=1 72ns monitor: clk=1 nclk1=0 nclk2=1 73ns monitor: clk=1 nclk1=0 nclk2=0 80ns monitor: clk=0 nclk1=0 nclk2=0 82ns monitor: clk=0 nclk1=1 nclk2=0 83ns monitor: clk=0 nclk1=1 nclk2=1 90ns monitor: clk=1 nclk1=1 nclk2=1 92ns monitor: clk=1 nclk1=0 nclk2=1 93ns monitor: clk=1 nclk1=0 nclk2=0 100ns monitor: clk=0 nclk1=0 nclk2=0 以 上 輸 出 的 波 形 如 下, 您 可 以 看 到 nclk1 的 延 遲 固 定 為 2, 而 nclk2 的 延 遲 則 介 於 1 到 5 之 間 圖 delay.vcd 的 顯 示 波 型 利 用 閘 級 延 遲 製 作 脈 波 變 化 偵 測 器 (Pulse Transition Detector, PTD) 雖 然 延 遲 現 象 看 起 來 像 是 個 缺 陷, 但 事 實 上 如 果 好 好 的 利 用 這 種 現 象, 有 時 反 而 可 以 達 到 很 好 的 效 果, 脈 波 變 化 偵 測 器 電 路 就 是 利 用 這 種 現 象 所 設 計 的 一 種 電 路, 可 以 用 來 偵 測 脈 波 的 上 升 邊 緣 或 下 降 邊 緣 以 下 是 脈 波 變 化 偵 測 電 路 的 圖 形, 其 中 的 關 鍵 是 在 左 邊 的 not 閘 身 上, 由 於 每 個 閘 都 會 造 成 延 遲, 因 此 多 了 not 閘 的 那 條 路 徑 所 造 成 的 延 遲 較 多, 這 讓 輸 出 部 份 會 因 為 延 遲 而 形 成 一 個 脈 衝 波 形
圖 脈 波 變 化 偵 測 器 以 下 是 這 個 電 路 以 Verilog 實 作 的 結 果 檔 案 :ptd.v module ptd(input clk, output ppulse); not #2 P1(nclkd, clk); nand #2 P2(npulse, nclkd, clk); not #2 P3(ppulse, npulse); module module main; reg clk; wire p; ptd ptd1(clk, p); initial begin clk = 0; $monitor("%dns monitor: clk=%b p=%d", $stime, clk, p); $dumpfile("ptd.vcd"); // 輸 出 給 GTK wave 顯 示 波 型 $dumpvars; always #50 begin clk = clk + 1; initial #500 $finish;
module 執 行 結 果 D:\Dropbox\Public\pmag\201311\code>iverilog -o ptd ptd.v D:\Dropbox\Public\pmag\201311\code>vvp ptd VCD info: dumpfile ptd.vcd opened for output. 0ns monitor: clk=0 p=z 4ns monitor: clk=0 p=0 50ns monitor: clk=1 p=0 54ns monitor: clk=1 p=1 56ns monitor: clk=1 p=0 100ns monitor: clk=0 p=0 150ns monitor: clk=1 p=0 154ns monitor: clk=1 p=1 156ns monitor: clk=1 p=0 200ns monitor: clk=0 p=0 250ns monitor: clk=1 p=0 254ns monitor: clk=1 p=1 256ns monitor: clk=1 p=0 300ns monitor: clk=0 p=0 350ns monitor: clk=1 p=0 354ns monitor: clk=1 p=1 356ns monitor: clk=1 p=0 400ns monitor: clk=0 p=0 450ns monitor: clk=1 p=0 454ns monitor: clk=1 p=1 456ns monitor: clk=1 p=0 500ns monitor: clk=0 p=0
圖 ptd.vcd 的 顯 示 圖 形 使 用 脈 衝 偵 測 電 路 製 作 邊 緣 觸 發 正 反 器 有 了 正 反 器 與 脈 波 變 化 偵 測 電 路 之 後, 我 們 就 可 以 組 合 出 邊 緣 觸 發 正 反 器 了, 以 下 是 其 電 路 圖 圖 邊 緣 觸 發 的 正 反 器 事 實 上, 上 述 電 路 圖 只 是 將 有 enable 的 正 反 器 前 面 加 上 一 個 脈 波 變 化 偵 測 電 路 而 已, 其 實 做 的 Verilog 程 式 如 下 檔 案 :ptdlatch.v module latch(input Sbar, Rbar, output Q, Qbar); nand LS(Q, Sbar, Qbar); nand LR(Qbar, Rbar, Q); module module enlatch(input en, S, R, output Q, Qbar);
nand ES(Senbar, en, S); nand ER(Renbar, en, R); latch L1(Senbar, Renbar, Q, Qbar); module module ptd(input clk, output ppulse); not #2 P1(nclkd, clk); nand #2 P2(npulse, nclkd, clk); not #2 P3(ppulse, npulse); module module ptdlatch(input clk, S, R, output Q, Qbar); ptd PTD(clk, ppulse); enlatch EL(ppulse, S, R, Q, Qbar); module module main; reg S, clk, R; wire Q, Qbar; ptdlatch ptdlatch1(clk, S, R, Q, Qbar); initial begin clk = 0; $monitor("%4dns monitor: clk=%d ppulse=%d S=%d R=%d Q=%d Qbar=%d", $sti me, clk, ptdlatch1.ppulse, S, R, Q, Qbar); $dumpfile("ptdlatch.vcd"); // 輸 出 給 GTK wave 顯 示 波 型 $dumpvars; always #20 begin clk = ~clk; always #50 begin S = 1; R = 0;
#50; S = 0; R = 0; #50; S = 0; R = 1; #50; initial #500 $finish; module 執 行 結 果 D:\verilog>iverilog -o ptdlatch ptdlatch.v D:\verilog>vvp ptdlatch VCD info: dumpfile ptdlatch.vcd opened for output. 0ns monitor: clk=0 ppulse=z S=x R=x Q=x Qbar=x 4ns monitor: clk=0 ppulse=0 S=x R=x Q=x Qbar=x 20ns monitor: clk=1 ppulse=0 S=x R=x Q=x Qbar=x 24ns monitor: clk=1 ppulse=1 S=x R=x Q=x Qbar=x 26ns monitor: clk=1 ppulse=0 S=x R=x Q=x Qbar=x 40ns monitor: clk=0 ppulse=0 S=x R=x Q=x Qbar=x 50ns monitor: clk=0 ppulse=0 S=1 R=0 Q=x Qbar=x 60ns monitor: clk=1 ppulse=0 S=1 R=0 Q=x Qbar=x 64ns monitor: clk=1 ppulse=1 S=1 R=0 Q=1 Qbar=0 66ns monitor: clk=1 ppulse=0 S=1 R=0 Q=1 Qbar=0 80ns monitor: clk=0 ppulse=0 S=1 R=0 Q=1 Qbar=0 100ns monitor: clk=1 ppulse=0 S=0 R=0 Q=1 Qbar=0 104ns monitor: clk=1 ppulse=1 S=0 R=0 Q=1 Qbar=0 106ns monitor: clk=1 ppulse=0 S=0 R=0 Q=1 Qbar=0 120ns monitor: clk=0 ppulse=0 S=0 R=0 Q=1 Qbar=0 140ns monitor: clk=1 ppulse=0 S=0 R=0 Q=1 Qbar=0 144ns monitor: clk=1 ppulse=1 S=0 R=0 Q=1 Qbar=0 146ns monitor: clk=1 ppulse=0 S=0 R=0 Q=1 Qbar=0 150ns monitor: clk=1 ppulse=0 S=0 R=1 Q=1 Qbar=0 160ns monitor: clk=0 ppulse=0 S=0 R=1 Q=1 Qbar=0 180ns monitor: clk=1 ppulse=0 S=0 R=1 Q=1 Qbar=0
184ns monitor: clk=1 ppulse=1 S=0 R=1 Q=0 Qbar=1 186ns monitor: clk=1 ppulse=0 S=0 R=1 Q=0 Qbar=1 200ns monitor: clk=0 ppulse=0 S=0 R=1 Q=0 Qbar=1 220ns monitor: clk=1 ppulse=0 S=0 R=1 Q=0 Qbar=1 224ns monitor: clk=1 ppulse=1 S=0 R=1 Q=0 Qbar=1 226ns monitor: clk=1 ppulse=0 S=0 R=1 Q=0 Qbar=1 240ns monitor: clk=0 ppulse=0 S=0 R=1 Q=0 Qbar=1 250ns monitor: clk=0 ppulse=0 S=1 R=0 Q=0 Qbar=1 260ns monitor: clk=1 ppulse=0 S=1 R=0 Q=0 Qbar=1 264ns monitor: clk=1 ppulse=1 S=1 R=0 Q=1 Qbar=0 266ns monitor: clk=1 ppulse=0 S=1 R=0 Q=1 Qbar=0 280ns monitor: clk=0 ppulse=0 S=1 R=0 Q=1 Qbar=0 300ns monitor: clk=1 ppulse=0 S=0 R=0 Q=1 Qbar=0 304ns monitor: clk=1 ppulse=1 S=0 R=0 Q=1 Qbar=0 306ns monitor: clk=1 ppulse=0 S=0 R=0 Q=1 Qbar=0 320ns monitor: clk=0 ppulse=0 S=0 R=0 Q=1 Qbar=0 340ns monitor: clk=1 ppulse=0 S=0 R=0 Q=1 Qbar=0 344ns monitor: clk=1 ppulse=1 S=0 R=0 Q=1 Qbar=0 346ns monitor: clk=1 ppulse=0 S=0 R=0 Q=1 Qbar=0 350ns monitor: clk=1 ppulse=0 S=0 R=1 Q=1 Qbar=0 360ns monitor: clk=0 ppulse=0 S=0 R=1 Q=1 Qbar=0 380ns monitor: clk=1 ppulse=0 S=0 R=1 Q=1 Qbar=0 384ns monitor: clk=1 ppulse=1 S=0 R=1 Q=0 Qbar=1 386ns monitor: clk=1 ppulse=0 S=0 R=1 Q=0 Qbar=1 400ns monitor: clk=0 ppulse=0 S=0 R=1 Q=0 Qbar=1 420ns monitor: clk=1 ppulse=0 S=0 R=1 Q=0 Qbar=1 424ns monitor: clk=1 ppulse=1 S=0 R=1 Q=0 Qbar=1 426ns monitor: clk=1 ppulse=0 S=0 R=1 Q=0 Qbar=1 440ns monitor: clk=0 ppulse=0 S=0 R=1 Q=0 Qbar=1 450ns monitor: clk=0 ppulse=0 S=1 R=0 Q=0 Qbar=1 460ns monitor: clk=1 ppulse=0 S=1 R=0 Q=0 Qbar=1 464ns monitor: clk=1 ppulse=1 S=1 R=0 Q=1 Qbar=0 466ns monitor: clk=1 ppulse=0 S=1 R=0 Q=1 Qbar=0 480ns monitor: clk=0 ppulse=0 S=1 R=0 Q=1 Qbar=0 500ns monitor: clk=1 ppulse=0 S=0 R=0 Q=1 Qbar=0
圖 ptdlatch.vcd 的 顯 示 圖 形 使 用 脈 衝 偵 測 電 路 設 計 邊 緣 觸 發 暫 存 器 有 了 脈 波 變 化 偵 測 電 路, 只 要 與 任 何 需 要 偵 測 脈 波 變 化 的 元 件 串 接 起 來, 就 可 以 達 到 邊 緣 觸 發 的 功 能 其 實 像 是 Verilog 當 中 的 以 下 程 式, 其 實 都 是 利 用 類 似 的 脈 波 變 化 偵 測 電 路 所 完 成 的 always @(posedge clock) begin... 如 果 我 們 真 的 不 想 使 用 posedge clock 這 種 語 法, 我 們 也 可 以 用 前 述 的 脈 波 變 化 偵 測 電 路 (PTD) 來 製 作 這 類 的 邊 緣 觸 發 功 能, 以 下 是 我 們 用 這 種 方 式 設 計 的 一 個 邊 緣 觸 發 暫 存 器 檔 案 : ptdregister.v module ptd(input clk, output ppulse); not #2 g1(nclkd, clk); nand #2 g2(npulse, nclkd, clk); not #2 g3(ppulse, npulse); module module register(input en, input [31:0] d, output reg [31:0] r); always @(en) begin if (en)
r <= d; module module main; reg [31:0] d; wire [31:0] r; reg clk; wire en; ptd ptd1(clk, en); register register1(en, d, r); initial begin clk = 0; d = 3; $monitor("%4dns monitor: clk=%d en=%d d=%d r=%d", $stime, clk, en, d, r ); always #10 begin clk = clk + 1; always #20 begin d = d + 1; initial #100 $finish; module 執 行 結 果 D:\Dropbox\Public\web\oc\code>iverilog -o ptdregister ptdregister.v D:\Dropbox\Public\web\oc\code>vvp ptdregister 0ns monitor: clk=0 en=z d= 3 r= x
4ns monitor: clk=0 en=0 d= 3 r= x 10ns monitor: clk=1 en=0 d= 3 r= x 14ns monitor: clk=1 en=1 d= 3 r= 3 16ns monitor: clk=1 en=0 d= 3 r= 3 20ns monitor: clk=0 en=0 d= 4 r= 3 30ns monitor: clk=1 en=0 d= 4 r= 3 34ns monitor: clk=1 en=1 d= 4 r= 4 36ns monitor: clk=1 en=0 d= 4 r= 4 40ns monitor: clk=0 en=0 d= 5 r= 4 50ns monitor: clk=1 en=0 d= 5 r= 4 54ns monitor: clk=1 en=1 d= 5 r= 5 56ns monitor: clk=1 en=0 d= 5 r= 5 60ns monitor: clk=0 en=0 d= 6 r= 5 70ns monitor: clk=1 en=0 d= 6 r= 5 74ns monitor: clk=1 en=1 d= 6 r= 6 76ns monitor: clk=1 en=0 d= 6 r= 6 80ns monitor: clk=0 en=0 d= 7 r= 6 90ns monitor: clk=1 en=0 d= 7 r= 6 94ns monitor: clk=1 en=1 d= 7 r= 7 96ns monitor: clk=1 en=0 d= 7 r= 7 100ns monitor: clk=0 en=0 d= 8 r= 7 其 輸 出 的 波 型 檔 如 下 圖 所 示 : 圖 在 GTKWave 中 顯 示 的 ptdregister.vcd 波 型 檔 使 用 脈 衝 偵 測 電 路 製 作 計 數 電 路
如 果 我 們 將 暫 存 器 的 輸 出 在 接 到 一 個 加 法 電 路 上, 進 行 回 饋 性 的 累 加 的 動 作, 如 下 圖 所 示, 那 麼 整 個 電 路 就 會 變 成 一 個 邊 緣 觸 發 的 計 數 電 路 圖 邊 緣 觸 發 的 計 數 電 路 以 上 這 種 電 路 可 以 做 為 採 用 區 塊 方 法 設 計 CPU 的 基 礎, 因 為 CPU 當 中 的 程 式 計 數 器 (Program Counter) 通 常 會 採 用 這 種 邊 緣 觸 發 的 設 計 方 式 以 下 是 上 述 電 路 的 設 計 與 實 作 測 試 結 果 檔 案 : ptdcounter.v module register(input en, input [31:0] d, output reg [31:0] r); always @(en) begin if (en) r <= d; module module ptd(input clk, output ppulse); not #2 P1(nclkd, clk); nand #2 P2(npulse, nclkd, clk); not #2 P3(ppulse, npulse); module module inc(input [31:0] i, output [31:0] o); assign o = i + 4; module module main; wire [31:0] r, ro; reg clk; wire en;
ptd ptd1(clk, en); register r1(en, ro, r); inc i1(r, ro); initial begin clk = 0; r1.r = 0; $monitor("%4dns monitor: clk=%d en=%d r=%d", $stime, clk, en, r); $dumpfile("ptdcounter.vcd"); // 輸 出 給 GTK wave 顯 示 波 型 $dumpvars; always #10 begin clk = clk + 1; initial #100 $finish; module 執 行 結 果 D:\Dropbox\Public\web\oc\code>iverilog -o ptdcounter ptdcounter.v D:\Dropbox\Public\web\oc\code>vvp ptdcounter VCD info: dumpfile ptdcounter.vcd opened for output. 0ns monitor: clk=0 en=z r= 0 4ns monitor: clk=0 en=0 r= 0 10ns monitor: clk=1 en=0 r= 0 14ns monitor: clk=1 en=1 r= 4 16ns monitor: clk=1 en=0 r= 4 20ns monitor: clk=0 en=0 r= 4 30ns monitor: clk=1 en=0 r= 4 34ns monitor: clk=1 en=1 r= 8 36ns monitor: clk=1 en=0 r= 8 40ns monitor: clk=0 en=0 r= 8 50ns monitor: clk=1 en=0 r= 8
54ns monitor: clk=1 en=1 r= 12 56ns monitor: clk=1 en=0 r= 12 60ns monitor: clk=0 en=0 r= 12 70ns monitor: clk=1 en=0 r= 12 74ns monitor: clk=1 en=1 r= 16 76ns monitor: clk=1 en=0 r= 16 80ns monitor: clk=0 en=0 r= 16 90ns monitor: clk=1 en=0 r= 16 94ns monitor: clk=1 en=1 r= 20 96ns monitor: clk=1 en=0 r= 20 100ns monitor: clk=0 en=0 r= 20 其 輸 出 的 波 型 檔 如 下 圖 所 示 : 圖 在 GTKWave 中 顯 示 的 ptdcounter.vcd 波 型 檔 暫 存 器 單 元 檔 案 :regbank.v module regbank(input [3:0] ra1, output [31:0] rd1, input [3:0] ra2, output [31:0] rd2, input clk, input w_en, input [3:0] wa, input [31:0] wd); reg [31:0] r[15:0]; // 宣 告 16 個 32 位 元 的 暫 存 器 assign rd1 = r[ra1]; // 讀 取 索 引 值 為 ra1 的 暫 存 器 assign rd2 = r[ra2]; // 讀 取 索 引 值 為 ra2 的 暫 存 器 always @(posedge clk)
begin if (w_en) // w_en=1 時 寫 入 到 暫 存 器 r[wa] <= wd; // 將 wd 寫 入 到 索 引 值 為 wa 的 暫 存 器 module module main; reg [3:0] ra1, ra2, wa; reg clk, w_en; wire [31:0] rd1, rd2; reg [31:0] wd; regbank rb0(ra1, rd1, ra2, rd2, clk, w_en, wa, wd); initial begin wa = 0; ra1 = 0; ra2 = 0; wd = 0; clk = 0; w_en = 1; initial #200 ra1 = 0; always #50 begin clk = clk + 1; $monitor("%4dns monitor: ra1=%d rd1=%d ra2=%d rd2=%d wa=%d wd=%d", $stime, ra1, rd1, ra2, rd2, wa, wd); always #100 begin wa = wa + 1; wd = wd + 2; ra1 = ra1 + 1; ra2 = ra2-1;
initial #1000 $finish; module 執 行 結 果 : D:\Dropbox\Public\web\oc\code\verilog>iverilog -o regbank regbank.v D:\Dropbox\Public\web\oc\code\verilog>vvp regbank 50ns monitor: ra1= 0 rd1= 0 ra2= 0 rd2= 0 wa= 0 wd= 0 100ns monitor: ra1= 1 rd1= x ra2=15 rd2= x wa= 1 wd= 2 150ns monitor: ra1= 1 rd1= 2 ra2=15 rd2= x wa= 1 wd= 2 200ns monitor: ra1= 1 rd1= 2 ra2=14 rd2= x wa= 2 wd= 4 250ns monitor: ra1= 1 rd1= 2 ra2=14 rd2= x wa= 2 wd= 4 300ns monitor: ra1= 2 rd1= 4 ra2=13 rd2= x wa= 3 wd= 6 350ns monitor: ra1= 2 rd1= 4 ra2=13 rd2= x wa= 3 wd= 6 400ns monitor: ra1= 3 rd1= 6 ra2=12 rd2= x wa= 4 wd= 8 450ns monitor: ra1= 3 rd1= 6 ra2=12 rd2= x wa= 4 wd= 8 500ns monitor: ra1= 4 rd1= 8 ra2=11 rd2= x wa= 5 wd= 10 550ns monitor: ra1= 4 rd1= 8 ra2=11 rd2= x wa= 5 wd= 10 600ns monitor: ra1= 5 rd1= 10 ra2=10 rd2= x wa= 6 wd= 12 650ns monitor: ra1= 5 rd1= 10 ra2=10 rd2= x wa= 6 wd= 12 700ns monitor: ra1= 6 rd1= 12 ra2= 9 rd2= x wa= 7 wd=
14 750ns monitor: ra1= 6 rd1= 12 ra2= 9 rd2= x wa= 7 wd= 14 800ns monitor: ra1= 7 rd1= 14 ra2= 8 rd2= x wa= 8 wd= 16 850ns monitor: ra1= 7 rd1= 14 ra2= 8 rd2= 16 wa= 8 wd= 16 900ns monitor: ra1= 8 rd1= 16 ra2= 7 rd2= 14 wa= 9 wd= 18 950ns monitor: ra1= 8 rd1= 16 ra2= 7 rd2= 14 wa= 9 wd= 18 1000ns monitor: ra1= 9 rd1= 18 ra2= 6 rd2= 12 wa=10 wd= 20 記 憶 體 module memory(input clock, reset, en, rw, input [31:0] abus, input [31:0] dbus_in, output [31:0] db us_out); reg [7:0] m [0:128]; reg [31:0] data; always @(clock or reset or abus or en or rw or dbus_in) begin if (reset == 1) begin {m[0],m[1],m[2],m[3]} = 32'h002F000C; // 0000 L D R2, K0 {m[4],m[5],m[6],m[7]} = 32'h001F000C; // 0004 L D R1, K1 {m[8],m[9],m[10],m[11]} = 32'h13221000; // 0008 LOOP: A DD R2, R2, R1 {m[12],m[13],m[14],m[15]} = 32'h26FFFFF8; // 000C J MP LOOP {m[16],m[17],m[18],m[19]} = 32'h00000000; // 0010 K0: WORD 0 {m[20],m[21],m[22],m[23]} = 32'h00000001; // 0014 K1: WORD 1
data = 32'hZZZZZZZZ; else if (abus >=0 && abus < 128) begin if (en == 1 && rw == 0) // r_w==0:write begin data = dbus_in; {m[abus], m[abus+1], m[abus+2], m[abus+3]} = dbus_in; else if (en == 1 && rw == 1) // r_w==1:read data = {m[abus], m[abus+1], m[abus+2], m[abus+3]}; else data = 32'hZZZZZZZZ; else data = 32'hZZZZZZZZ; assign dbus_out = data; module module main; reg clock, reset, en, rw; reg [31:0] addr; reg [31:0] data_in; wire [31:0] data_out; memory DUT (.clock(clock),.reset(reset),.en(en),.rw(rw),.abus(addr),.dbus_in(data_in),.dbus_out(data_out)); initial // reset: 設 定 memory 內 容 為 0,1,2,...,127 begin clock = 0; reset = 1; en = 0; rw = 1; // rw=1: 讀 取 模 式 #75; en = 1; reset = 0; addr = 0; #500;
addr = 4; rw = 0; // 寫 入 模 式 data_in = 8'h3A; #100; addr = 0; rw = 1; // 讀 取 模 式 data_in = 0; always #50 begin clock = clock + 1; $monitor("%4dns monitor: clk=%d en=%d rw=%d, addr=%8h din=%8h dout=%8h", $stime, clock, en, rw, addr, data_in, data_out); always #200 begin addr=addr+1; initial #2000 $finish; module 執 行 結 果 : D:\Dropbox\Public\web\oc\code\verilog>iverilog -o memory32 memory32.v D:\Dropbox\Public\web\oc\code\verilog>vvp memory32 50ns monitor: clk=1 en=0 rw=1, addr=xxxxxxxx din=xxxxxxxx dout=zzzzzzzz 75ns monitor: clk=1 en=1 rw=1, addr=00000000 din=xxxxxxxx dout=002f000c 100ns monitor: clk=0 en=1 rw=1, addr=00000000 din=xxxxxxxx dout=002f000c 150ns monitor: clk=1 en=1 rw=1, addr=00000000 din=xxxxxxxx dout=002f000c 200ns monitor: clk=0 en=1 rw=1, addr=00000001 din=xxxxxxxx dout=2f000c00 250ns monitor: clk=1 en=1 rw=1, addr=00000001 din=xxxxxxxx dout=2f000c00 300ns monitor: clk=0 en=1 rw=1, addr=00000001 din=xxxxxxxx dout=2f000c00 350ns monitor: clk=1 en=1 rw=1, addr=00000001 din=xxxxxxxx dout=2f000c00
400ns monitor: clk=0 en=1 rw=1, addr=00000002 din=xxxxxxxx dout=000c001f 450ns monitor: clk=1 en=1 rw=1, addr=00000002 din=xxxxxxxx dout=000c001f 500ns monitor: clk=0 en=1 rw=1, addr=00000002 din=xxxxxxxx dout=000c001f 550ns monitor: clk=1 en=1 rw=1, addr=00000002 din=xxxxxxxx dout=000c001f 575ns monitor: clk=1 en=1 rw=0, addr=00000004 din=0000003a dout=0000003a 600ns monitor: clk=0 en=1 rw=0, addr=00000005 din=0000003a dout=0000003a 650ns monitor: clk=1 en=1 rw=0, addr=00000005 din=0000003a dout=0000003a 675ns monitor: clk=1 en=1 rw=1, addr=00000000 din=00000000 dout=002f000c 700ns monitor: clk=0 en=1 rw=1, addr=00000000 din=00000000 dout=002f000c 750ns monitor: clk=1 en=1 rw=1, addr=00000000 din=00000000 dout=002f000c 800ns monitor: clk=0 en=1 rw=1, addr=00000001 din=00000000 dout=2f000c00 850ns monitor: clk=1 en=1 rw=1, addr=00000001 din=00000000 dout=2f000c00 900ns monitor: clk=0 en=1 rw=1, addr=00000001 din=00000000 dout=2f000c00 950ns monitor: clk=1 en=1 rw=1, addr=00000001 din=00000000 dout=2f000c00 1000ns monitor: clk=0 en=1 rw=1, addr=00000002 din=00000000 dout=000c0000 1050ns monitor: clk=1 en=1 rw=1, addr=00000002 din=00000000 dout=000c0000 1100ns monitor: clk=0 en=1 rw=1, addr=00000002 din=00000000 dout=000c0000 1150ns monitor: clk=1 en=1 rw=1, addr=00000002 din=00000000 dout=000c0000 1200ns monitor: clk=0 en=1 rw=1, addr=00000003 din=00000000 dout=0c000000 1250ns monitor: clk=1 en=1 rw=1, addr=00000003 din=00000000 dout=0c000000 1300ns monitor: clk=0 en=1 rw=1, addr=00000003 din=00000000 dout=0c000000 1350ns monitor: clk=1 en=1 rw=1, addr=00000003 din=00000000 dout=0c000000 1400ns monitor: clk=0 en=1 rw=1, addr=00000004 din=00000000 dout=00000000 1450ns monitor: clk=1 en=1 rw=1, addr=00000004 din=00000000 dout=00000000 1500ns monitor: clk=0 en=1 rw=1, addr=00000004 din=00000000 dout=00000000 1550ns monitor: clk=1 en=1 rw=1, addr=00000004 din=00000000 dout=00000000 1600ns monitor: clk=0 en=1 rw=1, addr=00000005 din=00000000 dout=0000003a 1650ns monitor: clk=1 en=1 rw=1, addr=00000005 din=00000000 dout=0000003a 1700ns monitor: clk=0 en=1 rw=1, addr=00000005 din=00000000 dout=0000003a 1750ns monitor: clk=1 en=1 rw=1, addr=00000005 din=00000000 dout=0000003a 1800ns monitor: clk=0 en=1 rw=1, addr=00000006 din=00000000 dout=00003a22 1850ns monitor: clk=1 en=1 rw=1, addr=00000006 din=00000000 dout=00003a22 1900ns monitor: clk=0 en=1 rw=1, addr=00000006 din=00000000 dout=00003a22 1950ns monitor: clk=1 en=1 rw=1, addr=00000006 din=00000000 dout=00003a22 2000ns monitor: clk=0 en=1 rw=1, addr=00000007 din=00000000 dout=003a2210 結 語
在 本 章 中, 我 們 介 紹 了 正 反 器 (Flip-Flop, Latch, 栓 鎖 器 ) 等 循 序 電 路 的 概 念, 並 且 利 用 閘 級 延 遲 的 現 象, 設 計 出 了 脈 衝 偵 測 電 路 (Pause Transition Detector, PTD), 於 是 我 們 可 以 利 用 脈 衝 偵 測 電 路 設 計 出 像 邊 緣 觸 發 型 的 電 路, 像 是 邊 緣 觸 發 型 的 正 反 器 暫 存 器 計 數 器 等 等 電 路, 這 些 電 路 是 構 成 電 腦 當 中 的 記 憶 線 路 的 基 礎 一 但 理 解 這 些 基 礎 原 理 之 後, 我 們 就 可 以 用 Verilog 的 高 階 語 法 直 接 宣 告 暫 存 器 一 群 暫 存 器 與 一 整 塊 記 憶 體, 這 種 高 階 寫 法 已 經 非 常 接 近 高 階 語 言 的 寫 法, 只 是 由 於 是 設 計 硬 體, 所 以 這 些 高 階 指 令 最 後 都 會 被 轉 換 為 線 路, 燒 錄 到 FPGA 或 內 建 於 ASIC 裡 面 而 已, 如 此 我 們 就 不 需 要 用 一 條 一 條 的 線 路 去 兜 出 暫 存 器 或 記 憶 體, 可 以 輕 鬆 的 透 過 Verilog 設 計 出 記 憶 單 元 了
控 制 單 元 簡 介 如 果 您 曾 經 用 硬 接 線 的 方 式 設 計 過 CPU, 那 就 會 發 現 控 制 單 元 主 要 就 是 一 堆 開 關 與 多 工 器 的 接 線 開 關 可 以 用 來 控 制 某 些 資 料 是 否 要 流 過, 而 多 工 器 則 可 以 從 很 多 組 輸 入 資 料 中 選 擇 一 組 輸 出, 以 下 是 一 個 四 選 一 多 工 器 的 方 塊 圖 圖 4 選 1 多 工 器 4 選 1 多 工 器 的 內 部 電 路 結 構 如 下 : 圖 4 選 1 多 工 器 的 內 部 電 路 接 著 就 讓 我 們 來 看 一 個 完 整 的 Verilog 的 4 選 1 的 多 工 器 程 式, 由 於 Verilog 支 援 像 Case 這 樣 的 高 階 語
法, 因 此 在 實 作 時 可 以 不 需 要 採 用 細 部 的 接 線 方 式, 只 要 使 用 case 語 句 就 可 以 輕 易 完 成 多 工 器 的 設 計 檔 案 :mux4.v module mux4(input[1:0] select, input[3:0] d, output reg q ); always @( select or d ) begin case( select ) 0 : q = d[0]; 1 : q = d[1]; 2 : q = d[2]; 3 : q = d[3]; case module module main; reg [3:0] d; reg [1:0] s; wire q; mux4 DUT (s, d, q); initial begin s = 0; d = 4'b0110; always #50 begin s=s+1; $monitor("%4dns monitor: s=%d d=%d q=%d", $stime, s, d, q); initial #1000 $finish; module 執 行 結 果
D:\ccc101\icarus>iverilog mux4.v -o mux4 D:\ccc101\icarus>vvp mux4 50ns monitor: s=1 d= 6 q=1 100ns monitor: s=2 d= 6 q=1 150ns monitor: s=3 d= 6 q=0 200ns monitor: s=0 d= 6 q=0 250ns monitor: s=1 d= 6 q=1 300ns monitor: s=2 d= 6 q=1 350ns monitor: s=3 d= 6 q=0 400ns monitor: s=0 d= 6 q=0 450ns monitor: s=1 d= 6 q=1 500ns monitor: s=2 d= 6 q=1 550ns monitor: s=3 d= 6 q=0 600ns monitor: s=0 d= 6 q=0 650ns monitor: s=1 d= 6 q=1 700ns monitor: s=2 d= 6 q=1 750ns monitor: s=3 d= 6 q=0 800ns monitor: s=0 d= 6 q=0 850ns monitor: s=1 d= 6 q=1 900ns monitor: s=2 d= 6 q=1 950ns monitor: s=3 d= 6 q=0 1000ns monitor: s=0 d= 6 q=0 您 可 以 看 到 在 上 述 範 例 中, 輸 入 資 料 6 的 二 進 位 是 0110, 如 下 所 示 : 位 置 s 3 2 1 0 位 元 d 0 1 1 0 因 此 當 s=0 時 會 輸 出 0, s=1 時 會 輸 出 1, s=2 時 會 輸 出 1, s=3 時 會 輸 出 0, 這 就 是 上 述 輸 出 結 果 的 意 義 但 是 這 種 採 用 多 工 器 硬 的 接 線 方 式, 必 須 搭 配 區 塊 式 的 設 計, 才 能 建 構 出 CPU, 但 是 這 種 方 式 較 為 困 難, 因 此 我 們 留 待 後 續 章 節 再 來 介 紹 為 了 簡 單 起 見, 我 們 會 先 採 用 流 程 式 的 設 計 方 法 流 程 式 設 計 傳 統 上 當 您 設 計 出 ALU 暫 存 器 等 基 本 元 件 之 後, 就 可 以 設 計 控 制 單 元, 去 控 制 這 些 基 本 元 件, 形 成 一 顆 CPU 但 是 在 Verilog 當 中, +, -, *, /, 暫 存 器 等 都 是 基 本 語 法, 因 此 整 個 CPU 的 設 計 其 實 就 是 一 個 控 制 單 元 的 設 計 而 已, 我 們 只 要 在 適 當 的 時 候 呼 叫 +, -, *, / 運 算 與 暫 存 器 讀 取 寫 入 功 能, 就 能 設 計 完 一
顆 CPU 了 換 句 話 說, 只 要 在 Verilog 中 設 計 出 控 制 單 元, 基 本 上 就 已 經 設 計 完 成 整 顆 CPU 了, 因 為 +, -, *, /, 暫 存 器 等 元 件 都 已 經 內 建 了 以 下 是 我 們 用 流 程 法 設 計 mcu0 微 處 理 器 的 重 要 程 式 片 段, 您 可 以 看 到 在 這 種 作 法 上, 整 個 處 理 器 就 僅 僅 是 一 個 控 制 單 元, 而 這 個 控 制 單 元 的 責 任 就 是 根 據 擷 取 解 碼 執 行 的 流 程, 操 控 暫 存 器 的 流 向 與 運 算 module cpu(input clock);... always @(posedge clock) begin // 在 clock 時 脈 的 正 邊 緣 時 觸 發 IR = {m[pc], m[pc+1]}; // 指 令 擷 取 階 段 :IR=m[PC], 2 個 Byte 的 記 憶 體 pc0= PC; // 儲 存 舊 的 PC 值 在 pc0 中 PC = PC+2; // 擷 取 完 成,PC 前 進 到 下 一 個 指 令 位 址 case (`OP) // 解 碼 根 據 OP 執 行 動 作 LD: A = `M; // LD C ST: `M = A; // ST C CMP: begin `N=(A < `M); `Z=(A==`M); // CMP C ADD: A = A + `M; // ADD C JMP: PC = `C; // JMP C JEQ: if (`Z) PC=`C; // JEQ C... case // 印 出 PC, IR, SW, A 等 暫 存 器 值 以 供 觀 察 $display("%4dns PC=%x IR=%x, SW=%x, A=%d", $stime, pc0, IR, SW, A); module 區 塊 式 設 計 module mcu(input clock);... register#(.w(12)) PC(clock, 1, pci, pco); adder#(.w(12)) adder0(2, pco, pcnext); memory mem(mw, `C, ao, pco, ir, `C, mo); register#(.w(16)) A(~clock, aw, aluout, ao);
register#(.w(16)) SW(~clock, sww, aluout, swo); alu alu0(aluop, mo, ao, aluout); mux#(.w(12)) muxpc(pcmux, pcnext, `C, pci); control cu(`op, `Z, mw, aw, pcmux, sww, aluop);... module 以 流 程 法 設 計 控 制 單 元 然 而 當 指 令 愈 來 愈 多, 系 統 愈 來 愈 複 雜 時, 區 塊 式 的 設 計 方 法 就 會 愈 來 愈 困 難, 此 時 有 兩 種 解 決 方 式, 一 種 是 採 用 流 程 式 的 設 計 法 來 撰 寫 控 制 單 元, 操 控 各 種 開 關 與 多 工 器 這 種 設 計 方 法 混 合 了 區 塊 式 與 流 程 式 的 設 計 方 法, 算 是 一 種 折 衷 性 的 方 法 module control(input [3:0] op, input z, output mw, aw, pcmux, sww, outpu t [3:0] aluop); reg mw, aw, pcmux, sww; reg [3:0] aluop; always @(op) begin mw = 0; aw = 0; sww = 0; pcmux = 0; aluop = alu0.zero; case (op) mcu0.ld: begin aw=1; aluop=alu0.apass; // LD C mcu0.st: mw=1; // ST C mcu0.jmp: pcmux=1; // JMP C mcu0.jeq: if (`Z) pcmux=1; // JEQ C mcu0.cmp: begin sww=1; aluop = alu0.cmp; // CMP C mcu0.add: begin aw=1; aluop=alu0.add; // ADD C case module 以 區 塊 法 設 計 控 制 單 元 當 然 我 們 也 可 以 將 上 述 的 控 制 訊 號 硬 是 用 and, or, not 等 方 式 寫 下 來, 這 樣 就 能 將 整 個 設 計 完 全 區 塊 化, 而 去 掉 流 程 式 的 寫 法 了 module control(input [3:0] op, input z, output mw, aw, pcmux, sww, outpu
t [3:0] aluop); assign mw=(op==mcu0.st); assign aw=(op==mcu0.ld op==mcu0.add); assign sww=(op==mcu0.cmp); assign pcmux=(op==mcu0.jmp (op==mcu0.jeq && z)); assign aluop=(op==mcu0.ld)?alu0.apass:(op==mcu0.cmp)?alu0.cmp:(op==mcu 0.ADD)?alu0.ADD:alu0.ZERO; module 以 微 指 令 設 計 控 制 單 元 另 一 種 可 以 克 服 區 塊 式 複 雜 度 問 題 的 方 法, 是 採 用 微 指 令 (microcode or microprogram) 的 設 計 方 法, 這 種 方 法 將 指 令 的 擷 取 (fetch) 解 碼 (decode) 執 行 (execute) 與 寫 回 (write-back) 等 分 成 T1, T2, T3, T4,... 等 子 步 驟, 然 後 用 一 個 微 型 計 數 器 mpc 控 制 這 些 子 步 驟 的 執 行, 如 下 圖 所 示 由 於 T1, T2, T3, T4 代 表 擷 取 (fetch) 解 碼 (decode) 執 行 (execute) 與 寫 回 (write-back), 每 個 步 驟 各 佔 用 一 個 Clock, 於 是 一 個 指 令 需 要 四 個 Clock 才 能 完 成, 因 此 我 們 可 以 用 以 下 方 法 將 各 個 開 關 與 多 工 器 的 控 制 編 為 一 個 表 格 T1 T2 T3 T4 C1 C2 C3... Ck 這 樣 就 可 以 很 有 系 統 的 運 用 區 塊 建 構 法 將 控 制 單 元 也 區 塊 化 了, 於 是 我 們 就 不 需 要 採 用 流 程 式 的 寫 法, 也 能 透 過 按 表 操 課 的 方 法 完 成 處 理 器 的 區 塊 式 建 構 了
微 處 理 器 (Micro Processor) 現 代 的 處 理 器 通 常 會 被 分 為 微 處 理 器 與 高 階 處 理 器 兩 類 微 處 理 器 指 的 是 能 力 較 弱, 通 常 指 令 長 度 較 短, 以 8 位 於 為 主, 少 數 為 16 位 元, 像 是 8051 AVR8 或 PIC 等 處 理 器, 這 些 處 理 器 通 常 不 會 搭 配 外 部 記 憶 體, 因 此 常 用 在 單 晶 片 或 者 嵌 入 式 系 統 當 中 而 高 階 處 理 器 則 是 能 力 較 強, 通 常 指 令 寬 度 較 大 (32 位 元 以 上 ), 像 是 Intel 的 x86 等 等, 高 階 的 ARM11 ARM Cortex 處 理 器 等 等, 這 些 處 理 器 的 定 址 空 間 很 大, 可 以 搭 配 容 量 很 大 的 記 憶 體 (ex: 4GB), 而 且 速 度 很 快, 因 此 需 要 內 建 記 憶 體 管 理 單 元 (Memory Management Unit, MMU) 與 多 層 快 取 機 制 (cache), 以 便 能 夠 充 分 發 揮 處 理 器 的 效 能 在 本 章 當 中, 我 們 將 透 過 mcu0 這 個 簡 易 的 架 構, 說 明 微 處 理 器 的 設 計 方 法 當 然 微 處 理 器 的 設 計 方 法 有 很 多 種, 在 本 章 中 我 們 將 展 示 兩 種 設 計 方 法, 一 種 是 流 程 式 的 設 計 法, 這 種 方 法 比 較 符 合 軟 體 程 式 人 的 設 計 習 慣, 寫 起 來 有 點 類 似 C 語 言 的 寫 法 另 一 種 是 區 塊 式 的 設 計 法, 這 種 方 法 是 傳 統 硬 體 人 的 設 計 方 法, 採 用 像 積 木 一 樣 的 方 式 由 下 往 上 拼 接 出 來, 然 後 再 透 過 線 路 連 接 各 個 區 塊 形 成 一 個 更 大 的 區 塊 MCU0 的 迷 你 版 -- mcu0m MCU0 處 理 器 MCU0 為 一 個 16 位 元 處 理 器, 包 含 指 令 暫 存 器 IR, 程 式 計 數 器 PC, 狀 態 佔 存 器 SW 與 累 積 器 A 等 四 個 暫 存 器 指 令 表 OP name 格 式 意 義 0 LD LD C A = [C] 1 ADD ADD C A = A + [C] 2 JMP JMP C PC = C 3 ST ST C [C] = A 4 CMP CMP C SW = A CMP [C] 5 JEQ JEQ C if SW[30]=Z=1 then PC = C 組 合 語 言 與 機 器 碼 00 LOOP: LD I 0016 02 CMP K10 401A
04 JEQ EXIT 5012 06 ADD K1 1018 08 ST I 3016 0A LD SUM 0014 0C ADD I 1016 0E ST SUM 3014 10 JMP LOOP 2000 12 EXIT: JMP EXIT 2012 14 SUM: WORD 0 0000 16 I: WORD 0 0000 18 K1: WORD 1 0001 1A K10: WORD 10 000A 轉 成 Hex 輸 入 檔 格 式 : 檔 案 : mcu0m.hex 00 16 // 00 LOOP: LD I 40 1A // 02 CMP K10 50 12 // 04 JEQ EXIT 實 作 10 18 // 06 ADD K1 30 16 // 08 ST I 00 14 // 0A LD SUM 10 16 // 0C ADD I 30 14 // 0E ST SUM 20 00 // 10 JMP LOOP 20 12 // 12 EXIT: JMP EXIT 00 00 // 14 SUM: WORD 0 00 00 // 16 I: WORD 0 00 01 // 18 K1: WORD 1 00 0A // 1A K10: WORD 10 Verilog 程 式 實 作 檔 案 : mcu0m.v `define N `define Z `define OP `define C SW[15] // 負 號 旗 標 SW[14] // 零 旗 標 IR[15:12] // 運 算 碼 IR[11:0] // 常 數 欄 位
`define M {m[`c], m[`c+1]} module cpu(input clock); // CPU0-Mini 的 快 取 版 :cpu0mc 模 組 parameter [3:0] LD=4'h0,ADD=4'h1,JMP=4'h2,ST=4'h3,CMP=4'h4,JEQ=4'h5; reg signed [15:0] A; // 宣 告 暫 存 器 R[0..15] 等 16 個 32 位 元 暫 存 器 reg [15:0] IR; // 指 令 暫 存 器 IR reg [15:0] SW; // 指 令 暫 存 器 IR reg [15:0] PC; // 程 式 計 數 器 reg [15:0] pc0; reg [7:0] m [0:32]; // 內 部 的 快 取 記 憶 體 integer i; initial // 初 始 化 begin PC = 0; // 將 PC 設 為 起 動 位 址 0 SW = 0; $readmemh("mcu0m.hex", m); for (i=0; i < 32; i=i+2) begin $display("%8x: %8x", i, {m[i], m[i+1]}); always @(posedge clock) begin // 在 clock 時 脈 的 正 邊 緣 時 觸 發 IR = {m[pc], m[pc+1]}; // 指 令 擷 取 階 段 :IR=m[PC], 2 個 Byte 的 記 憶 體 pc0= PC; // 儲 存 舊 的 PC 值 在 pc0 中 PC = PC+2; // 擷 取 完 成,PC 前 進 到 下 一 個 指 令 位 址 case (`OP) // 解 碼 根 據 OP 執 行 動 作 LD: A = `M; // LD C ST: `M = A; // ST C CMP: begin `N=(A < `M); `Z=(A==`M); // CMP C ADD: A = A + `M; // ADD C JMP: PC = `C; // JMP C JEQ: if (`Z) PC=`C; // JEQ C case // 印 出 PC, IR, SW, A 等 暫 存 器 值 以 供 觀 察 $display("%4dns PC=%x IR=%x, SW=%x, A=%d", $stime, pc0, IR, SW, A);
module module main; reg clock; // 測 試 程 式 開 始 // 時 脈 clock 變 數 cpu cpux(clock); // 宣 告 cpu0mc 處 理 器 initial clock = 0; // 一 開 始 clock 設 定 為 0 always #10 clock=~clock; // 每 隔 10ns 反 相, 時 脈 週 期 為 20ns initial #2000 $finish; // 在 2000 奈 秒 的 時 候 停 止 測 試 module 執 行 結 果 D:\Dropbox\Public\web\oc\code>iverilog -o mcu0m mcu0m.v D:\Dropbox\Public\web\oc\code>vvp cpu16m WARNING: cpu16m.v:20: $readmemh(cpu16m.hex): Not enough words in the file for th e requested range [0:32]. 00000000: 0016 00000002: 401a 00000004: 5012 00000006: 1018 00000008: 3016 0000000a: 0014 0000000c: 1016 0000000e: 3014 00000010: 2000 00000012: 2012 00000014: 0000 00000016: 0000 00000018: 0001 0000001a: 000a 0000001c: xxxx 0000001e: xxxx 10ns PC=0000 IR=0016, SW=0000, A= 0
30ns PC=0002 IR=401a, SW=8000, A= 0 50ns PC=0004 IR=5012, SW=8000, A= 0 70ns PC=0006 IR=1018, SW=8000, A= 1 90ns PC=0008 IR=3016, SW=8000, A= 1 110ns PC=000a IR=0014, SW=8000, A= 0 130ns PC=000c IR=1016, SW=8000, A= 1 150ns PC=000e IR=3014, SW=8000, A= 1 170ns PC=0010 IR=2000, SW=8000, A= 1 190ns PC=0000 IR=0016, SW=8000, A= 1 210ns PC=0002 IR=401a, SW=8000, A= 1 230ns PC=0004 IR=5012, SW=8000, A= 1 250ns PC=0006 IR=1018, SW=8000, A= 2 270ns PC=0008 IR=3016, SW=8000, A= 2 290ns PC=000a IR=0014, SW=8000, A= 1 310ns PC=000c IR=1016, SW=8000, A= 3 330ns PC=000e IR=3014, SW=8000, A= 3 350ns PC=0010 IR=2000, SW=8000, A= 3 370ns PC=0000 IR=0016, SW=8000, A= 2 390ns PC=0002 IR=401a, SW=8000, A= 2 410ns PC=0004 IR=5012, SW=8000, A= 2 430ns PC=0006 IR=1018, SW=8000, A= 3 450ns PC=0008 IR=3016, SW=8000, A= 3 470ns PC=000a IR=0014, SW=8000, A= 3 490ns PC=000c IR=1016, SW=8000, A= 6 510ns PC=000e IR=3014, SW=8000, A= 6 530ns PC=0010 IR=2000, SW=8000, A= 6 550ns PC=0000 IR=0016, SW=8000, A= 3 570ns PC=0002 IR=401a, SW=8000, A= 3 590ns PC=0004 IR=5012, SW=8000, A= 3 610ns PC=0006 IR=1018, SW=8000, A= 4 630ns PC=0008 IR=3016, SW=8000, A= 4 650ns PC=000a IR=0014, SW=8000, A= 6 670ns PC=000c IR=1016, SW=8000, A= 10 690ns PC=000e IR=3014, SW=8000, A= 10 710ns PC=0010 IR=2000, SW=8000, A= 10 730ns PC=0000 IR=0016, SW=8000, A= 4 750ns PC=0002 IR=401a, SW=8000, A= 4
770ns PC=0004 IR=5012, SW=8000, A= 4 790ns PC=0006 IR=1018, SW=8000, A= 5 810ns PC=0008 IR=3016, SW=8000, A= 5 830ns PC=000a IR=0014, SW=8000, A= 10 850ns PC=000c IR=1016, SW=8000, A= 15 870ns PC=000e IR=3014, SW=8000, A= 15 890ns PC=0010 IR=2000, SW=8000, A= 15 910ns PC=0000 IR=0016, SW=8000, A= 5 930ns PC=0002 IR=401a, SW=8000, A= 5 950ns PC=0004 IR=5012, SW=8000, A= 5 970ns PC=0006 IR=1018, SW=8000, A= 6 990ns PC=0008 IR=3016, SW=8000, A= 6 1010ns PC=000a IR=0014, SW=8000, A= 15 1030ns PC=000c IR=1016, SW=8000, A= 21 1050ns PC=000e IR=3014, SW=8000, A= 21 1070ns PC=0010 IR=2000, SW=8000, A= 21 1090ns PC=0000 IR=0016, SW=8000, A= 6 1110ns PC=0002 IR=401a, SW=8000, A= 6 1130ns PC=0004 IR=5012, SW=8000, A= 6 1150ns PC=0006 IR=1018, SW=8000, A= 7 1170ns PC=0008 IR=3016, SW=8000, A= 7 1190ns PC=000a IR=0014, SW=8000, A= 21 1210ns PC=000c IR=1016, SW=8000, A= 28 1230ns PC=000e IR=3014, SW=8000, A= 28 1250ns PC=0010 IR=2000, SW=8000, A= 28 1270ns PC=0000 IR=0016, SW=8000, A= 7 1290ns PC=0002 IR=401a, SW=8000, A= 7 1310ns PC=0004 IR=5012, SW=8000, A= 7 1330ns PC=0006 IR=1018, SW=8000, A= 8 1350ns PC=0008 IR=3016, SW=8000, A= 8 1370ns PC=000a IR=0014, SW=8000, A= 28 1390ns PC=000c IR=1016, SW=8000, A= 36 1410ns PC=000e IR=3014, SW=8000, A= 36 1430ns PC=0010 IR=2000, SW=8000, A= 36 1450ns PC=0000 IR=0016, SW=8000, A= 8 1470ns PC=0002 IR=401a, SW=8000, A= 8 1490ns PC=0004 IR=5012, SW=8000, A= 8
1510ns PC=0006 IR=1018, SW=8000, A= 9 1530ns PC=0008 IR=3016, SW=8000, A= 9 1550ns PC=000a IR=0014, SW=8000, A= 36 1570ns PC=000c IR=1016, SW=8000, A= 45 1590ns PC=000e IR=3014, SW=8000, A= 45 1610ns PC=0010 IR=2000, SW=8000, A= 45 1630ns PC=0000 IR=0016, SW=8000, A= 9 1650ns PC=0002 IR=401a, SW=8000, A= 9 1670ns PC=0004 IR=5012, SW=8000, A= 9 1690ns PC=0006 IR=1018, SW=8000, A= 10 1710ns PC=0008 IR=3016, SW=8000, A= 10 1730ns PC=000a IR=0014, SW=8000, A= 45 1750ns PC=000c IR=1016, SW=8000, A= 55 1770ns PC=000e IR=3014, SW=8000, A= 55 1790ns PC=0010 IR=2000, SW=8000, A= 55 1810ns PC=0000 IR=0016, SW=8000, A= 10 1830ns PC=0002 IR=401a, SW=4000, A= 10 1850ns PC=0004 IR=5012, SW=4000, A= 10 1870ns PC=0012 IR=2012, SW=4000, A= 10 1890ns PC=0012 IR=2012, SW=4000, A= 10 1910ns PC=0012 IR=2012, SW=4000, A= 10 1930ns PC=0012 IR=2012, SW=4000, A= 10 1950ns PC=0012 IR=2012, SW=4000, A= 10 1970ns PC=0012 IR=2012, SW=4000, A= 10 1990ns PC=0012 IR=2012, SW=4000, A= 10 MCU0 的 區 塊 式 設 計 -- MCU0bm.v 前 言 我 們 曾 經 在 下 列 文 章 中 設 計 出 了 MCU0 迷 你 版 這 個 只 有 六 個 指 令 的 微 控 制 器, 整 個 實 作 只 有 51 行 開 放 電 腦 計 畫 (6) 一 顆 只 有 51 行 Verilog 程 式 碼 的 16 位 元 處 理 器 MCU0 但 是 上 述 程 式 雖 然 簡 單, 但 卻 是 採 用 流 程 式 的 寫 法 雖 然 筆 者 不 覺 得 流 程 式 的 寫 法 有 甚 麼 特 別 的 缺 陷, 但 是 對 那 些 習 慣 採 用 硬 體 角 度 設 計 Verilog 程 式 的 人 而 言, 似 乎 採 用 區 塊 式 的 設 計 方 式 才 是 正 統, 所 以 筆 者 將 於 本 文 中 採 用 區 塊 式 的 方 式 重 新 設 計 MCU0 迷 你 版, 以 便 能 學 習 硬 體 設 計 者 的 思 考 方 式 MCU0 迷 你 版 的 指 令 表
為 了 方 便 讀 者 閱 讀, 不 需 要 查 閱 前 文, 我 們 再 次 列 出 了 MCU0 迷 你 版 的 指 令 表 如 下 : OP name 格 式 意 義 0 LD LD C A = [C] 1 ADD ADD C A = A + [C] 2 JMP JMP C PC = C 3 ST ST C [C] = A 4 CMP CMP C SW = A CMP [C] 5 JEQ JEQ C if SW[30]=Z=1 then PC = C MCU0 迷 你 版 的 區 塊 設 計 圖 在 MCU0 迷 你 版 裏, 總 共 有 三 個 暫 存 器, 分 別 是 A, PC 與 SW, 一 個 具 有 兩 組 讀 取 (i1/d1, i2/d2) 與 一 組 寫 入 的 記 憶 體 (wi/wd), 還 有 一 個 算 術 邏 輯 單 元 ALU, 這 個 電 路 的 設 計 圖 如 下 圖 MCU0bm 的 區 塊 設 計 圖 由 於 筆 者 不 熟 悉 數 位 電 路 設 計 的 繪 圖 軟 體, 因 此 就 簡 單 的 用 LibreOffice 的 Impress 繪 製 了 上 圖, 純 粹 採 用 區 塊 表 達 法, 並 沒 有 使 用 標 準 的 數 位 電 路 設 計 圖 示 原 始 碼 根 據 上 圖, 我 們 設 計 出 了 下 列 Verilog 程 式, 您 應 該 可 以 很 清 楚 的 找 出 程 式 與 圖 形 之 間 的 對 應 關 係 module memory(input w, input [11:0] wi, input [15:0] wd, input [11:0] i1
, output [15:0] d1, input [11:0] i2, output [15:0] d2); integer i; reg [7:0] m[0:2**12-1]; initial begin $readmemh("mcu0m.hex", m); for (i=0; i < 32; i=i+2) begin $display("%x: %x", i, {m[i], m[i+1]}); assign d1 = {m[i1], m[i1+1]}; assign d2 = {m[i2], m[i2+1]}; always @(w) begin if (w) {m[wi], m[wi+1]} = wd; module module adder#(parameter W=16)(input [W-1:0] a, input [W-1:0] b, output [ W-1:0] c); assign c = a + b; module module register#(parameter W=16)(input clock, w, input [W-1:0] ri, outpu t [W-1:0] ro); reg [W-1:0] r; always @(posedge clock) begin if (w) r = ri; assign ro=r; module module alu(input [3:0] op, input [15:0] a, input [15:0] b, output reg [1 5:0] c); parameter [3:0] ZERO=4'h0, ADD=4'h1, CMP=4'he, APASS=4'hf; always @(*) begin case (op) ADD: c = a+b; CMP: begin c[15]=(a < b); c[14]=(a==b); c[13:0]=14'h0;
APASS: c = a; default: c = 0; case module module mux#(parameter W=16)(input sel, input [W-1:0] i0, i1, output [W-1 :0] o); assign o=(sel)?i1:i0; module `define OP ir[15:12] `define C ir[11:0] `define N SW.r[15] `define Z SW.r[14] module control(input [3:0] op, input z, output mw, aw, pcmux, sww, outpu t [3:0] aluop); assign mw=(op==mcu0.st); assign aw=(op==mcu0.ld op==mcu0.add); assign sww=(op==mcu0.cmp); assign pcmux=(op==mcu0.jmp (op==mcu0.jeq && z)); assign aluop=(op==mcu0.ld)?alu0.apass:(op==mcu0.cmp)?alu0.cmp:(op==mcu 0.ADD)?alu0.ADD:alu0.ZERO; module module mcu(input clock); parameter [3:0] LD=4'h0,ADD=4'h1,JMP=4'h2,ST=4'h3,CMP=4'h4,JEQ=4'h5; wire mw, aw, pcmux, sww; wire [3:0] aluop; wire [11:0] pco, pci, pcnext; wire [15:0] aluout, ao, swo, ir, mo; register#(.w(12)) PC(clock, 1, pci, pco); adder#(.w(12)) adder0(2, pco, pcnext); memory mem(mw, `C, ao, pco, ir, `C, mo); register#(.w(16)) A(~clock, aw, aluout, ao);
register#(.w(16)) SW(~clock, sww, aluout, swo); alu alu0(aluop, mo, ao, aluout); mux#(.w(12)) muxpc(pcmux, pcnext, `C, pci); control cu(`op, `Z, mw, aw, pcmux, sww, aluop); initial begin PC.r = 0; SW.r = 0; module module main; reg clock; // 測 試 程 式 開 始 // 時 脈 clock 變 數 mcu mcu0(clock); initial begin clock = 0; $monitor("%4dns pc=%x ir=%x mo=%x sw=%x a=%d mw=%b aluout=%x", $stime, mcu0.pc.r, mcu0.ir, mcu0.mo, mcu0.sw.r, mcu0.a.r, mcu0.mw, mcu0.aluout); #1000 $finish; always #5 begin clock=~clock; // 每 隔 5ns 反 相, 時 脈 週 期 為 10ns module 輸 入 的 機 器 碼 mcu0m.hex 為 了 測 試 上 述 程 式, 我 們 同 樣 採 用 了 計 算 SUM=1+2+...+10 的 這 個 程 式 作 為 輸 入, 以 下 是 機 器 碼 與 對 應 的 組 合 語 言 程 式 00 16 // 00 LOOP: LD I 40 1A // 02 CMP N 50 12 // 04 JEQ EXIT 10 18 // 06 ADD K1 30 16 // 08 ST I 00 14 // 0A LD SUM 10 16 // 0C ADD I 30 14 // 0E ST SUM
20 00 // 10 JMP LOOP 20 12 // 12 EXIT: JMP EXIT 00 00 // 14 SUM: WORD 0 00 00 // 16 I: WORD 0 00 01 // 18 K1: WORD 1 00 0A // 1A N: WORD 10 執 行 結 果 編 寫 完 成 之 後, 我 們 就 可 以 測 試 整 個 mcu0bm.v 程 式 了, 其 執 行 結 果 如 下 所 示 D:\Dropbox\Public\web\co\code\mcu0>iverilog mcu0bm.v -o mcu0bm D:\Dropbox\Public\web\co\code\mcu0>vvp mcu0bm WARNING: mcu0bm.v:5: $readmemh(mcu0m.hex): Not enough words in the file f or the requested range [0:4095]. 00000000: 0016 00000002: 401a 00000004: 5012 00000006: 1018 00000008: 3016 0000000a: 0014 0000000c: 1016 0000000e: 3014 00000010: 2000 00000012: 2012 00000014: 0000 00000016: 0000 00000018: 0001 0000001a: 000a 0000001c: xxxx 0000001e: xxxx 0ns pc=000 ir=0016 mo=0000 sw=0000 a= 0 mw=0 aluout=0000 5ns pc=002 ir=401a mo=000a sw=0000 a= 0 mw=0 aluout=0000 15ns pc=004 ir=5012 mo=2012 sw=0000 a= 0 mw=0 aluout=0000 25ns pc=006 ir=1018 mo=0001 sw=0000 a= 0 mw=0 aluout=0001 30ns pc=006 ir=1018 mo=0001 sw=0000 a= 1 mw=0 aluout=0002
35ns pc=008 ir=3016 mo=0001 sw=0000 a= 45ns pc=00a ir=0014 mo=0000 sw=0000 a= 50ns pc=00a ir=0014 mo=0000 sw=0000 a= 55ns pc=00c ir=1016 mo=0001 sw=0000 a= 60ns pc=00c ir=1016 mo=0001 sw=0000 a= 65ns pc=00e ir=3014 mo=0001 sw=0000 a= 75ns pc=010 ir=2000 mo=0016 sw=0000 a= 85ns pc=000 ir=0016 mo=0001 sw=0000 a= 95ns pc=002 ir=401a mo=000a sw=0000 a= 105ns pc=004 ir=5012 mo=2012 sw=0000 a= 115ns pc=006 ir=1018 mo=0001 sw=0000 a= 120ns pc=006 ir=1018 mo=0001 sw=0000 a= 125ns pc=008 ir=3016 mo=0002 sw=0000 a= 135ns pc=00a ir=0014 mo=0001 sw=0000 a= 140ns pc=00a ir=0014 mo=0001 sw=0000 a= 145ns pc=00c ir=1016 mo=0002 sw=0000 a= 150ns pc=00c ir=1016 mo=0002 sw=0000 a= 155ns pc=00e ir=3014 mo=0003 sw=0000 a= 165ns pc=010 ir=2000 mo=0016 sw=0000 a= 175ns pc=000 ir=0016 mo=0002 sw=0000 a= 180ns pc=000 ir=0016 mo=0002 sw=0000 a= 185ns pc=002 ir=401a mo=000a sw=0000 a= 195ns pc=004 ir=5012 mo=2012 sw=0000 a= 205ns pc=006 ir=1018 mo=0001 sw=0000 a= 210ns pc=006 ir=1018 mo=0001 sw=0000 a= 215ns pc=008 ir=3016 mo=0003 sw=0000 a= 225ns pc=00a ir=0014 mo=0003 sw=0000 a= 235ns pc=00c ir=1016 mo=0003 sw=0000 a= 240ns pc=00c ir=1016 mo=0003 sw=0000 a= 245ns pc=00e ir=3014 mo=0006 sw=0000 a= 255ns pc=010 ir=2000 mo=0016 sw=0000 a= 265ns pc=000 ir=0016 mo=0003 sw=0000 a= 270ns pc=000 ir=0016 mo=0003 sw=0000 a= 275ns pc=002 ir=401a mo=000a sw=0000 a= 285ns pc=004 ir=5012 mo=2012 sw=0000 a= 295ns pc=006 ir=1018 mo=0001 sw=0000 a= 300ns pc=006 ir=1018 mo=0001 sw=0000 a= 1 mw=1 aluout=0000 1 mw=0 aluout=0000 0 mw=0 aluout=0000 0 mw=0 aluout=0001 1 mw=0 aluout=0002 1 mw=1 aluout=0000 1 mw=0 aluout=0000 1 mw=0 aluout=0001 1 mw=0 aluout=0000 1 mw=0 aluout=0000 1 mw=0 aluout=0002 2 mw=0 aluout=0003 2 mw=1 aluout=0000 2 mw=0 aluout=0001 1 mw=0 aluout=0001 1 mw=0 aluout=0003 3 mw=0 aluout=0005 3 mw=1 aluout=0000 3 mw=0 aluout=0000 3 mw=0 aluout=0002 2 mw=0 aluout=0002 2 mw=0 aluout=0000 2 mw=0 aluout=0000 2 mw=0 aluout=0003 3 mw=0 aluout=0004 3 mw=1 aluout=0000 3 mw=0 aluout=0003 3 mw=0 aluout=0006 6 mw=0 aluout=0009 6 mw=1 aluout=0000 6 mw=0 aluout=0000 6 mw=0 aluout=0003 3 mw=0 aluout=0003 3 mw=0 aluout=0000 3 mw=0 aluout=0000 3 mw=0 aluout=0004 4 mw=0 aluout=0005
305ns pc=008 ir=3016 mo=0004 sw=0000 a= 315ns pc=00a ir=0014 mo=0006 sw=0000 a= 320ns pc=00a ir=0014 mo=0006 sw=0000 a= 325ns pc=00c ir=1016 mo=0004 sw=0000 a= 330ns pc=00c ir=1016 mo=0004 sw=0000 a= 335ns pc=00e ir=3014 mo=000a sw=0000 a= 345ns pc=010 ir=2000 mo=0016 sw=0000 a= 355ns pc=000 ir=0016 mo=0004 sw=0000 a= 360ns pc=000 ir=0016 mo=0004 sw=0000 a= 365ns pc=002 ir=401a mo=000a sw=0000 a= 375ns pc=004 ir=5012 mo=2012 sw=0000 a= 385ns pc=006 ir=1018 mo=0001 sw=0000 a= 390ns pc=006 ir=1018 mo=0001 sw=0000 a= 395ns pc=008 ir=3016 mo=0005 sw=0000 a= 405ns pc=00a ir=0014 mo=000a sw=0000 a= 410ns pc=00a ir=0014 mo=000a sw=0000 a= 415ns pc=00c ir=1016 mo=0005 sw=0000 a= 420ns pc=00c ir=1016 mo=0005 sw=0000 a= 425ns pc=00e ir=3014 mo=000f sw=0000 a= 435ns pc=010 ir=2000 mo=0016 sw=0000 a= 445ns pc=000 ir=0016 mo=0005 sw=0000 a= 450ns pc=000 ir=0016 mo=0005 sw=0000 a= 455ns pc=002 ir=401a mo=000a sw=0000 a= 465ns pc=004 ir=5012 mo=2012 sw=0000 a= 475ns pc=006 ir=1018 mo=0001 sw=0000 a= 480ns pc=006 ir=1018 mo=0001 sw=0000 a= 485ns pc=008 ir=3016 mo=0006 sw=0000 a= 495ns pc=00a ir=0014 mo=000f sw=0000 a= 500ns pc=00a ir=0014 mo=000f sw=0000 a= 505ns pc=00c ir=1016 mo=0006 sw=0000 a= 510ns pc=00c ir=1016 mo=0006 sw=0000 a= 515ns pc=00e ir=3014 mo=0015 sw=0000 a= 525ns pc=010 ir=2000 mo=0016 sw=0000 a= 535ns pc=000 ir=0016 mo=0006 sw=0000 a= 540ns pc=000 ir=0016 mo=0006 sw=0000 a= 545ns pc=002 ir=401a mo=000a sw=0000 a= 555ns pc=004 ir=5012 mo=2012 sw=0000 a= 4 mw=1 aluout=0000 4 mw=0 aluout=0006 6 mw=0 aluout=0006 6 mw=0 aluout=000a 10 mw=0 aluout=000e 10 mw=1 aluout=0000 10 mw=0 aluout=0000 10 mw=0 aluout=0004 4 mw=0 aluout=0004 4 mw=0 aluout=0000 4 mw=0 aluout=0000 4 mw=0 aluout=0005 5 mw=0 aluout=0006 5 mw=1 aluout=0000 5 mw=0 aluout=000a 10 mw=0 aluout=000a 10 mw=0 aluout=000f 15 mw=0 aluout=0014 15 mw=1 aluout=0000 15 mw=0 aluout=0000 15 mw=0 aluout=0005 5 mw=0 aluout=0005 5 mw=0 aluout=0000 5 mw=0 aluout=0000 5 mw=0 aluout=0006 6 mw=0 aluout=0007 6 mw=1 aluout=0000 6 mw=0 aluout=000f 15 mw=0 aluout=000f 15 mw=0 aluout=0015 21 mw=0 aluout=001b 21 mw=1 aluout=0000 21 mw=0 aluout=0000 21 mw=0 aluout=0006 6 mw=0 aluout=0006 6 mw=0 aluout=0000 6 mw=0 aluout=0000
565ns pc=006 ir=1018 mo=0001 sw=0000 a= 570ns pc=006 ir=1018 mo=0001 sw=0000 a= 575ns pc=008 ir=3016 mo=0007 sw=0000 a= 585ns pc=00a ir=0014 mo=0015 sw=0000 a= 590ns pc=00a ir=0014 mo=0015 sw=0000 a= 595ns pc=00c ir=1016 mo=0007 sw=0000 a= 600ns pc=00c ir=1016 mo=0007 sw=0000 a= 605ns pc=00e ir=3014 mo=001c sw=0000 a= 615ns pc=010 ir=2000 mo=0016 sw=0000 a= 625ns pc=000 ir=0016 mo=0007 sw=0000 a= 630ns pc=000 ir=0016 mo=0007 sw=0000 a= 635ns pc=002 ir=401a mo=000a sw=0000 a= 645ns pc=004 ir=5012 mo=2012 sw=0000 a= 655ns pc=006 ir=1018 mo=0001 sw=0000 a= 660ns pc=006 ir=1018 mo=0001 sw=0000 a= 665ns pc=008 ir=3016 mo=0008 sw=0000 a= 675ns pc=00a ir=0014 mo=001c sw=0000 a= 680ns pc=00a ir=0014 mo=001c sw=0000 a= 685ns pc=00c ir=1016 mo=0008 sw=0000 a= 690ns pc=00c ir=1016 mo=0008 sw=0000 a= 695ns pc=00e ir=3014 mo=0024 sw=0000 a= 705ns pc=010 ir=2000 mo=0016 sw=0000 a= 715ns pc=000 ir=0016 mo=0008 sw=0000 a= 720ns pc=000 ir=0016 mo=0008 sw=0000 a= 725ns pc=002 ir=401a mo=000a sw=0000 a= 735ns pc=004 ir=5012 mo=2012 sw=0000 a= 745ns pc=006 ir=1018 mo=0001 sw=0000 a= 750ns pc=006 ir=1018 mo=0001 sw=0000 a= 755ns pc=008 ir=3016 mo=0009 sw=0000 a= 765ns pc=00a ir=0014 mo=0024 sw=0000 a= 770ns pc=00a ir=0014 mo=0024 sw=0000 a= 775ns pc=00c ir=1016 mo=0009 sw=0000 a= 780ns pc=00c ir=1016 mo=0009 sw=0000 a= 785ns pc=00e ir=3014 mo=002d sw=0000 a= 795ns pc=010 ir=2000 mo=0016 sw=0000 a= 805ns pc=000 ir=0016 mo=0009 sw=0000 a= 810ns pc=000 ir=0016 mo=0009 sw=0000 a= 6 mw=0 aluout=0007 7 mw=0 aluout=0008 7 mw=1 aluout=0000 7 mw=0 aluout=0015 21 mw=0 aluout=0015 21 mw=0 aluout=001c 28 mw=0 aluout=0023 28 mw=1 aluout=0000 28 mw=0 aluout=0000 28 mw=0 aluout=0007 7 mw=0 aluout=0007 7 mw=0 aluout=0000 7 mw=0 aluout=0000 7 mw=0 aluout=0008 8 mw=0 aluout=0009 8 mw=1 aluout=0000 8 mw=0 aluout=001c 28 mw=0 aluout=001c 28 mw=0 aluout=0024 36 mw=0 aluout=002c 36 mw=1 aluout=0000 36 mw=0 aluout=0000 36 mw=0 aluout=0008 8 mw=0 aluout=0008 8 mw=0 aluout=0000 8 mw=0 aluout=0000 8 mw=0 aluout=0009 9 mw=0 aluout=000a 9 mw=1 aluout=0000 9 mw=0 aluout=0024 36 mw=0 aluout=0024 36 mw=0 aluout=002d 45 mw=0 aluout=0036 45 mw=1 aluout=0000 45 mw=0 aluout=0000 45 mw=0 aluout=0009 9 mw=0 aluout=0009
815ns pc=002 ir=401a mo=000a sw=0000 a= 825ns pc=004 ir=5012 mo=2012 sw=0000 a= 835ns pc=006 ir=1018 mo=0001 sw=0000 a= 840ns pc=006 ir=1018 mo=0001 sw=0000 a= 845ns pc=008 ir=3016 mo=000a sw=0000 a= 855ns pc=00a ir=0014 mo=002d sw=0000 a= 860ns pc=00a ir=0014 mo=002d sw=0000 a= 865ns pc=00c ir=1016 mo=000a sw=0000 a= 870ns pc=00c ir=1016 mo=000a sw=0000 a= 875ns pc=00e ir=3014 mo=0037 sw=0000 a= 885ns pc=010 ir=2000 mo=0016 sw=0000 a= 895ns pc=000 ir=0016 mo=000a sw=0000 a= 900ns pc=000 ir=0016 mo=000a sw=0000 a= 905ns pc=002 ir=401a mo=000a sw=0000 a= 910ns pc=002 ir=401a mo=000a sw=4000 a= 915ns pc=004 ir=5012 mo=2012 sw=4000 a= 925ns pc=012 ir=2012 mo=2012 sw=4000 a= 9 mw=0 aluout=0000 9 mw=0 aluout=0000 9 mw=0 aluout=000a 10 mw=0 aluout=000b 10 mw=1 aluout=0000 10 mw=0 aluout=002d 45 mw=0 aluout=002d 45 mw=0 aluout=0037 55 mw=0 aluout=0041 55 mw=1 aluout=0000 55 mw=0 aluout=0000 55 mw=0 aluout=000a 10 mw=0 aluout=000a 10 mw=0 aluout=4000 10 mw=0 aluout=4000 10 mw=0 aluout=0000 10 mw=0 aluout=0000 您 可 以 清 楚 的 看 到, 該 程 式 在 870ns 時 計 算 出 了 總 合 SUM=55 的 結 果, 這 代 表 mcu0bm.v 的 設 計 完 成 了 計 算 1+...+10 的 功 能 結 語 在 上 述 實 作 中, 採 用 區 塊 式 設 計 的 mcu0bm.v 總 共 有 98 行, 比 起 同 樣 功 能 的 流 程 式 設 計 mcu0m.v 的 51 行 多 了 將 近 一 倍, 而 且 程 式 的 設 計 難 度 感 覺 高 了 不 少, 但 是 我 們 可 以 很 清 楚 的 掌 握 到 整 個 設 計 的 硬 體 結 構, 這 是 採 用 流 程 式 設 計 所 難 以 確 定 的 當 然 由 於 筆 者 是 程 式 人 員, 並 非 硬 體 設 計 人 員, 因 此 比 較 喜 歡 採 用 流 程 式 的 設 計 方 式 不 過 採 用 了 區 塊 式 設 計 法 設 計 出 mcu0bm.v 之 後, 也 逐 漸 開 始 能 理 解 這 種 硬 體 導 向 的 設 計 方 式, 這 大 概 是 我 在 撰 寫 本 程 式 時 最 大 的 收 穫 了 MCU0 完 整 版 MCU0 的 架 構 MCU0 是 一 顆 16 位 元 的 CPU, 所 有 暫 存 器 都 是 16 位 元 的, 總 共 有 (IR, SP, LR, SW, PC, A) 等 暫 存 器, 如 下 所 示 : `define A R[0] // 累 積 器 `define LR R[1] // 狀 態 暫 存 器 `define SW R[2] // 狀 態 暫 存 器 `define SP R[3] // 堆 疊 暫 存 器
`define PC R[4] // 程 式 計 數 器 這 些 暫 存 器 的 功 能 與 說 明 如 下 : 暫 存 器 名 稱 IR 功 能 指 令 暫 存 器 說 明 用 來 儲 存 從 記 憶 體 載 入 的 機 器 碼 指 令 A =R[0] 累 積 器 用 來 儲 存 計 算 的 結 果, 像 是 加 減 法 的 結 果 LR=R[1] SW=R[2] SP=R[3] PC=R[4] 連 結 暫 存 器 狀 態 暫 存 器 堆 疊 暫 存 器 程 式 計 數 器 用 來 儲 存 函 數 呼 叫 的 返 回 位 址 用 來 儲 存 CMP 比 較 指 令 的 結 果 旗 標, 像 是 負 旗 標 N 與 零 旗 標 Z 等 作 為 條 件 跳 躍 JEQ 等 指 令 是 否 跳 躍 的 判 斷 依 據 堆 疊 指 標,PUSH, POP 指 令 會 用 到 用 來 儲 存 指 令 的 位 址 ( 也 就 是 目 前 執 行 到 哪 個 指 令 的 記 憶 體 位 址 ) MCU0 的 指 令 表 指 令 暫 存 器 IR 的 前 4 個 位 元 是 指 令 代 碼 OP, 由 於 4 位 元 只 能 表 達 16 種 指 令, 這 數 量 太 少 不 敷 使 用, 因 此 當 OP=0xF 時, 我 們 繼 續 用 後 面 的 位 元 作 為 延 伸 代 碼, 以 便 有 更 多 的 指 令 可 以 使 用, 以 下 是 MCU0 微 控 制 器 的 完 整 指 令 表 代 碼 名 稱 格 式 說 明 語 意 0 LD LD C 載 入 A = [C] 1 ST ST C 儲 存 [C] = A 2 ADD ADD C 加 法 A = A + [C] 3 SUB SUB C 減 法 A = A - [C] 4 MUL MUL C 乘 法 A = A * [C] 5 DIV DIV C 除 法 A = A / [C] 6 AND AND C 位 元 AND 運 算 A = A & [C]
7 OR OR C 8 XOR XOR C 位 元 OR 運 算 位 元 XOR 運 算 A = A [C] A = A ^ [C] 9 CMP CMP C 比 較 SW = A CMP [C] ; N=(A<[C]), Z=(A==[C]) A JMP JMP C 跳 躍 PC = C B JEQ JEQ C C JLT JLT C D JLE JLE C E CALL CALL C 相 等 時 跳 躍 小 於 時 跳 躍 小 於 或 等 於 時 跳 躍 呼 叫 副 程 式 if Z then PC = C if N then PC = C if Z or N then PC = C LR=PC; PC = C F OP8 OP 為 8 位 元 的 運 算 F0 LDI LDI Ra,C4 載 入 常 數 Ra=C4 F2 MOV MOV Ra,Rb 暫 存 器 移 動 Ra=Rb F3 PUSH PUSH Ra 堆 疊 推 入 SP--; [SP] = Ra F4 POP POP Ra 堆 疊 取 出 Ra=[SP]; SP++; F5 SHL SHL Ra,C4 左 移 Ra = Ra << C4 F6 SHR SHL Ra,C4 右 移 Ra = Ra >> C4 F7 ADDI ADDI Ra,C4 常 數 加 法 Ra = Ra + C4 SUBI
F8 SUBI Ra,C4 常 數 減 法 Ra = Ra - C4 F9 NEG NEG Ra 反 相 Ra = ~Ra FA SWI SWI C 軟 體 中 斷 BIOS 中 斷 呼 叫 FD NSW NSW 狀 態 反 相 N=~N, Z=~Z; 由 於 沒 有 JGE, JGT, JNE, 因 此 可 用 此 指 令 將 SW 反 相, 再 用 JLE, JLT, JEQ 完 成 跳 躍 動 作 FE RET RET 返 回 PC = LR FF IRET IRET 從 中 斷 返 回 PC = LR; I=0; mcu0 程 式 碼 檔 案 :mcu0s.v `define OP IR[15:12] // 運 算 碼 `define C IR[11:0] // 常 數 欄 位 `define SC8 $signed(ir[7:0]) // 常 數 欄 位 `define C4 IR[3:0] // 常 數 欄 位 `define Ra IR[7:4] // Ra `define Rb IR[3:0] // Rb `define A R[0] // 累 積 器 `define LR R[1] // 狀 態 暫 存 器 `define SW R[2] // 狀 態 暫 存 器 `define SP R[3] // 堆 疊 暫 存 器 `define PC R[4] // 程 式 計 數 器 `define N `SW[15] // 負 號 旗 標 `define Z `SW[14] // 零 旗 標 `define I `SW[3] // 是 否 中 斷 中 `define M m[`c] // 存 取 記 憶 體 module cpu(input clock); // CPU0-Mini 的 快 取 版 :cpu0mc 模 組 parameter [3:0] LD=4'h0,ST=4'h1,ADD=4'h2,SUB=4'h3,MUL=4'h4,DIV=4'h5,AND =4'h6,OR=4'h7,XOR=4'h8,CMP=4'h9,JMP=4'hA,JEQ=4'hB, JLT=4'hC, JLE=4'hD, JS UB=4'hE, OP8=4'hF; parameter [3:0] LDI=4'h0, MOV=4'h2, PUSH=4'h3, POP=4'h4, SHL=4'h5, SHR= 4'h6, ADDI=4'h7, SUBI=4'h8, NEG=4'h9, SWI=4'hA, NSW=4'hD, RET=4'hE, IRET=
4'hF; reg [15:0] IR; // 指 令 暫 存 器 reg signed [15:0] R[0:4]; reg signed [15:0] pc0; reg signed [15:0] m [0:4096]; // 內 部 的 快 取 記 憶 體 integer i; initial // 初 始 化 begin `PC = 0; // 將 PC 設 為 起 動 位 址 0 `SW = 0; $readmemh("mcu0s.hex", m); always @(posedge clock) begin // 在 clock 時 脈 的 正 邊 緣 時 觸 發 IR = m[`pc]; // 指 令 擷 取 階 段 :IR=m[PC], 2 個 Byte 的 記 憶 體 pc0= `PC; // 儲 存 舊 的 PC 值 在 pc0 中 `PC = `PC+1; // 擷 取 完 成,PC 前 進 到 下 一 個 指 令 位 址 case (`OP) // 解 碼 根 據 OP 執 行 動 作 LD: `A = `M; // LD C ST: `M = `A; // ST C ADD: `A = `A + `M; // ADD C SUB: `A = `A - `M; // SUB C MUL: `A = `A * `M; // MUL C DIV: `A = `A / `M; // DIV C AND: `A = `A & `M; // AND C OR : `A = `A `M; // OR C XOR: `A = `A ^ `M; // XOR C CMP: begin `N=(`A < `M); `Z=(`A==`M); // CMP C JMP: `PC = `C; // JSUB C JEQ: if (`Z) `PC=`C; // JEQ C JLT: if (`N) `PC=`C; // JLT C JLE: if (`N `Z) `PC=`C;// JLE C JSUB:begin `LR = `PC; `PC = `C; // JSUB C OP8: case (IR[11:8]) // OP8: 加 長 運 算 碼 LDI: R[`Ra] = `C4; // LDI C ADDI: R[`Ra] = R[`Ra] + `C4; // ADDI C
SUBI: R[`Ra] = R[`Ra] - `C4; // ADDI C MOV: R[`Ra] = R[`Rb]; // MOV Ra, Rb PUSH: begin `SP=`SP-1; m[`sp] = R[`Ra]; // PUSH Ra POP: begin R[`Ra] = m[`sp]; `SP=`SP+1; // POP Ra SHL: R[`Ra] = R[`Ra] << `C4; // SHL C SHR: R[`Ra] = R[`Ra] >> `C4; // SHR C SWI: $display("swi C8=%d A=%d", `SC8, `A); // SWI C NEG: R[`Ra] = ~R[`Ra]; // NEG Ra NSW: begin `N=~`N; `Z=~`Z; // NSW (negate N, Z ) RET: `PC = `LR; // RET IRET: begin `PC = `LR; `I = 0; // IRET default: $display("op8=%d, not defined!", IR[11:8]); case case // 印 出 PC, IR, SW, A 等 暫 存 器 值 以 供 觀 察 $display("%4dns PC=%x IR=%x, SW=%x, A=%d SP=%x LR=%x", $stime, pc0, I R, `SW, `A, `SP, `LR); module module main; reg clock; // 測 試 程 式 開 始 // 時 脈 clock 變 數 cpu mcu0(clock); // 宣 告 mcu0 處 理 器 initial clock = 0; // 一 開 始 clock 設 定 為 0 always #10 clock=~clock; // 每 隔 10ns 反 相, 時 脈 週 期 為 20ns initial #1000 $finish; // 停 止 測 試 module 組 合 語 言 檔 案 :mcu0s.hex 0020 // 00 RESET: LD X 2021 // 01 ADD Y
3021 // 02 SUB Y 4021 // 03 MUL Y 5021 // 04 DIV Y 7021 // 05 OR Y 6021 // 06 AND Y 8021 // 07 XOR Y 0020 // 08 LD X F503 // 09 SHL A, 3 F603 // 0A SHR A, 3 F701 // 0B ADDI 1 0023 // 0C LD STACKEND F230 // 0D MOV SP, A E011 // 0E JSUB MIN 0022 // 0F LD Z A010 // 10 HALT: JMP HALT F301 // 11 MIN: PUSH LR 0020 // 12 LD X 9021 // 13 CMP Y FD00 // 14 NSW C018 // 15 JLT ELSE 1022 // 16 ST Z A019 // 17 JMP NEXT 0021 // 18 ELSE: LD Y 1022 // 19 NEXT: ST Z F401 // 1A POP LR FE00 // 1B RET 0000 // 1C 0000 // 1D 0000 // 1E 0000 // 1F 0003 // 20 X: WORD 3 0005 // 21 Y: WORD 5 0000 // 22 Z: WORD 0 007F // 23 STACKEND: WORD 127 執 行 結 果 D:\Dropbox\Public\web\oc\code\mcu0>iverilog -o mcu0s mcu0s.v
D:\Dropbox\Public\web\oc\code\mcu0>vvp mcu0s WARNING: mcu0s.v:29: $readmemh(mcu0s.hex): Not enough words in the file f or the requested range [0:4096]. 10ns PC=0000 IR=0020, SW=0000, A= 3 SP=xxxx LR=xxxx 30ns PC=0001 IR=2021, SW=0000, A= 8 SP=xxxx LR=xxxx 50ns PC=0002 IR=3021, SW=0000, A= 3 SP=xxxx LR=xxxx 70ns PC=0003 IR=4021, SW=0000, A= 15 SP=xxxx LR=xxxx 90ns PC=0004 IR=5021, SW=0000, A= 3 SP=xxxx LR=xxxx 110ns PC=0005 IR=7021, SW=0000, A= 7 SP=xxxx LR=xxxx 130ns PC=0006 IR=6021, SW=0000, A= 5 SP=xxxx LR=xxxx 150ns PC=0007 IR=8021, SW=0000, A= 0 SP=xxxx LR=xxxx 170ns PC=0008 IR=0020, SW=0000, A= 3 SP=xxxx LR=xxxx 190ns PC=0009 IR=f503, SW=0000, A= 24 SP=xxxx LR=xxxx 210ns PC=000a IR=f603, SW=0000, A= 3 SP=xxxx LR=xxxx 230ns PC=000b IR=f701, SW=0000, A= 4 SP=xxxx LR=xxxx 250ns PC=000c IR=0023, SW=0000, A= 127 SP=xxxx LR=xxxx 270ns PC=000d IR=f230, SW=0000, A= 127 SP=007f LR=xxxx 290ns PC=000e IR=e011, SW=0000, A= 127 SP=007f LR=000f 310ns PC=0011 IR=f301, SW=0000, A= 127 SP=007e LR=000f 330ns PC=0012 IR=0020, SW=0000, A= 3 SP=007e LR=000f 350ns PC=0013 IR=9021, SW=8000, A= 3 SP=007e LR=000f 370ns PC=0014 IR=fd00, SW=4000, A= 3 SP=007e LR=000f 390ns PC=0015 IR=c018, SW=4000, A= 3 SP=007e LR=000f 410ns PC=0016 IR=1022, SW=4000, A= 3 SP=007e LR=000f 430ns PC=0017 IR=a019, SW=4000, A= 3 SP=007e LR=000f 450ns PC=0019 IR=1022, SW=4000, A= 3 SP=007e LR=000f 470ns PC=001a IR=f401, SW=4000, A= 127 SP=007f LR=000f 490ns PC=001b IR=fe00, SW=4000, A= 127 SP=007f LR=000f 510ns PC=000f IR=0022, SW=4000, A= 3 SP=007f LR=000f 530ns PC=0010 IR=a010, SW=4000, A= 3 SP=007f LR=000f 550ns PC=0010 IR=a010, SW=4000, A= 3 SP=007f LR=000f 570ns PC=0010 IR=a010, SW=4000, A= 3 SP=007f LR=000f 590ns PC=0010 IR=a010, SW=4000, A= 3 SP=007f LR=000f 610ns PC=0010 IR=a010, SW=4000, A= 3 SP=007f LR=000f 630ns PC=0010 IR=a010, SW=4000, A= 3 SP=007f LR=000f
650ns PC=0010 IR=a010, SW=4000, A= 670ns PC=0010 IR=a010, SW=4000, A= 690ns PC=0010 IR=a010, SW=4000, A= 710ns PC=0010 IR=a010, SW=4000, A= 730ns PC=0010 IR=a010, SW=4000, A= 750ns PC=0010 IR=a010, SW=4000, A= 770ns PC=0010 IR=a010, SW=4000, A= 790ns PC=0010 IR=a010, SW=4000, A= 810ns PC=0010 IR=a010, SW=4000, A= 830ns PC=0010 IR=a010, SW=4000, A= 850ns PC=0010 IR=a010, SW=4000, A= 870ns PC=0010 IR=a010, SW=4000, A= 890ns PC=0010 IR=a010, SW=4000, A= 910ns PC=0010 IR=a010, SW=4000, A= 930ns PC=0010 IR=a010, SW=4000, A= 950ns PC=0010 IR=a010, SW=4000, A= 970ns PC=0010 IR=a010, SW=4000, A= 990ns PC=0010 IR=a010, SW=4000, A= 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 3 SP=007f LR=000f 結 語 由 於 16 位 元 處 理 器 的 指 令 長 度 很 短, 因 此 空 間 必 須 有 效 利 用, 所 以 我 們 將 一 些 不 包 含 記 憶 體 位 址 的 指 令, 編 到 最 後 的 0xF 的 OP 代 碼 當 中, 這 樣 就 可 以 再 度 延 伸 出 一 大 群 指 令 空 間, 於 是 讓 指 令 數 可 以 不 受 限 於 4 位 元 OP 碼 的 16 個 指 令, 而 能 延 伸 為 30 個 左 右 的 指 令 在 使 用 Verilog 這 種 硬 體 描 述 語 言 設 計 處 理 器 時, 位 元 數 越 少, 往 往 處 理 器 的 指 令 長 度 越 少, 這 時 處 理 器 不 見 得 會 更 好 設 計, 往 往 反 而 會 更 難 設 計, 指 令 集 的 編 碼 相 對 會 困 難 一 些
輸 出 入 單 元 (I/O) 前 言 圖 PC 匯 流 排 的 連 接 結 構 BUS ( 總 線, 匯 流 排 ) 由 於 線 路 多 的 話 會 很 混 亂, 而 且 成 本 很 高 舉 例 而 言, 假 如 有 n 個 節 點 ( 裝 置 ), 所 有 節 點 之 間 都 要 互 相 直 接 相 連, 那 麼 就 需 要 n*(n-1)2/ 這 個 多 組 線 路, 這 將 會 是 個 密 密 麻 麻 的 災 難 如 果 我 們 讓 所 有 的 節 點 都 連 接 到 一 組 共 用 的 線 路, 這 套 線 路 就 稱 為 BUS 只 要 大 家 都 遵 循 一 套 固 定 的 傳 送 規 則, 我 們 就 可 以 用 BUS 作 為 所 有 人 的 通 訊 橋 梁 以 下 是 用 Verilog 宣 告 BUS 線 路 的 三 種 方 法, 分 別 是 wire, wand 與 wor wire [n-1:0] BUS; wand [n-1:0] andbus; wor [n-1:0] orbus; 下 列 是 採 用 wire 方 式 宣 告 BUS 的 一 個 範 例, 當 某 個 seli 選 擇 線 為 1 時, 就 會 將 對 應 的 來 源 資 料 sourcei 放 到 BUS 上 而 那 些 沒 被 選 到 的 來 源, 由 於 是 放 高 阻 抗 Z, 所 以 會 處 於 斷 線 的 狀 態 wire [n-1:0] BUS; parameter [n-1:0] disable = n'bz; assign BUS = sel1?source1:disable;
assign BUS = sel2?source2:disable;... assign BUS = selk?sourcek:disable; 另 外 兩 種 宣 告 BUS 的 方 法, 也 就 是 wand 與 wor, 與 wire 其 實 很 像, 差 別 只 在 於 wand 的 disable 是 用 n'b1, 而 wor 的 disable 是 用 n'b0 同 步 匯 流 排 (Synchronous BUS) module master(input clock, w, output [15:0] address, inout [15:0] data); reg [15:0] ar, dr; assign address = ar; assign data = (w)?dr : 16'hzzzz; always @(*) begin if (!w) dr=#1 data; module module rdevice(input clock, w, input [15:0] address, output [15:0] data) ; reg [15:0] dr; assign data=(!w)?dr:16'hzzzz; always @(clock or w or address) begin if (!w && address == 16'hFFF0) dr = #1 16'he3e3; module module wdevice(input clock, w, input [15:0] address, input [15:0] data); reg [15:0] dr; always @(clock or w or address) begin if (w && address == 16'hFFF8) dr = #1 data;
module module main; reg clock, w; wire [15:0] abus, dbus; master m(clock, w, abus, dbus); rdevice rd(clock, w, abus, dbus); wdevice wd(clock, w, abus, dbus); initial begin $monitor("%4dns abus=%x dbus=%x w=%x m.ar=%x m.dr=%x rd.dr=%x wd.dr=%x", $stime, abus, dbus, w, m.ar, m.dr, rd.dr, wd.dr); clock = 0; #10; m.ar=16'h0000; w=0; #50; m.ar=16'hfff0; #50; m.ar=16'hfff8; m.dr=16'h71f0; w=1; #300; $finish; always #5 clock=~clock; // 每 隔 5ns 反 相, 時 脈 週 期 為 10ns module 執 行 結 果 D:\Dropbox\Public\web\co\code>iverilog syncbus.v -o syncbus D:\Dropbox\Public\web\co\code>vvp syncbus 0ns abus=xxxx dbus=xxxx w=x m.ar=xxxx m.dr=xxxx rd.dr=xxxx wd.dr=xxxx 10ns abus=0000 dbus=xxxx w=0 m.ar=0000 m.dr=xxxx rd.dr=xxxx wd.dr=xxxx 60ns abus=fff0 dbus=xxxx w=0 m.ar=fff0 m.dr=xxxx rd.dr=xxxx wd.dr=xxxx 61ns abus=fff0 dbus=e3e3 w=0 m.ar=fff0 m.dr=xxxx rd.dr=e3e3 wd.dr=xxxx 62ns abus=fff0 dbus=e3e3 w=0 m.ar=fff0 m.dr=e3e3 rd.dr=e3e3 wd.dr=xxxx 110ns abus=fff8 dbus=71f0 w=1 m.ar=fff8 m.dr=71f0 rd.dr=e3e3 wd.dr=xxxx 111ns abus=fff8 dbus=71f0 w=1 m.ar=fff8 m.dr=71f0 rd.dr=e3e3 wd.dr=e3e3 116ns abus=fff8 dbus=71f0 w=1 m.ar=fff8 m.dr=71f0 rd.dr=e3e3 wd.dr=71f0
圖 上 述 同 步 BUS 的 波 形 圖 異 步 匯 流 排 (Asynchronous BUS) 異 步 匯 流 排 是 指 沒 有 共 同 Clock 訊 號 的 匯 流 排, 因 此 無 法 依 賴 Clock 進 行 同 步, 所 以 必 須 依 靠 主 控 就 緒 (Master Ready), 從 動 就 緒 (Slave Ready) 等 訊 號, 來 進 行 握 手 (Handshaking) 的 協 調 程 序 匯 流 排 仲 裁 (BUS arbitery) 當 有 很 多 個 主 控 裝 置 都 有 可 能 請 求 使 用 BUS 的 時 候, 就 必 須 要 加 入 一 個 仲 裁 機 制, 通 常 是 由 一 個 仲 裁 者 (arbiter) 進 行 仲 裁 循 序 與 平 行 輸 出 入 (Serial vs. Parallel) 常 見 的 輸 出 入 協 定 MCU0 的 輸 出 入 -- 輪 詢 篇 在 本 文 中, 我 們 利 用 輪 詢 的 方 式 實 作 了 MCU0 的 鍵 盤 與 文 字 輸 出 的 函 數
MCU0 的 中 斷 位 元 在 電 腦 中, 進 行 輸 出 入 所 採 用 的 方 式, 在 指 令 上 可 分 為 專 用 輸 出 入 指 令 與 記 憶 體 映 射 輸 出 入 兩 種, 在 本 文 中 我 們 將 用 記 憶 體 映 射 輸 出 入 進 行 輸 出 入 另 外 進 行 輸 出 入 的 驅 動 方 式, 可 分 為 輪 詢 與 中 斷 兩 種 方 式, 本 文 將 採 用 輪 詢 的 方 式 實 作 MCU0 的 輸 出 入 實 作 方 式 `define OP IR[15:12] // 運 算 碼 `define C IR[11:0] // 常 數 欄 位 `define SC8 $signed(ir[7:0]) // 常 數 欄 位 `define C4 IR[3:0] // 常 數 欄 位 `define Ra IR[7:4] // Ra `define Rb IR[3:0] // Rb `define A R[0] // 累 積 器 `define LR R[1] // 狀 態 暫 存 器 `define SW R[2] // 狀 態 暫 存 器 `define SP R[3] // 堆 疊 暫 存 器 `define PC R[4] // 程 式 計 數 器 `define N `SW[15] // 負 號 旗 標 `define Z `SW[14] // 零 旗 標 `define I `SW[3] // 是 否 中 斷 中 `define M m[`c] // 存 取 記 憶 體 module mcu(input clock, input interrupt, input[2:0] irq); parameter [3:0] LD=4'h0,ST=4'h1,ADD=4'h2,SUB=4'h3,MUL=4'h4,DIV=4'h5,AND =4'h6,OR=4'h7,XOR=4'h8,CMP=4'h9,JMP=4'hA,JEQ=4'hB, JLT=4'hC, JLE=4'hD, CA LL=4'hE, OP8=4'hF; parameter [3:0] LDI=4'h0, MOV=4'h2, PUSH=4'h3, POP=4'h4, SHL=4'h5, SHR= 4'h6, ADDI=4'h7, SUBI=4'h8, NEG=4'h9, SWI=4'hA, NSW=4'hD, RET=4'hE, IRET= 4'hF; reg [15:0] IR; // 指 令 暫 存 器 reg signed [15:0] R[0:4]; reg signed [15:0] pc0; reg signed [15:0] m [0:4095]; // 內 部 的 快 取 記 憶 體 integer i; initial // 初 始 化 begin
`PC = 0; // 將 PC 設 為 起 動 位 址 0 `SW = 0; $readmemh("mcu0io.hex", m); for (i=0; i < 32; i=i+1) begin $display("%x %x", i, m[i]); always @(posedge clock) begin // 在 clock 時 脈 的 正 邊 緣 時 觸 發 IR = m[`pc]; // 指 令 擷 取 階 段 :IR=m[PC], 2 個 Byte 的 記 憶 體 pc0= `PC; // 儲 存 舊 的 PC 值 在 pc0 中 `PC = `PC+1; // 擷 取 完 成,PC 前 進 到 下 一 個 指 令 位 址 case (`OP) // 解 碼 根 據 OP 執 行 動 作 LD: `A = `M; // LD C ST: `M = `A; // ST C ADD: `A = `A + `M; // ADD C SUB: `A = `A - `M; // SUB C MUL: `A = `A * `M; // MUL C DIV: `A = `A / `M; // DIV C AND: `A = `A & `M; // AND C OR : `A = `A `M; // OR C XOR: `A = `A ^ `M; // XOR C CMP: begin `N=(`A < `M); `Z=(`A==`M); // CMP C JMP: `PC = `C; // JSUB C JEQ: if (`Z) `PC=`C; // JEQ C JLT: if (`N) `PC=`C; // JLT C JLE: if (`N `Z) `PC=`C;// JLE C CALL:begin `LR = `PC; `PC = `C; // CALL C OP8: case (IR[11:8]) // OP8: 加 長 運 算 碼 LDI: R[`Ra] = `C4; // LDI C ADDI: R[`Ra] = R[`Ra] + `C4; // ADDI C SUBI: R[`Ra] = R[`Ra] - `C4; // ADDI C MOV: R[`Ra] = R[`Rb]; // MOV Ra, Rb PUSH: begin `SP=`SP-1; m[`sp] = R[`Ra]; // PUSH Ra POP: begin R[`Ra] = m[`sp]; `SP=`SP+1; // POP Ra SHL: R[`Ra] = R[`Ra] << `C4; // SHL C
SHR: R[`Ra] = R[`Ra] >> `C4; // SHR C SWI: $display("swi C8=%d A=%d", `SC8, `A); // SWI C NEG: R[`Ra] = ~R[`Ra]; // NEG Ra NSW: begin `N=~`N; `Z=~`Z; // NSW (negate N, Z ) RET: `PC = `LR; // RET IRET: begin `PC = `LR; `I = 0; // IRET default: $display("op8=%d, not defined!", IR[11:8]); case case // 印 出 PC, IR, SW, A 等 暫 存 器 值 以 供 觀 察 $display("%4dns PC=%x IR=%x, SW=%x, A=%d SP=%x LR=%x", $stime, pc0, I R, `SW, `A, `SP, `LR); if (!`I && interrupt) begin `I = 1; `LR = `PC; `PC = irq; module module keyboard; reg [7:0] ch[0:20]; reg [7:0] i; initial begin i=0; {ch[0],ch[1],ch[2],ch[3],ch[4],ch[5],ch[6],ch[7],ch[8],ch[9],ch[10],ch[ 11],ch[12],ch[13]} = "hello verilog!"; main.mcu0.m[16'h07f0] = 0; main.mcu0.m[16'h07f1] = 0; always #20 begin if (main.mcu0.m[16'h07f0] == 0) begin main.mcu0.m[16'h07f1] = {8'h0, ch[i]}; main.mcu0.m[16'h07f0] = 1; $display("key = %c", ch[i]);
i = i+1; module module screen; reg [7:0] ch; initial begin main.mcu0.m[16'h07f2] = 0; main.mcu0.m[16'h07f3] = 0; always #10 begin if (main.mcu0.m[16'h07f2] == 1) begin ch = main.mcu0.m[16'h07f3][7:0]; $display("screen %c", ch); main.mcu0.m[16'h07f2] = 0; module module main; reg clock; reg interrupt; reg [2:0] irq; // 測 試 程 式 開 始 // 時 脈 clock 變 數 mcu mcu0(clock, interrupt, irq); // 宣 告 cpu0mc 處 理 器 keyboard kb0(); screen sc0(); initial begin clock = 0; // 一 開 始 clock 設 定 為 0 interrupt = 0; irq = 2; always #10 clock=~clock; // 每 隔 10ns 反 相, 時 脈 週 期 為 20ns
initial #4000 $finish; // 停 止 測 試 module 輸 入 機 器 碼 與 組 合 語 言 07F0 // 00 WAITK: LD 0x7F0 ; wait keyboard 9010 // 01 CMP K0 B000 // 02 JEQ WAIT 07F1 // 03 LD 0x7F1 ; read key 1011 // 04 ST KEY 0010 // 05 LD K0 17F0 // 06 ST 0x7F0 ; release keyboard 07F2 // 07 WAITS: LD 0x7F2 ; wait screen 0010 // 08 CMP K0 B007 // 09 JEQ WAITS 0011 // 0A LD KEY ; print key 17F3 // 0B ST 0x7F3 F001 // 0C LDI 1 17F2 // 0D ST 0x7F2 ; eanble screen A000 // 0E JMP WAIT 0000 // 0F 0000 // 10 K0: WORD 0 0000 // 11 KEY: WORD 0 執 行 結 果 D:\Dropbox\Public\web\oc\code\mcu0>iverilog -o mcu0io mcu0io.v D:\Dropbox\Public\web\oc\code\mcu0>vvp mcu0io WARNING: mcu0io.v:29: $readmemh(mcu0io.hex): Not enough words in the file for th e requested range [0:4095]. 00000000 07f0 00000001 9010 00000002 b000 00000003 07f1 00000004 1011
00000005 0010 00000006 17f0 00000007 07f2 00000008 0010 00000009 b007 0000000a 0011 0000000b 17f3 0000000c f001 0000000d 17f2 0000000e a000 0000000f 0000 00000010 0000 00000011 0000 00000012 xxxx 00000013 xxxx 00000014 xxxx 00000015 xxxx 00000016 xxxx 00000017 xxxx 00000018 xxxx 00000019 xxxx 0000001a xxxx 0000001b xxxx 0000001c xxxx 0000001d xxxx 0000001e xxxx 0000001f xxxx 10ns PC=0000 IR=07f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = h 30ns PC=0001 IR=9010, SW=4000, A= 0 SP=xxxx LR=xxxx 50ns PC=0002 IR=b000, SW=4000, A= 0 SP=xxxx LR=xxxx 70ns PC=0000 IR=07f0, SW=4000, A= 1 SP=xxxx LR=xxxx 90ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 110ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 130ns PC=0003 IR=07f1, SW=0000, A= 104 SP=xxxx LR=xxxx 150ns PC=0004 IR=1011, SW=0000, A= 104 SP=xxxx LR=xxxx 170ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx
190ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = e 210ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 230ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 250ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 270ns PC=000a IR=0011, SW=0000, A= 104 SP=xxxx LR=xxxx 290ns PC=000b IR=17f3, SW=0000, A= 104 SP=xxxx LR=xxxx 310ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 330ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen h 350ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 370ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 390ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 410ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 430ns PC=0003 IR=07f1, SW=0000, A= 101 SP=xxxx LR=xxxx 450ns PC=0004 IR=1011, SW=0000, A= 101 SP=xxxx LR=xxxx 470ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 490ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = l 510ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 530ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 550ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 570ns PC=000a IR=0011, SW=0000, A= 101 SP=xxxx LR=xxxx 590ns PC=000b IR=17f3, SW=0000, A= 101 SP=xxxx LR=xxxx 610ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 630ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen e 650ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 670ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 690ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 710ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 730ns PC=0003 IR=07f1, SW=0000, A= 108 SP=xxxx LR=xxxx 750ns PC=0004 IR=1011, SW=0000, A= 108 SP=xxxx LR=xxxx 770ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 790ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = l 810ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx
830ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 850ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 870ns PC=000a IR=0011, SW=0000, A= 108 SP=xxxx LR=xxxx 890ns PC=000b IR=17f3, SW=0000, A= 108 SP=xxxx LR=xxxx 910ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 930ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen l 950ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 970ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 990ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 1010ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 1030ns PC=0003 IR=07f1, SW=0000, A= 108 SP=xxxx LR=xxxx 1050ns PC=0004 IR=1011, SW=0000, A= 108 SP=xxxx LR=xxxx 1070ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 1090ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = o 1110ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 1130ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 1150ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 1170ns PC=000a IR=0011, SW=0000, A= 108 SP=xxxx LR=xxxx 1190ns PC=000b IR=17f3, SW=0000, A= 108 SP=xxxx LR=xxxx 1210ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 1230ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen l 1250ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 1270ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 1290ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 1310ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 1330ns PC=0003 IR=07f1, SW=0000, A= 111 SP=xxxx LR=xxxx 1350ns PC=0004 IR=1011, SW=0000, A= 111 SP=xxxx LR=xxxx 1370ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 1390ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = 1410ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 1430ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 1450ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 1470ns PC=000a IR=0011, SW=0000, A= 111 SP=xxxx LR=xxxx
1490ns PC=000b IR=17f3, SW=0000, A= 111 SP=xxxx LR=xxxx 1510ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 1530ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen o 1550ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 1570ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 1590ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 1610ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 1630ns PC=0003 IR=07f1, SW=0000, A= 32 SP=xxxx LR=xxxx 1650ns PC=0004 IR=1011, SW=0000, A= 32 SP=xxxx LR=xxxx 1670ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 1690ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = v 1710ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 1730ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 1750ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 1770ns PC=000a IR=0011, SW=0000, A= 32 SP=xxxx LR=xxxx 1790ns PC=000b IR=17f3, SW=0000, A= 32 SP=xxxx LR=xxxx 1810ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 1830ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen 1850ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 1870ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 1890ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 1910ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 1930ns PC=0003 IR=07f1, SW=0000, A= 118 SP=xxxx LR=xxxx 1950ns PC=0004 IR=1011, SW=0000, A= 118 SP=xxxx LR=xxxx 1970ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 1990ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = e 2010ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 2030ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 2050ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 2070ns PC=000a IR=0011, SW=0000, A= 118 SP=xxxx LR=xxxx 2090ns PC=000b IR=17f3, SW=0000, A= 118 SP=xxxx LR=xxxx 2110ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 2130ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx
screen v 2150ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 2170ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 2190ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 2210ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 2230ns PC=0003 IR=07f1, SW=0000, A= 101 SP=xxxx LR=xxxx 2250ns PC=0004 IR=1011, SW=0000, A= 101 SP=xxxx LR=xxxx 2270ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 2290ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = r 2310ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 2330ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 2350ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 2370ns PC=000a IR=0011, SW=0000, A= 101 SP=xxxx LR=xxxx 2390ns PC=000b IR=17f3, SW=0000, A= 101 SP=xxxx LR=xxxx 2410ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 2430ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen e 2450ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 2470ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 2490ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 2510ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 2530ns PC=0003 IR=07f1, SW=0000, A= 114 SP=xxxx LR=xxxx 2550ns PC=0004 IR=1011, SW=0000, A= 114 SP=xxxx LR=xxxx 2570ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 2590ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = i 2610ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 2630ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 2650ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 2670ns PC=000a IR=0011, SW=0000, A= 114 SP=xxxx LR=xxxx 2690ns PC=000b IR=17f3, SW=0000, A= 114 SP=xxxx LR=xxxx 2710ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 2730ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen r 2750ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 2770ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx
2790ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 2810ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 2830ns PC=0003 IR=07f1, SW=0000, A= 105 SP=xxxx LR=xxxx 2850ns PC=0004 IR=1011, SW=0000, A= 105 SP=xxxx LR=xxxx 2870ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 2890ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = l 2910ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 2930ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 2950ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 2970ns PC=000a IR=0011, SW=0000, A= 105 SP=xxxx LR=xxxx 2990ns PC=000b IR=17f3, SW=0000, A= 105 SP=xxxx LR=xxxx 3010ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 3030ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen i 3050ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 3070ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 3090ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 3110ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 3130ns PC=0003 IR=07f1, SW=0000, A= 108 SP=xxxx LR=xxxx 3150ns PC=0004 IR=1011, SW=0000, A= 108 SP=xxxx LR=xxxx 3170ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 3190ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = o 3210ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 3230ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 3250ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 3270ns PC=000a IR=0011, SW=0000, A= 108 SP=xxxx LR=xxxx 3290ns PC=000b IR=17f3, SW=0000, A= 108 SP=xxxx LR=xxxx 3310ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 3330ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen l 3350ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 3370ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 3390ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 3410ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 3430ns PC=0003 IR=07f1, SW=0000, A= 111 SP=xxxx LR=xxxx
3450ns PC=0004 IR=1011, SW=0000, A= 111 SP=xxxx LR=xxxx 3470ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 3490ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key = g 3510ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 3530ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 3550ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 3570ns PC=000a IR=0011, SW=0000, A= 111 SP=xxxx LR=xxxx 3590ns PC=000b IR=17f3, SW=0000, A= 111 SP=xxxx LR=xxxx 3610ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 3630ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen o 3650ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 3670ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 3690ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 3710ns PC=0002 IR=b000, SW=0000, A= 1 SP=xxxx LR=xxxx 3730ns PC=0003 IR=07f1, SW=0000, A= 103 SP=xxxx LR=xxxx 3750ns PC=0004 IR=1011, SW=0000, A= 103 SP=xxxx LR=xxxx 3770ns PC=0005 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 3790ns PC=0006 IR=17f0, SW=0000, A= 0 SP=xxxx LR=xxxx key =! 3810ns PC=0007 IR=07f2, SW=0000, A= 0 SP=xxxx LR=xxxx 3830ns PC=0008 IR=0010, SW=0000, A= 0 SP=xxxx LR=xxxx 3850ns PC=0009 IR=b007, SW=0000, A= 0 SP=xxxx LR=xxxx 3870ns PC=000a IR=0011, SW=0000, A= 103 SP=xxxx LR=xxxx 3890ns PC=000b IR=17f3, SW=0000, A= 103 SP=xxxx LR=xxxx 3910ns PC=000c IR=f001, SW=0000, A= 1 SP=xxxx LR=xxxx 3930ns PC=000d IR=17f2, SW=0000, A= 1 SP=xxxx LR=xxxx screen g 3950ns PC=000e IR=a000, SW=0000, A= 1 SP=xxxx LR=xxxx 3970ns PC=0000 IR=07f0, SW=0000, A= 1 SP=xxxx LR=xxxx 3990ns PC=0001 IR=9010, SW=0000, A= 1 SP=xxxx LR=xxxx 結 語 以 上 的 輸 出 入 方 式, 並 非 典 型 的 設 計, 而 是 屬 於 系 統 單 晶 片 (SOC) 的 設 計 方 式, 因 此 直 接 將 鍵 盤 與 螢 幕 的 輸 出 入 暫 存 器 直 接 內 建 在 MCU0 的 記 憶 體 之 內, 這 樣 的 設 計 會 比 將 輸 出 入 控 制 卡 與 CPU 分 開 的 方 式 更 容 易 一 些, 但 是 由 於 這 種 ASIC 的 量 產 費 用 昂 貴, 所 以 目 前 還 很 少 有 這 種 設 計 方 式
不 過 就 簡 單 性 而 言, 這 樣 的 設 計 確 實 非 常 簡 單, 因 此 符 合 開 放 電 腦 計 畫 的 Keep it Simple and Stupid (KISS) 原 則, 所 以 我 們 先 介 紹 這 樣 一 個 簡 易 的 輸 出 入 設 計 方 式, 以 便 讓 讀 者 能 從 最 簡 單 的 架 構 入 手
記 憶 系 統 (Storage) 除 了 記 憶 體 之 外, 電 腦 裏 還 有 隨 身 碟 記 憶 卡 硬 碟 光 碟 磁 帶 等 儲 存 裝 置, 這 些 裝 置 構 成 一 整 個 儲 存 體 系, 只 有 充 分 考 慮 這 些 儲 存 體 的 速 度 問 題, 才 能 讓 電 腦 得 到 最 好 的 效 能 事 實 上 記 憶 體 也 有 很 多 種, 而 且 速 度 不 一, 像 是 靜 態 記 憶 體 SRAM 動 態 記 憶 體 DRAM 唯 讀 記 憶 體 ROM 等 等, 而 且 靜 態 記 憶 體 的 速 度 又 可 以 分 為 很 多 種 等 級, 因 此 整 個 儲 存 體 會 形 成 一 個 多 層 次 不 同 速 度 的 記 憶 體 階 層 高 階 處 理 器 與 微 處 理 器 之 間 最 大 的 差 別, 是 高 階 處 理 器 會 利 用 各 種 記 憶 單 元 的 速 度 差 異, 有 效 的 安 排 並 平 衡 既 快 又 大 的 這 種 速 度 與 大 小 的 考 量 因 此 高 階 處 理 器 通 常 有 快 取 (cache) 記 憶 體 管 理 單 元 (Memory Management Unit, MMU) 等 機 制, 以 便 能 讓 電 腦 能 夠 容 量 又 大 速 度 又 快 要 能 夠 讓 高 階 處 理 器 充 分 利 用 這 種 記 憶 體 階 層 特 性, 首 先 讓 我 們 來 看 看 一 個 經 典 的 三 階 層 情 況, 那 就 是 (cache/memory/disk), 如 下 圖 所 示 記 憶 體 階 層 (Memory Hierarchy) 圖 常 見 的 三 層 式 記 憶 體 階 層 在 上 圖 中,cache 是 直 接 封 裝 在 CPU 內 部 的 靜 態 記 憶 體, 其 運 作 速 度 與 CPU 的 內 部 電 路 一 樣 快, 因 此 可 以 在 1 個 Clock Cycle 之 內 完 成 存 取 而 身 為 主 記 憶 體 的 DRAM, 速 度 比 靜 態 記 憶 體 慢 上 數 十 倍, 因 此 必 須 耗 費 幾 十 個 Cycle 才 能 完 成 存 取 最 下 層 的 硬 碟 (hard disk) 速 度 更 慢, 由 於 依 賴 讀 寫 頭 與 硬 盤 轉 動 的 機 械 性 動 作, 因 此 又 比 DRAM 慢 上 數 百 倍 ( 雖 然 轉 到 了 之 後 讀 取 還 算 快 速, 但 是 仍 然 相 對 緩 慢, 而 且 每 次 必 須 讀 一 大 塊, 否 則 轉 了 好 久 才 讀 1 個 byte 將 會 慢 如 蝸 牛 ) 當 然 有 些 電 腦 包 含 更 多 種 類 的 記 憶 單 元, 這 些 記 憶 裝 置 的 速 度 與 容 量 不 一, 以 下 是 一 個 更 多 層 次 的 記 憶 階 層 範 例
圖 更 多 層 次 的 記 憶 體 階 層 為 了 要 讓 電 腦 能 夠 又 快 又 大, 高 階 處 理 器 通 常 採 用 了 快 取 (cache) 與 記 憶 體 管 理 單 元 (MMU) 等 兩 個 技 術, 其 中 的 cache 位 於 CPU 內 部, 用 來 儲 存 常 用 的 指 令 與 資 料, 而 MMU 則 是 利 用 分 段 或 分 頁 等 機 制, 讓 記 憶 體 管 理 更 有 效 率, 甚 至 可 以 用 虛 擬 記 憶 體 技 術 把 硬 碟 拿 來 當 備 援 記 憶 體 使 用, 讓 容 量 可 以 進 一 步 提 升 當 然 要 使 用 這 些 快 取 與 暫 存 技 術, 都 必 須 付 出 相 對 應 的 代 價, 那 就 是 讓 你 的 CPU 設 計 更 加 複 雜, 而 且 有 時 也 會 快 不 起 來 ( 套 句 俗 話 說, 出 來 混 的, 總 有 一 天 是 要 還 的,CPU 的 設 計 也 是 如 此 ) 快 取 記 憶 體 (Cache) 傳 統 的 處 理 器 通 常 採 用 單 一 匯 流 排 馮 紐 曼 架 構 設 計, 這 種 架 構 非 常 簡 單 且 易 懂, 如 下 圖 所 示 : 圖 單 一 匯 流 排 的 馮 紐 曼 架 構 但 是 以 當 今 的 技 術, 上 圖 中 的 記 憶 體 通 常 採 用 DRAM 為 主 這 是 因 為 快 速 的 靜 態 記 憶 體 (SRAM) 仍 然 相 當 昂 貴, 而 速 度 慢 上 數 十 倍 的 動 態 記 憶 體 (DRAM) 則 相 對 便
宜, 因 此 電 腦 的 主 記 憶 體 通 常 仍 然 採 用 DRAM 為 主 如 果 要 讓 CPU 的 執 行 速 度 更 快, 就 不 能 讓 CPU 遷 就 於 DRAM 的 速 度 跟 著 變 慢, 此 時 我 們 可 以 在 CPU 內 部 加 入 一 個 快 取 記 憶 體 (cache), 讓 位 於 DRAM 中 常 用 到 的 指 令 與 資 料 放 入 快 取 當 中, 如 下 圖 所 示 : 圖 單 一 匯 流 排 的 馮 紐 曼 架 構 當 CPU 想 要 執 行 一 個 指 令 時, 如 果 該 指 令 已 經 在 cache 當 中, 就 不 需 要 讀 取 DRAM, 因 此 指 令 擷 取 階 段 就 可 以 快 上 數 十 倍 當 然 如 果 該 指 令 需 要 存 取 資 料, 而 該 資 料 已 經 在 cache 當 中 了, 那 麼 CPU 就 不 需 要 從 DRAM 中 讀 取 資 料, 因 此 資 料 讀 取 階 段 就 可 以 快 上 數 十 倍 當 需 要 寫 入 資 料 時, 如 果 暫 時 先 寫 到 cache 當 中, 而 不 是 直 接 寫 回 DRAM, 那 麼 資 料 寫 入 的 動 作 就 可 以 快 上 數 十 倍 當 然 上 述 的 美 好 情 況 不 會 永 遠 都 成 立, 假 如 想 存 取 的 指 令 或 資 料 不 存 在 cache 當 中, 那 麼 就 必 須 從 DRAM 讀 取 資 料, 這 時 CPU 的 速 度 就 會 被 打 回 原 形, 變 成 與 DRAM 的 速 度 一 樣 而 且 當 資 料 被 放 入 cache 之 後, 由 於 DRAM 當 中 還 有 一 份 同 樣 的 資 料, 要 又 如 何 才 能 快 速 的 查 出 資 料 是 否 在 cache 當 中, 就 是 一 個 不 容 易 的 問 題 了 這 個 問 題 的 解 決, 必 須 依 靠 某 種 記 憶 體 位 址 的 標 籤 (tag), 透 過 這 種 標 籤 我 們 可 以 查 出 某 個 位 址 的 內 容 到 底 是 在 cache 當 中 的 那 一 格 而 標 籤 的 設 計 方 法, 大 致 可 分 為 直 接 映 射 (direct-mapping), 關 連 映 射 (associate-mapping) 與 組 關 連 映 射 (set associative-mapping) 等 幾 種 方 法 其 中 最 常 採 用 的 是 後 者 對 這 些 cache 作 法 的 細 節 有 興 趣 的 讀 者, 可 以 參 考 下 列 文 件 維 基 百 科 :CPU 快 取
高 階 處 理 器 (Processor) 在 前 一 章 當 中, 我 們 說 明 了 微 處 理 器 的 設 計 方 式, 在 本 章 中 我 們 將 說 明 高 階 處 理 器 的 設 計 方 式 與 微 處 理 器 比 起 來, 高 階 處 理 器 除 了 指 令 寬 度 較 大 之 外, 能 定 址 的 記 憶 體 空 間 通 常 也 很 大, 而 且 會 內 建 記 憶 體 管 理 單 元 (Memory Management Unit, MMU) 與 多 層 快 取 機 制 (cache) 等 等, 這 些 都 是 為 了 充 分 發 揮 處 理 器 的 效 能 的 設 計, 這 也 正 是 為 何 稱 為 高 階 處 理 器 的 原 因 在 本 章 當 中, 我 們 將 透 過 cpu0 這 個 架 構, 給 出 一 個 處 理 器 的 簡 易 範 例 之 後, 就 開 始 針 對 現 今 的 高 階 處 理 器 之 結 構 進 行 探 討, 以 便 讓 讀 者 在 能 清 楚 的 理 解 現 代 高 階 處 理 器 的 設 計 原 理 與 特 性 哈 佛 架 構 (Harvard Architecture) 在 單 一 匯 流 排 馮 紐 曼 架 構 之 下, 由 於 指 令 與 資 料 放 在 同 一 個 記 憶 體 當 中, 而 且 只 有 一 套 匯 流 排, 因 此 指 令 與 資 料 勢 必 無 法 同 時 存 取 如 果 我 們 希 望 同 時 進 行 指 令 與 資 料 的 存 取, 那 麼 就 可 以 將 指 令 與 資 料 分 別 放 在 兩 塊 不 同 的 記 憶 體 當 中, 並 且 各 用 一 套 內 部 匯 流 排 連 接 到 CPU, 這 樣 就 有 可 能 同 時 存 取 指 令 和 資 料, 也 就 有 可 能 將 指 令 擷 取 (instruction fetch) 與 資 料 存 取 (data access) 階 段 重 疊 執 行 了 圖 指 令 與 資 料 分 開 為 兩 套 記 憶 體 的 哈 佛 架 構 採 用 指 令 與 資 料 分 開 的 兩 套 記 憶 體 模 式, 如 果 都 是 DRAM 的 話, 那 麼 該 處 理 器 仍 然 會 式 非 常 緩 慢 的, 因 為 受 限 於 DRAM 的 速 度 限 制, 因 此 這 種 方 式 根 本 就 是 花 了 兩 倍 力 氣 卻 得 不 到 太 多 好 處, 可 以 說 是 一 種 很 爛 的 設 計 但 是 假 如 我 們 不 是 將 主 記 憶 體 分 為 兩 套, 而 是 將 cache 分 為 指 令 快 取 (i-cache) 與 資 料 快 取 (dcache) 的 變 種 哈 佛 架 構 話, 那 麼 速 度 就 真 的 是 會 變 快 了
圖 採 用 兩 套 快 取 的 單 一 匯 流 排 哈 佛 架 構 不 過 其 實 還 可 以 變 得 更 快, 只 要 我 們 能 夠 用 流 水 線 的 模 式, 就 能 讓 CPU 快 上 五 倍, 這 必 須 讓 指 令 的 每 個 階 段 都 能 重 疊 起 來 才 行 ( 包 含 指 令 擷 取 與 資 料 存 取 階 段 的 重 疊, 這 也 正 是 為 何 要 討 論 哈 佛 架 構 的 原 因 ) 流 水 線 架 構 (Pipeline) Pipeline 是 一 種 讓 指 令 分 成 幾 個 步 驟, 然 後 兩 個 指 令 的 不 同 步 驟 可 以 瀑 布 式 重 疊 的 方 法 舉 例 而 言 假 如 我 們 將 一 個 指 令 的 執 行 分 為 五 個 步 驟 如 下 : 1. 擷 取 F (Fatch) 2. 解 碼 D (Decode) 3. 計 算 C (Compute) 4. 存 取 M (Memory Access) 5. 寫 回 W (Write Back) 那 麼 在 理 想 的 情 況 下, 我 們 就 能 夠 讓 這 些 指 令 充 分 的 重 疊 執 行, 如 下 圖 所 示
圖 理 想 的 Pipeline 執 行 情 況 如 此 只 要 讓 每 個 步 驟 所 需 的 時 間 幾 乎 一 樣, 然 後 將 Clock 調 快 五 倍, 就 能 讓 速 度 整 整 加 快 五 倍 這 就 是 所 謂 的 Pipeline 流 水 線 架 構 的 原 理 然 而 流 水 線 架 構 的 處 理 器 設 計 比 較 困 難, 因 為 上 圖 的 理 想 情 況 畢 竟 只 是 理 想, 現 實 生 活 中 有 很 多 因 素 會 干 擾 流 水 線 的 運 行, 造 成 流 水 線 某 個 階 段 受 到 阻 礙 無 法 完 成, 必 須 暫 停 等 後 某 個 步 驟 完 成, 這 種 暫 停 會 形 成 流 水 線 當 中 的 氣 泡 (bubble), 這 種 情 況 統 稱 為 指 令 圍 障 (instruction hazard) Hazard 有 好 幾 種, 像 是 資 料 圍 障 (data hazard) 快 取 失 敗 (cache miss) 分 支 延 遲 (branch penalty) 等 等 流 水 線 架 構 的 設 計, 通 常 必 須 用 暫 存 器 圍 牆 (Register wall) 將 每 個 步 驟 之 間 相 互 隔 開, 以 避 免 線 路 之 間 的 干 擾, 如 下 所 示 :
圖 以 暫 存 器 圍 牆 分 隔 流 水 線 架 構 的 每 個 階 段 Pipeline 處 理 器 最 困 難 的 地 方, 就 是 要 盡 可 能 的 減 少 這 些 hazard 然 而 有 些 hazard 是 可 以 用 硬 體 方 式 消 除 的, 但 是 很 多 hazard 的 消 除 卻 是 需 要 編 譯 器 的 配 合 才 能 夠 做 得 到 透 過 編 譯 器 的 協 助 減 少 hazard 的 方 法, 包 括 指 令 重 排 插 入 NOP 指 令 分 支 預 測 等 等 想 要 進 一 步 瞭 解 流 水 線 架 構 細 節 的 讀 者, 可 以 參 考 下 列 文 件 維 基 百 科 : 流 水 線 維 基 百 科 : 指 令 管 線 化 維 基 百 科 : 分 支 預 測 器 CPU0 迷 你 版 - CPU0m 在 本 章 中, 我 們 將 透 過 設 計 一 顆 只 支 援 4 個 指 令 的 超 微 小 處 理 器 CPU0m (CPU0-Mini) 開 始, 來 解 說 處 理 器 的 設 計 方 式 只 有 4 個 指 令 的 處 理 器 - CPU0m 在 此 我 們 將 用 最 簡 單 的 方 式, 在 完 全 不 考 慮 成 本 與 實 用 性 的 情 況 之 下, 設 計 一 個 將 記 憶 體 嵌 入 處 理 器 內 部 的 CPU, 也 就 是 整 個 記 憶 體 都 用 Verilog 內 嵌 在 CPU 理 面 我 們 從 CPU0 的 指 令 集 當 中, 挑 出 了 以 下 的 四 個 指 令, 以 便 寫 出 一 個 可 以 計 算 1+2+...+n+... 的 組 合 語 言 程
式 ( 喔! 不 應 該 說 是 機 器 語 言 程 式 ), 然 後 用 Verilog 實 作 一 個 可 以 執 行 這 些 指 令 的 CPU0m 處 理 器 格 式 指 令 OP 說 明 語 法 語 意 L LD 00 載 入 word LD Ra, [Rb+Cx] Ra=[Rb+Cx] L ST 01 儲 存 word ST Ra, [Rb+Cx] Ra=[Rb+Cx] A ADD 13 加 法 ADD Ra, Rb, Rc Ra=Rb+Rc J JMP 26 跳 躍 ( 無 條 件 ) JMP Cx PC=PC+Cx 然 後, 我 們 就 可 以 用 這 幾 個 指 令 寫 出 以 下 的 程 式 : 位 址 機 器 碼 標 記 組 合 語 言 對 照 的 C 語 言 0000 001F0018 LD R1, K1 R1 = K1 0004 002F0010 LD R2, K0 R2 = K0 0008 003F0014 LD R3, SUM R3 = SUM 000C 13221000 LOOP: ADD R2, R2, R1 R2 = R2 + R1 0010 13332000 ADD R3, R3, R2 R3 = R3 + R2 0014 26FFFFF4 JMP LOOP goto LOOP 0018 00000000 K0: WORD 0 int K0=0 001C 00000001 K1: WORD 1 int K1=1 0020 00000000 SUM: WORD 0 int SUM=0 這 個 程 式 的 行 為 模 式, 是 會 讓 暫 存 器 R3 ( 對 應 到 SUM) 從 0, 1, 1+2, 1+2+3,... 一 路 向 上 跑, 而 且 是 永 無 止 境 的 無 窮 迴 圈 因 此 我 們 會 看 到 R3 的 內 容 會 是 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55..., 的 情 況 透 過 CPU0m, 讀 者 將 可 以 開 始 瞭 解 一 顆 CPU 的 結 構 與 設 計 方 式, 並 且 更 清 楚 的 理 解 CPU 的 控 制 單 元 之 功 能 指 令 提 取 解 碼 與 執 行 CPU0 在 執 行 一 個 指 令 時, 必 須 經 過 提 取 解 碼 與 執 行 等 三 大 階 段, 以 下 是 這 三 個 階 段 的 詳 細 步 驟 階 段 (a): 提 取 階 段 動 作 1 提 取 指 令 :IR = m[pc] 動 作 2 更 新 計 數 器 :PC = PC + 4 階 段 (b): 解 碼 階 段
動 作 3 解 碼 : 將 IR 分 解 為 ( 運 算 代 碼 op, 暫 存 器 代 號 ra, rb, r c, 常 數 c5, c16, c24 等 欄 位 ) 階 段 (c): 執 行 階 段 動 作 4 執 行 : 根 據 運 算 代 碼 op, 執 行 對 應 的 動 作 執 行 階 段 的 動 作 根 據 指 令 的 類 型 而 有 所 不 同 舉 例 而 言, 假 如 執 行 的 指 令 是 ADD R1, R2, R1, 那 麼 執 行 階 段 所 進 行 的 動 作 如 下 : 格 式 指 令 OP 說 明 語 法 執 行 動 作 L LD 00 載 入 word LD Ra, [Rb+Cx] R[ra] = m[rb+c16] (4byte) L ST 01 儲 存 word ST Ra, [Rb+Cx] m[rb+c16] = R[ra] (4byte) A ADD 13 加 法 ADD Ra, Rb, Rc R[ra] = R[rb]+R[rc] J JMP 26 跳 躍 ( 無 條 件 ) JMP Cx PC = PC+c24 CPU0m 模 組 以 下 就 是 我 們 所 設 計 的 CPU0m 模 組, 以 及 測 試 的 主 程 式, 我 們 在 程 式 中 寫 了 詳 細 的 說 明, 請 讀 者 對 照 閱 讀 檔 案 :CPU0m `define PC R[15] // 程 式 計 數 器 PC 其 實 是 R[15] 的 別 名 module CPU(input clock); // CPU0-Mini 的 快 取 版 :cpu0m 模 組 parameter [7:0] LD = 8'h00, ST=8'h01, ADD=8'h13, JMP=8'h26; // 支 援 4 個 指 令 reg signed [31:0] R [0:15]; // 宣 告 暫 存 器 R[0..15] 等 16 個 32 位 元 暫 存 器 reg signed [31:0] IR; // 指 令 暫 存 器 IR reg [7:0] m [0:128]; // 內 部 的 快 取 記 憶 體 reg [7:0] op; // 變 數 : 運 算 代 碼 op reg [3:0] ra, rb, rc; // 變 數 : 暫 存 器 代 號 ra, rb, rc reg signed [11:0] cx12; // 變 數 :12 位 元 常 數 cx12 reg signed [15:0] cx16; // 變 數 :16 位 元 常 數 cx16 reg signed [23:0] cx24; // 變 數 :24 位 元 常 數 cx24 reg signed [31:0] addr; // 變 數 : 暫 存 記 憶 體 位 址 initial // 初 始 化
1 2 begin `PC = 0; // 將 PC 設 為 起 動 位 址 0 R[0] = 0; // 將 R[0] 暫 存 器 強 制 設 定 為 0 {m[0],m[1],m[2],m[3]} = 32'h001F0018; // 0000 LD R1, K1 {m[4],m[5],m[6],m[7]} = 32'h002F0010; // 0004 LD R2, K0 {m[8],m[9],m[10],m[11]} = 32'h003F0014; // 0008 LD R3, SUM {m[12],m[13],m[14],m[15]}= 32'h13221000; // 000C LOOP: ADD R2, R2, R {m[16],m[17],m[18],m[19]}= 32'h13332000; // 0010 ADD R3, R3, R {m[20],m[21],m[22],m[23]}= 32'h26FFFFF4; // 0014 JMP LOOP {m[24],m[25],m[26],m[27]}= 32'h00000000; // 0018 K0: WORD 0 {m[28],m[29],m[30],m[31]}= 32'h00000001; // 001C K1: WORD 1 {m[32],m[33],m[34],m[35]}= 32'h00000000; // 0020 SUM: WORD 0 always @(posedge clock) begin // 在 clock 時 脈 的 正 邊 緣 時 觸 發 IR = {m[`pc], m[`pc+1], m[`pc+2], m[`pc+3]}; // 指 令 擷 取 階 段 :IR=m [PC], 4 個 Byte 的 記 憶 體 `PC = `PC+4; // 擷 取 完 成,PC 前 進 到 下 一 個 指 令 位 址 {op,ra,rb,rc,cx12} = IR; // 解 碼 階 段 : 將 IR 解 為 {op, ra, rb, rc, cx12} cx24 = IR[23:0]; // 解 出 IR[ 23:0] 放 入 cx24 cx16 = IR[15:0]; // 解 出 IR[ 15:0] 放 入 cx16 addr = R[rb]+cx16; // 記 憶 體 存 取 位 址 = PC+cx16 case (op) // 根 據 OP 執 行 對 應 的 動 作 LD: begin // 載 入 指 令 : R[ra] = m[addr] R[ra] = {m[addr], m[addr+1], m[addr+2], m[addr+3]}; $write("%4dns %8x : LD %x,%x,%-4x", $stime, `PC, ra, rb, cx16) ; ST: begin // 儲 存 指 令 : m[addr] = R[ra] {m[addr], m[addr+1], m[addr+2], m[addr+3]} = R[ra];
$write("%4dns %8x : ST %x,%x,%-4x", $stime, `PC, ra, rb, cx16) ; ADD: begin // 加 法 指 令 : R[ra] = R[rb]+R[rc] R[ra] = R[rb]+R[rc]; $write("%4dns %8x : ADD %x,%x,%-4x", $stime, `PC, ra, rb, rc); JMP:begin // 跳 躍 指 令 : PC = PC + cx24 `PC = `PC + cx24; // 跳 躍 目 標 位 址 =PC+cx $write("%4dns %8x : JMP %-8x", $stime, `PC, cx24); case $display(" R[%2d]=%4d", ra, R[ra]); // 顯 示 目 標 暫 存 器 的 值 module module main; reg clock; // 測 試 程 式 開 始 // 時 脈 clock 變 數 cpu cpu0m(clock); // 宣 告 cpu0m 處 理 器 initial clock = 0; // 一 開 始 clock 設 定 為 0 always #10 clock=~clock; // 每 隔 10 奈 秒 將 clock 反 相, 產 生 週 期 為 20 奈 秒 的 時 脈 initial #640 $finish; // 在 640 奈 秒 的 時 候 停 止 測 試 ( 因 為 這 時 的 R[1 ] 恰 好 是 1+2+...+10=55 的 結 果 ) module 測 試 結 果 上 述 程 式 使 用 icarus 測 試 與 執 行 的 結 果 如 下 所 示 D:\Dropbox\Public\web\oc\code>iverilog -o cpu0m cpu0m.v D:\Dropbox\Public\web\oc\code>vvp cpu0m 10ns 00000004 : LD 1,f,0018 R[ 1]= 1 30ns 00000008 : LD 2,f,0010 R[ 2]= 0 50ns 0000000c : LD 3,f,0014 R[ 3]= 0
70ns 00000010 : ADD 2,2,1 R[ 2]= 1 90ns 00000014 : ADD 3,3,2 R[ 3]= 1 110ns 0000000c : JMP fffff4 R[15]= 12 130ns 00000010 : ADD 2,2,1 R[ 2]= 2 150ns 00000014 : ADD 3,3,2 R[ 3]= 3 170ns 0000000c : JMP fffff4 R[15]= 12 190ns 00000010 : ADD 2,2,1 R[ 2]= 3 210ns 00000014 : ADD 3,3,2 R[ 3]= 6 230ns 0000000c : JMP fffff4 R[15]= 12 250ns 00000010 : ADD 2,2,1 R[ 2]= 4 270ns 00000014 : ADD 3,3,2 R[ 3]= 10 290ns 0000000c : JMP fffff4 R[15]= 12 310ns 00000010 : ADD 2,2,1 R[ 2]= 5 330ns 00000014 : ADD 3,3,2 R[ 3]= 15 350ns 0000000c : JMP fffff4 R[15]= 12 370ns 00000010 : ADD 2,2,1 R[ 2]= 6 390ns 00000014 : ADD 3,3,2 R[ 3]= 21 410ns 0000000c : JMP fffff4 R[15]= 12 430ns 00000010 : ADD 2,2,1 R[ 2]= 7 450ns 00000014 : ADD 3,3,2 R[ 3]= 28 470ns 0000000c : JMP fffff4 R[15]= 12 490ns 00000010 : ADD 2,2,1 R[ 2]= 8 510ns 00000014 : ADD 3,3,2 R[ 3]= 36 530ns 0000000c : JMP fffff4 R[15]= 12 550ns 00000010 : ADD 2,2,1 R[ 2]= 9 570ns 00000014 : ADD 3,3,2 R[ 3]= 45 590ns 0000000c : JMP fffff4 R[15]= 12 610ns 00000010 : ADD 2,2,1 R[ 2]= 10 630ns 00000014 : ADD 3,3,2 R[ 3]= 55 從 上 述 輸 出 訊 息 當 中, 您 可 以 看 到 程 式 的 執 行 是 正 確 的, 其 中 R[2] 從 0, 1, 2,... 一 路 上 數, 而 R[3] 則 從 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55 一 路 累 加 上 來, 完 成 了 我 們 想 要 的 程 式 功 能 結 語 其 實 CPU0m 這 樣 的 設 計 應 該 還 不 能 稱 之 為 快 取, 而 是 在 程 式 不 大 的 情 況 之 下, 將 SRAM 直 接 包 入 在 CPU 當 中 的 一 種 作 法, 這 種 作 法 的 好 處 是 記 憶 體 存 取 速 度 很 快, 但 相 對 的 記 憶 體 成 本 也 很 貴, 因 為 這 些 記 憶 體 是 直 接 用 靜 態 記 憶 體 的 方 式 內 建 在 CPU 當 中 的 這 種 方 式 比 較 像 SOC 系 統 單 晶 片 的 做 法, 在 程 式 很 小 的 情 況 之 下, 直 接 將 記 憶 體 包 入 SOC 當 中, 會 得 到
比 較 高 速 的 電 路, 可 惜 的 是 這 種 做 法 不 像 目 前 的 電 腦 架 構 一 樣, 是 採 用 外 掛 DRAM 的 方 式, 可 以 大 幅 降 低 記 憶 體 的 成 本, 增 大 記 憶 體 的 容 量 就 是 了 CPU0 完 整 版 -- cpu0s CPU0 是 一 個 簡 易 的 32 位 元 單 匯 流 排 處 理 器, 其 架 構 如 下 圖 所 示, 包 含 R0..R15, IR, MAR, MDR 等 暫 存 器, 其 中 IR 是 指 令 暫 存 器, R0 是 一 個 永 遠 為 常 數 0 的 唯 讀 暫 存 器,R15 是 程 式 計 數 器 (Program Counter : PC),R14 是 連 結 暫 存 器 (Link Register : LR), R13 是 堆 疊 指 標 暫 存 器 (Stack Pointer : SP), 而 R12 是 狀 態 暫 存 器 (Status Word : SW) 圖 CPU0 的 架 構 圖 CPU0 的 指 令 集 CPU0 包 含 載 入 儲 存 運 算 指 令 跳 躍 指 令 堆 疊 指 令 等 四 大 類 指 令, 以 下 表 格 是 CPU0 的 指 令 編 碼 表, 記 載 了 CPU0 的 指 令 集 與 每 個 指 令 的 編 碼 格 式 指 令 OP 說 明 語 法 語 意 L LD 00 載 入 word LD Ra, [Rb+Cx] Ra=[Rb+Cx] L ST 01 儲 存 word ST Ra, [Rb+Cx] Ra=[Rb+Cx] L LDB 02 載 入 byte LDB Ra, [Rb+Cx] Ra=(byte)[Rb+Cx] L STB 03 儲 存 byte STB Ra, [Rb+Cx] Ra=(byte)[Rb+Cx] A LDR 04 LD 的 暫 存 器 版 LDR Ra, [Rb+Rc] Ra=[Rb+Rc] A STR 05 ST 的 暫 存 器 版 STR Ra, [Rb+Rc] Ra=[Rb+Rc] A LBR 06 LDB 的 暫 存 器 版 LBR Ra, [Rb+Rc] Ra=(byte)[Rb+Rc]
A SBR 07 STB 的 暫 存 器 版 SBR Ra, [Rb+Rc] Ra=(byte)[Rb+Rc] L LDI 08 載 入 常 數 LDI Ra, Cx Ra=Cx A CMP 10 比 較 CMP Ra, Rb SW=Ra >=< Rb A MOV 12 移 動 MOV Ra, Rb Ra=Rb A ADD 13 加 法 ADD Ra, Rb, Rc Ra=Rb+Rc A SUB 14 減 法 SUB Ra, Rb, Rc Ra=Rb-Rc A MUL 15 乘 法 MUL Ra, Rb, Rc Ra=Rb*Rc A DIV 16 除 法 DIV Ra, Rb, Rc Ra=Rb/Rc A AND 18 邏 輯 AND AND Ra, Rb, Rc Ra=Rb and Rc A OR 19 邏 輯 OR OR Ra, Rb, Rc Ra=Rb or Rc A XOR 1A 邏 輯 XOR XOR Ra, Rb, Rc Ra=Rb xor Rc A ADDI 1B 常 數 加 法 ADDI Ra, Rb, Cx Ra=Rb + Cx A ROL 1C 向 左 旋 轉 ROL Ra, Rb, Cx Ra=Rb rol Cx A ROR 1D 向 右 旋 轉 ROR Ra, Rb, Cx Ra=Rb ror Cx A SHL 1E 向 左 移 位 SHL Ra, Rb, Cx Ra=Rb << Cx A SHR 1F 向 右 移 位 SHR Ra, Rb, Cx Ra=Rb >> Cx J JEQ 20 跳 躍 ( 相 等 ) JEQ Cx if SW(=) PC=PC+Cx J JNE 21 跳 躍 ( 不 相 等 ) JNE Cx if SW(!=) PC=PC+Cx J JLT 22 跳 躍 (<) JLT Cx if SW(<) PC=PC+Cx J JGT 23 跳 躍 (>) JGT Cx if SW(>) PC=PC+Cx J JLE 24 跳 躍 (<=) JLE Cx if SW(<=) PC=PC+Cx J JGE 25 跳 躍 (>=) JGE Cx if SW(>=) PC=PC+Cx J JMP 26 跳 躍 ( 無 條 件 ) JMP Cx PC=PC+Cx J SWI 2A 軟 體 中 斷 SWI Cx LR=PC; PC=Cx; INT=1 J CALL 2B 跳 到 副 程 式 CALL Cx LR=PC; PC=PC+Cx J RET 2C 返 回 RET PC=LR
J IRET 2D 中 斷 返 回 IRET PC=LR; INT=0 A PUSH 30 推 入 word PUSH Ra SP-=4; [SP]=Ra; A POP 31 彈 出 word POP Ra Ra=[SP]; SP+=4; A PUSHB 32 推 入 byte PUSHB Ra SP--; [SP]=Ra; (byte) A POPB 33 彈 出 byte POPB Ra Ra=[SP]; SP++; (byte) CPU0 指 令 格 式 CPU0 所 有 指 令 長 度 均 為 32 位 元, 這 些 指 令 也 可 根 據 編 碼 方 式 分 成 三 種 不 同 的 格 式, 分 別 是 A 型 J 型 與 L 型 大 部 分 的 運 算 指 令 屬 於 A (Arithmatic) 型, 而 載 入 儲 存 指 令 通 常 屬 於 L (Load & Store) 型, 跳 躍 指 令 則 通 常 屬 於 J (Jump) 型, 這 三 種 型 態 的 指 令 格 式 如 下 圖 所 示 圖 CPU0 的 指 令 格 式 狀 態 暫 存 器 R12 狀 態 暫 存 器 (Status Word : SW) 是 用 來 儲 存 CPU 的 狀 態 值, 這 些 狀 態 是 許 多 旗 標 的 組 合 例 如, 零 旗 標 (Zero, 簡 寫 為 Z) 代 表 比 較 的 結 果 為 0, 負 旗 標 (Negative, 簡 寫 為 N) 代 表 比 較 的 結 果 為 負 值, 另 外 常 見 的 旗 標 還 有 進 位 旗 標 (Carry, 簡 寫 為 C), 溢 位 旗 標 (Overflow, 簡 寫 為 V) 等 等 下 圖 顯 示 了 CPU0 的 狀 態 暫 存 器 格 式, 最 前 面 的 四 個 位 元 N Z C V 所 代 表 的, 正 是 上 述 的 幾 個 旗 標 值 圖 CPU0 中 狀 態 暫 存 器 SW 的 結 構 條 件 旗 標 的 N Z 旗 標 值 可 以 用 來 代 表 比 較 結 果 是 大 於 (>) 等 於 (=) 還 是 小 於 (<), 當 執 行 CMP Ra, Rb 動 作 後, 會 有 下 列 三 種 可 能 的 情 形 1. 若 Ra > Rb, 則 N=0, Z=0 2. 若 Ra < Rb, 則 N=1, Z=0
3. 若 Ra = Rb, 則 N=0, Z=1 如 此, 用 來 進 行 條 件 跳 躍 的 JGT JGE JLT JLE JEQ JNE 指 令, 就 可 以 根 據 SW 暫 存 器 當 中 的 N Z 等 旗 標 決 定 是 否 進 行 跳 躍 SW 中 還 包 含 中 斷 控 制 旗 標 I (Interrupt) 與 T (Trap), 用 以 控 制 中 斷 的 啟 動 與 禁 止 等 行 為, 假 如 將 I 旗 標 設 定 為 0, 則 CPU0 將 禁 止 所 有 種 類 的 中 斷, 也 就 是 對 任 何 中 斷 都 不 會 起 反 應 但 如 果 只 是 將 T 旗 標 設 定 為 0, 則 只 會 禁 止 軟 體 中 斷 指 令 SWI (Software Interrupt), 不 會 禁 止 由 硬 體 觸 發 的 中 斷 SW 中 還 儲 存 有 處 理 器 模 式 的 欄 位,M=0 時 為 使 用 者 模 式 (user mode) 與 M=1 時 為 特 權 模 式 (super mode) 等, 這 在 作 業 系 統 的 設 計 上 經 常 被 用 來 製 作 安 全 保 護 功 能 在 使 用 者 模 式 當 中, 任 何 設 定 狀 態 暫 存 器 R12 的 動 作 都 會 被 視 為 是 非 法 的, 這 是 為 了 進 行 保 護 功 能 的 緣 故 但 是 在 特 權 模 式 中, 允 許 進 行 任 何 動 作, 包 含 設 定 中 斷 旗 標 與 處 理 器 模 式 等 位 元, 通 常 作 業 系 統 會 使 用 特 權 模 式 (M=1), 而 一 般 程 式 只 能 處 於 使 用 者 模 式 (M=0) CPU0 處 理 器 的 Verilog 實 作 -- CPU0sc.v 檔 案 :cpu0sc.v `define PC R[15] // 程 式 計 數 器 `define LR R[14] // 連 結 暫 存 器 `define SP R[13] // 堆 疊 暫 存 器 `define SW R[12] // 狀 態 暫 存 器 // 狀 態 暫 存 器 旗 標 位 元 `define N `SW[31] // 負 號 旗 標 `define Z `SW[30] // 零 旗 標 `define C `SW[29] // 進 位 旗 標 `define V `SW[28] // 溢 位 旗 標 `define I `SW[7] // 硬 體 中 斷 許 可 `define T `SW[6] // 軟 體 中 斷 許 可 `define M `SW[0] // 模 式 位 元 module cpu0c(input clock); // CPU0-Mini 的 快 取 版 :cpu0mc 模 組 parameter [7:0] LD=8'h00,ST=8'h01,LDB=8'h02,STB=8'h03,LDR=8'h04,STR=8'h 05, LBR=8'h06,SBR=8'h07,ADDI=8'h08,CMP=8'h10,MOV=8'h12,ADD=8'h13,SUB=8'h1 4, MUL=8'h15,DIV=8'h16,AND=8'h18,OR=8'h19,XOR=8'h1A,ROL=8'h1C,ROR=8'h1D, SHL=8'h1E,SHR=8'h1F,JEQ=8'h20,JNE=8'h21,JLT=8'h22,JGT=8'h23,JLE=8'h24, JGE=8'h25,JMP=8'h26,SWI=8'h2A,CALL=8'h2B,RET=8'h2C,IRET=8'h2D,
PUSH=8'h30,POP=8'h31,PUSHB=8'h32,POPB=8'h33; reg signed [31:0] R [0:15]; // 宣 告 暫 存 器 R[0..15] 等 16 個 32 位 元 暫 存 器 reg signed [31:0] IR; // 指 令 暫 存 器 IR reg [7:0] m [0:256]; // 內 部 的 快 取 記 憶 體 reg [7:0] op; // 變 數 : 運 算 代 碼 op reg [3:0] ra, rb, rc; // 變 數 : 暫 存 器 代 號 ra, rb, rc reg [4:0] c5; // 變 數 :5 位 元 常 數 c5 reg signed [11:0] c12; // 變 數 :12 位 元 常 數 c12 reg signed [15:0] c16; // 變 數 :16 位 元 常 數 c16 reg signed [23:0] c24; // 變 數 :24 位 元 常 數 c24 reg signed [31:0] sp, jaddr, laddr, raddr; reg signed [31:0] temp; reg signed [31:0] pc; integer i; initial // 初 始 化 begin `PC = 0; // 將 PC 設 為 起 動 位 址 0 `SW = 0; R[0] = 0; // 將 R[0] 暫 存 器 強 制 設 定 為 0 $readmemh("cpu0s.hex", m); for (i=0; i < 255; i=i+4) begin $display("%8x: %8x", i, {m[i], m[i+1], m[i+2], m[i+3]}); always @(posedge clock) begin // 在 clock 時 脈 的 正 邊 緣 時 觸 發 pc = `PC; IR = {m[`pc], m[`pc+1], m[`pc+2], m[`pc+3]}; // 指 令 擷 取 階 段 :IR=m [PC], 4 個 Byte 的 記 憶 體 `PC = `PC+4; // 擷 取 完 成,PC 前 進 到 下 一 個 指 令 位 址 {op,ra,rb,rc,c12} = IR; // 解 碼 階 段 : 將 IR 解 為 {op, ra, rb, rc, c12} c5 = IR[4:0]; c24 = IR[23:0];
c16 = IR[15:0]; jaddr = `PC+c16; laddr = R[rb]+c16; raddr = R[rb]+R[rc]; case (op) // 根 據 OP 執 行 對 應 的 動 作 LD: begin // 載 入 指 令 : R[ra] = m[addr] R[ra] = {m[laddr], m[laddr+1], m[laddr+2], m[laddr+3]}; $display("%4dns %8x : LD R%-d R%-d 0x%x ; R%-2d=0x%8x=%-d", $stime, pc, ra, rb, c16, ra, R[ra], R[ra]); ST: begin // 儲 存 指 令 : m[addr] = R[ra] {m[laddr], m[laddr+1], m[laddr+2], m[laddr+3]} = R[ra]; $display("%4dns %8x : ST R%-d R%-d 0x%x ; R%-2d=0x%8x=%-d", $stime, pc, ra, rb, c16, ra, R[ra], R[ra]); LDB:begin // 載 入 byte; LDB Ra, [Rb+ Cx]; Ra<=(byte)[Rb+ Cx] R[ra] = { 24'b0, m[laddr] }; $display("%4dns %8x : LDB R%-d R%-d 0x%x ; R%-2d=0x%8x=%-d", $stime, pc, ra, rb, c16, ra, R[ra], R[ra]); STB:begin // 儲 存 byte; STB Ra, [Rb+ Cx]; Ra=>(byte)[Rb+ Cx] m[laddr] = R[ra][7:0]; $display("%4dns %8x : STB R%-d R%-d 0x%x ; R%-2d=0x%8x=%-d", $stime, pc, ra, rb, c16, ra, R[ra], R[ra]); LDR:begin // LD 的 Rc 版 ; LDR Ra, [Rb+Rc]; Ra<=[Rb+ Rc] R[ra] = {m[raddr], m[raddr+1], m[raddr+2], m[raddr+3]}; $display("%4dns %8x : LDR R%-d R%-d R%-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]); STR:begin // ST 的 Rc 版 ; STR Ra, [Rb+Rc]; Ra=>[Rb+ Rc] {m[raddr], m[raddr+1], m[raddr+2], m[raddr+3]} = R[ra]; $display("%4dns %8x : STR R%-d R%-d R%-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]);
LBR:begin // LDB 的 Rc 版 ; LBR Ra, [Rb+Rc]; Ra<=(byte)[Rb+ Rc] R[ra] = { 24'b0, m[raddr] }; $display("%4dns %8x : LBR R%-d R%-d R%-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]); SBR:begin // STB 的 Rc 版 ; SBR Ra, [Rb+Rc]; Ra=>(byte)[Rb+ Rc] m[raddr] = R[ra][7:0]; $display("%4dns %8x : SBR R%-d R%-d R%-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]); MOV:begin // 移 動 ; MOV Ra, Rb; Ra<=Rb R[ra] = R[rb]; $display("%4dns %8x : MOV R%-d R%-d ; R%-2d=0x%8x=%-d", $stime, pc, ra, rb, ra, R[ra], R[ra]); CMP:begin // 比 較 ; CMP Ra, Rb; SW=(Ra >=< Rb) temp = R[ra]-R[rb]; `N=(temp<0);`Z=(temp==0); $display("%4dns %8x : CMP R%-d R%-d ; SW=0x%x", $stime, pc, ra, rb, `SW); ADDI:begin // R[a] = Rb+c16; // 立 即 值 加 法 ; LDI Ra, Rb+Cx; Ra <=Rb + Cx R[ra] = R[rb]+c16; $display("%4dns %8x : ADDI R%-d R%-d %-d ; R%-2d=0x%8x=%-d", $ stime, pc, ra, rb, c16, ra, R[ra], R[ra]); ADD: begin // 加 法 指 令 : R[ra] = R[rb]+R[rc] R[ra] = R[rb]+R[rc]; $display("%4dns %8x : ADD R%-d R%-d R%-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]); SUB:begin // 減 法 ; SUB Ra, Rb, Rc; Ra<=Rb-Rc R[ra] = R[rb]-R[rc]; $display("%4dns %8x : SUB R%-d R%-d R%-d ; R%-2d=0x%8x=%-d
", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]); MUL:begin // 乘 法 ; MUL Ra, Rb, Rc; Ra<=Rb*Rc R[ra] = R[rb]*R[rc]; $display("%4dns %8x : MUL R%-d R%-d R%-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]); DIV:begin // 除 法 ; DIV Ra, Rb, Rc; Ra<=Rb/Rc R[ra] = R[rb]/R[rc]; $display("%4dns %8x : DIV R%-d R%-d R%-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]); AND:begin // 位 元 AND; AND Ra, Rb, Rc; Ra<=Rb and Rc R[ra] = R[rb]&R[rc]; $display("%4dns %8x : AND R%-d R%-d R%-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]); OR:begin // 位 元 OR; OR Ra, Rb, Rc; Ra<=Rb or Rc R[ra] = R[rb] R[rc]; $display("%4dns %8x : OR R%-d R%-d R%-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]); XOR:begin // 位 元 XOR; XOR Ra, Rb, Rc; Ra<=Rb xor Rc R[ra] = R[rb]^R[rc]; $display("%4dns %8x : XOR R%-d R%-d R%-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, rc, ra, R[ra], R[ra]); SHL:begin // 向 左 移 位 ; SHL Ra, Rb, Cx; Ra<=Rb << Cx R[ra] = R[rb]<<c5; $display("%4dns %8x : SHL R%-d R%-d %-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, c5, ra, R[ra], R[ra]); SHR:begin // 向 右 移 位 ; SHR Ra, Rb, Cx; Ra<=Rb >> Cx R[ra] = R[rb]>>c5; $display("%4dns %8x : SHR R%-d R%-d %-d ; R%-2d=0x%8x=%-d ", $stime, pc, ra, rb, c5, ra, R[ra], R[ra]);
JMP:begin // 跳 躍 指 令 : PC = PC + cx24 `PC = `PC + c24; $display("%4dns %8x : JMP 0x%x ; PC=0x%x", $stime, pc, c24, `PC); JEQ:begin // 跳 躍 ( 相 等 ); JEQ Cx; if SW(=) PC PC +Cx if (`Z) `PC=`PC+c24; $display("%4dns %8x : JEQ 0x%08x ; PC=0x%x", $stime, pc, c24, `PC); JNE:begin // 跳 躍 ( 不 相 等 ); JNE Cx; if SW(!=) PC PC+Cx if (!`Z) `PC=`PC+c24; $display("%4dns %8x : JNE 0x%08x ; PC=0x%x", $stime, pc, c24, `PC); JLT:begin // 跳 躍 ( < ); JLT Cx; if SW(<) PC PC+Cx if (`N) `PC=`PC+c24; $display("%4dns %8x : JLT 0x%08x ; PC=0x%x", $stime, pc, c24, `PC); JGT:begin // 跳 躍 ( > ); JGT Cx; if SW(>) PC PC+Cx if (!`N&&!`Z) `PC=`PC+c24; $display("%4dns %8x : JGT 0x%08x ; PC=0x%x", $stime, pc, c24, `PC); JLE:begin // 跳 躍 ( <= ); JLE Cx; if SW(<=) PC PC+ Cx if (`N `Z) `PC=`PC+c24; $display("%4dns %8x : JLE 0x%08x ; PC=0x%x", $stime, pc, c24, `PC); JGE:begin // 跳 躍 ( >= ); JGE Cx; if SW(>=) PC PC+ Cx if (!`N `Z) `PC=`PC+c24; $display("%4dns %8x : JGE 0x%08x ; PC=0x%x", $stime, pc, c24, `PC);
SWI:begin // 軟 中 斷 ; SWI Cx; LR <= PC; PC <= Cx; INT <=1 `LR=`PC;`PC= c24; `I = 1'b1; $display("%4dns %8x : SWI 0x%08x ; PC=0x%x", $stime, pc, c24, `PC); CALL:begin // 跳 到 副 程 式 ; CALL Cx; LR<=PC; PC<=PC+Cx `LR=`PC;`PC=`PC + c24; $display("%4dns %8x : CALL 0x%08x ; PC=0x%x", $stime, pc, c24, `PC); RET:begin // 返 回 ; RET; PC <= LR `PC=`LR; $display("%4dns %8x : RET ; PC=0x%x", $stime, pc, `PC); if (`PC<0) $finish; IRET:begin // 中 斷 返 回 ; IRET; PC <= LR; INT<=0 `PC=`LR;`I = 1'b0; $display("%4dns %8x : IRET ; PC=0x%x", $stime, pc, `PC); PUSH:begin // 推 入 word; PUSH Ra; SP-=4;[SP]<=Ra; sp = `SP-4; `SP = sp; {m[sp], m[sp+1], m[sp+2], m[sp+3]} = R[ra ]; $display("%4dns %8x : PUSH R%-d ; R%-2d=0x%8x, SP=0 x%x", $stime, pc, ra, ra, R[ra], `SP); POP:begin // 彈 出 word; POP Ra; Ra=[SP];SP+=4; sp = `SP; R[ra]={m[sp], m[sp+1], m[sp+2], m[sp+3]}; `SP = sp+4; $display("%4dns %8x : POP R%-d ; R%-2d=0x%8x, SP=0 x%x", $stime, pc, ra, ra, R[ra], `SP); PUSHB:begin // 推 入 byte; PUSHB Ra; SP--;[SP]<=Ra;(byte) sp = `SP-1; `SP = sp; m[sp] = R[ra];
$display("%4dns %8x : PUSHB R%-d ; R[%-d]=0x%8x, SP= 0x%x", $stime, pc, ra, ra, R[ra], `SP); POPB:begin // 彈 出 byte; POPB Ra; Ra<=[SP];SP++;(byte) sp = `SP+1; `SP = sp; R[ra]=m[sp]; $display("%4dns %8x : POPB R%-d ; R[%-d]=0x%8x, SP= 0x%x", $stime, pc, ra, ra, R[ra], `SP); case module module main; reg clock; // 測 試 程 式 開 始 // 時 脈 clock 變 數 cpu0c cpu(clock); // 宣 告 cpu0mc 處 理 器 initial clock = 0; // 一 開 始 clock 設 定 為 0 always #10 clock=~clock; // 每 隔 10 奈 秒 將 clock 反 相, 產 生 週 期 為 20 奈 秒 的 時 脈 initial #2000 $finish; // 在 640 奈 秒 的 時 候 停 止 測 試 ( 因 為 這 時 的 R[1 ] 恰 好 是 1+2+...+10=55 的 結 果 ) module 程 式 碼 解 析 與 執 行 在 上 一 章 的 CPU0mc.v 當 中, 我 們 直 接 使 用 下 列 程 式 將 機 器 碼 塞 入 到 記 憶 體 當 中, 但 是 這 樣 做 顯 然 彈 性 不 太 夠 1 2 {m[0],m[1],m[2],m[3]} = 32'h001F0018; // 0000 LD R1, K1 {m[4],m[5],m[6],m[7]} = 32'h002F0010; // 0004 LD R2, K0 {m[8],m[9],m[10],m[11]} = 32'h003F0014; // 0008 LD R3, SUM {m[12],m[13],m[14],m[15]}= 32'h13221000; // 000C LOOP: ADD R2, R2, R {m[16],m[17],m[18],m[19]}= 32'h13332000; // 0010 ADD R3, R3, R {m[20],m[21],m[22],m[23]}= 32'h26FFFFF4; // 0014 JMP LOOP {m[24],m[25],m[26],m[27]}= 32'h00000000; // 0018 K0: WORD 0 {m[28],m[29],m[30],m[31]}= 32'h00000001; // 001C K1: WORD 1
{m[32],m[33],m[34],m[35]}= 32'h00000000; // 0020 SUM: WORD 0 因 此, 在 本 章 的 CPU0sc.v 這 個 程 式 中, 我 們 採 用 讀 取 外 部 檔 案 的 方 式, 將 機 器 碼 寫 在 cpu0s.hex 這 個 檔 案 中, 然 後 再 用 下 列 指 令 將 該 16 進 位 的 機 器 碼 檔 案 讀 入 $readmemh("cpu0s.hex", m); 其 中 的 readmemh 是 一 個 可 以 讀 取 16 進 位 的 文 字 檔 的 函 數, 上 述 指 令 會 將 cpu0s.hex 這 個 檔 案 內 的 16 進 位 字 串 讀 入 到 記 憶 體 變 數 m 當 中 以 下 是 cpu0s.hex 的 完 整 內 容 輸 入 檔 :cpu0s.hex 00 DF 00 B6 // 0 LD R13, StackEnd 08 40 00 04 // 4 ADDI R4, 4 08 50 00 08 // 8 ADDI R5, 8 05 4D 50 00 // c STR R4, [R13+R5] 04 6D 50 00 // 10 LDR R6, [R13+R5] 07 5D 40 00 // 14 SBR R5, [R13+R4] 06 6D 40 00 // 18 LBR R6, [R13+R4] 08 E0 FF FF // 1C ADDI R14,R0,-1 30 E0 00 00 // 20 PUSH R14 13 85 40 00 // 24 ADD R8, R5, R4 14 85 40 00 // 28 SUB R8, R5, R4 15 85 40 00 // 2c MUL R8, R5, R4 16 85 40 00 // 30 DIV R8, R5, R4 18 85 40 00 // 34 AND R8, R5, R4 19 85 40 00 // 38 OR R8, R5, R4 1A 85 40 00 // 3c XOR R8, R5, R4 1E 85 00 03 // 40 SHL R8, R5, 3 1F 85 00 02 // 44 SHR R8, R5, 2 10 45 00 00 // 48 CMP R4, R5 20 00 00 18 // 4c JEQ L1 23 00 00 14 // 50 JGT L1 25 00 00 10 // 54 JGE L1 22 00 00 0C // 58 JLT L1 24 00 00 08 // 5c JLE L1 21 00 00 04 // 60 JNE L1 26 00 00 00 // 64 JMP L1
08 10 00 0A // 68 L1: ADDI R1, R0, 10 2B 00 00 08 // 6c CALL SUM 31 E0 00 00 // 70 POP R14 2C 00 00 00 // 74 RET 30 E0 00 00 // 78 SUM: PUSH R14 12 30 00 00 // 7c MOV R3, R0 // R3 = i = 0 02 4F 00 24 // 80 LDB R4, k1 // R4 = 1 08 20 00 00 // 84 ADDI R2, 0 // SUM = R2 = 0 13 22 30 00 // 88 LOOP: ADD R2, R2, R3 // SUM = SUM + i 13 33 40 00 // 8c ADD R3, R3, R4 // i = i + 1 10 31 00 00 // 90 CMP R3, R1 // if (i < R1) 24 FF FF F0 // 94 JLE LOOP // goto LOOP 01 2F 00 0D // 98 ST R2, s 03 3F 00 0D // 9c STB R3, i 31 E0 00 00 // a0 POP R14 2C 00 00 00 // a4 RET // return 01 // a8 k1: BYTE 1 // char K1=1 00 00 00 00 // a9 s: WORD 0 // int s 00 // ad i: BYTE 0 // char i=1 00 01 02 03 // ae Stack: BYTE 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 04 05 06 07 // b2 08 09 0A 0B // b6 00 00 00 BA // ba StackEnd: WORD StackEnd 01 02 03 04 // be Data: BYTE 0, 1, 2, 3, 4, 5, 6, 7, 8 05 06 07 08 // c2 上 述 程 式 的 內 容, 大 致 是 先 準 備 好 堆 疊, 然 後 就 開 始 測 試 ADDI, STR, LDR, ADD, SUB,... 等 指 令 接 著 在 呼 叫 CMP R4, R5 之 後 進 行 跳 躍 測 試 動 作, 由 於 R4=4, R5=8, 所 以 CMP 的 結 果 會 是 小 於, 因 此 在 後 面 的 JEQ, JGT, JGE 等 指 令 都 不 會 真 的 跳 躍, 直 到 執 行 JLT L1 時 就 會 真 的 跳 到 L1 去 接 著 用 ADDI R1, R0, 10 將 R1 設 為 10, 然 後 就 用 CALL SUM 這 個 指 令 呼 叫 SUM 這 個 副 程 式, 於 是 跳 到 位 於 0x78 的 SUM: PUSH R14 這 一 行, 並 開 始 執 行 副 程 式, 該 副 程 式 會 計 算 1+2+...+R1 的 結 果, 放 在 R2 當 中, 並 在 最 後 用 STB R2, s 這 個 指 令 存 入 變 數 s 當 中, 然 後 在 執 行 完 RET 指 令 後 返 回 上 一 層, 也 就 是 0x70 行 的 POP R14 指 令, 接 著 在 執 行 RET 指 令 時, 由 於 此 時 R14 為 -1, 因 此 Verilog 程 式 就 在 完 成 RET 指 令 時 發 現 PC 已 經 小 於 0 了, 因 此 執 行 $finish` 指 令 停 止 整 個 程 式 RET:begin // 返 回 ; RET; PC <= LR `PC=`LR; $display("%4dns %8x : RET ; PC=0x%x", $stime, pc, `PC);
if (`PC<0) $finish; 執 行 結 果 有 了 上 述 的 程 式 cpu0sc.v 與 輸 入 的 機 器 碼 cpu0s.hex 檔 案 之 後, 我 們 就 可 以 用 下 列 指 令 進 行 編 譯 與 執 行, 以 下 是 該 程 式 編 譯 與 執 行 的 結 果 D:\verilog>iverilog -o cpu0sc cpu0sc.v D:\verilog>vvp cpu0sc WARNING: cpu0sc.v:40: $readmemh(cpu0s.hex): Not enough words in the file for the requested range [0:256]. 00000000: 00df00b6 00000004: 08400004 00000008: 08500008 0000000c: 054d5000 00000010: 046d5000 00000014: 075d4000 00000018: 066d4000 0000001c: 08e0ffff 00000020: 30e00000 00000024: 13854000 00000028: 14854000 0000002c: 15854000 00000030: 16854000 00000034: 18854000 00000038: 19854000 0000003c: 1a854000 00000040: 1e850003 00000044: 1f850002 00000048: 10450000 0000004c: 20000018 00000050: 23000014 00000054: 25000010 00000058: 2200000c 0000005c: 24000008
00000060: 21000004 00000064: 26000000 00000068: 0810000a 0000006c: 2b000008 00000070: 31e00000 00000074: 2c000000 00000078: 30e00000 0000007c: 12300000 00000080: 024f0024 00000084: 08200000 00000088: 13223000 0000008c: 13334000 00000090: 10310000 00000094: 24fffff0 00000098: 012f000d 0000009c: 033f000d 000000a0: 31e00000 000000a4: 2c000000 000000a8: 01000000 000000ac: 00000001 000000b0: 02030405 000000b4: 06070809 000000b8: 0a0b0000 000000bc: 00ba0102 000000c0: 03040506 000000c4: 0708xxxx 000000c8: xxxxxxxx 000000cc: xxxxxxxx 000000d0: xxxxxxxx 000000d4: xxxxxxxx 000000d8: xxxxxxxx 000000dc: xxxxxxxx 000000e0: xxxxxxxx 000000e4: xxxxxxxx 000000e8: xxxxxxxx 000000ec: xxxxxxxx 000000f0: xxxxxxxx
000000f4: xxxxxxxx 000000f8: xxxxxxxx 000000fc: xxxxxxxx 10ns 00000000 : LD R13 R15 0x00b6 ; R13=0x000000ba=186 30ns 00000004 : ADDI R4 R0 4 ; R4 =0x00000004=4 50ns 00000008 : ADDI R5 R0 8 ; R5 =0x00000008=8 70ns 0000000c : STR R4 R13 R5 ; R4 =0x00000004=4 90ns 00000010 : LDR R6 R13 R5 ; R6 =0x00000004=4 110ns 00000014 : SBR R5 R13 R4 ; R5 =0x00000008=8 130ns 00000018 : LBR R6 R13 R4 ; R6 =0x00000008=8 150ns 0000001c : ADDI R14 R0-1 ; R14=0xffffffff=-1 170ns 00000020 : PUSH R14 ; R14=0xffffffff, SP=0x000000b6 190ns 00000024 : ADD R8 R5 R4 ; R8 =0x0000000c=12 210ns 00000028 : SUB R8 R5 R4 ; R8 =0x00000004=4 230ns 0000002c : MUL R8 R5 R4 ; R8 =0x00000020=32 250ns 00000030 : DIV R8 R5 R4 ; R8 =0x00000002=2 270ns 00000034 : AND R8 R5 R4 ; R8 =0x00000000=0 290ns 00000038 : OR R8 R5 R4 ; R8 =0x0000000c=12 310ns 0000003c : XOR R8 R5 R4 ; R8 =0x0000000c=12 330ns 00000040 : SHL R8 R5 3 ; R8 =0x00000040=64 350ns 00000044 : SHR R8 R5 2 ; R8 =0x00000002=2 370ns 00000048 : CMP R4 R5 ; SW=0x80000000 390ns 0000004c : JEQ 0x00000018 ; PC=0x00000050 410ns 00000050 : JGT 0x00000014 ; PC=0x00000054 430ns 00000054 : JGE 0x00000010 ; PC=0x00000058 450ns 00000058 : JLT 0x0000000c ; PC=0x00000068 470ns 00000068 : ADDI R1 R0 10 ; R1 =0x0000000a=10 490ns 0000006c : CALL 0x00000008 ; PC=0x00000078 510ns 00000078 : PUSH R14 ; R14=0x00000070, SP=0x000000b2 530ns 0000007c : MOV R3 R0 ; R3 =0x00000000=0 550ns 00000080 : LDB R4 R15 0x0024 ; R4 =0x00000001=1 570ns 00000084 : ADDI R2 R0 0 ; R2 =0x00000000=0 590ns 00000088 : ADD R2 R2 R3 ; R2 =0x00000000=0 610ns 0000008c : ADD R3 R3 R4 ; R3 =0x00000001=1 630ns 00000090 : CMP R3 R1 ; SW=0x80000000 650ns 00000094 : JLE 0x00fffff0 ; PC=0x00000088 670ns 00000088 : ADD R2 R2 R3 ; R2 =0x00000001=1
690ns 0000008c : ADD R3 R3 R4 ; R3 =0x00000002=2 710ns 00000090 : CMP R3 R1 ; SW=0x80000000 730ns 00000094 : JLE 0x00fffff0 ; PC=0x00000088 750ns 00000088 : ADD R2 R2 R3 ; R2 =0x00000003=3 770ns 0000008c : ADD R3 R3 R4 ; R3 =0x00000003=3 790ns 00000090 : CMP R3 R1 ; SW=0x80000000 810ns 00000094 : JLE 0x00fffff0 ; PC=0x00000088 830ns 00000088 : ADD R2 R2 R3 ; R2 =0x00000006=6 850ns 0000008c : ADD R3 R3 R4 ; R3 =0x00000004=4 870ns 00000090 : CMP R3 R1 ; SW=0x80000000 890ns 00000094 : JLE 0x00fffff0 ; PC=0x00000088 910ns 00000088 : ADD R2 R2 R3 ; R2 =0x0000000a=10 930ns 0000008c : ADD R3 R3 R4 ; R3 =0x00000005=5 950ns 00000090 : CMP R3 R1 ; SW=0x80000000 970ns 00000094 : JLE 0x00fffff0 ; PC=0x00000088 990ns 00000088 : ADD R2 R2 R3 ; R2 =0x0000000f=15 1010ns 0000008c : ADD R3 R3 R4 ; R3 =0x00000006=6 1030ns 00000090 : CMP R3 R1 ; SW=0x80000000 1050ns 00000094 : JLE 0x00fffff0 ; PC=0x00000088 1070ns 00000088 : ADD R2 R2 R3 ; R2 =0x00000015=21 1090ns 0000008c : ADD R3 R3 R4 ; R3 =0x00000007=7 1110ns 00000090 : CMP R3 R1 ; SW=0x80000000 1130ns 00000094 : JLE 0x00fffff0 ; PC=0x00000088 1150ns 00000088 : ADD R2 R2 R3 ; R2 =0x0000001c=28 1170ns 0000008c : ADD R3 R3 R4 ; R3 =0x00000008=8 1190ns 00000090 : CMP R3 R1 ; SW=0x80000000 1210ns 00000094 : JLE 0x00fffff0 ; PC=0x00000088 1230ns 00000088 : ADD R2 R2 R3 ; R2 =0x00000024=36 1250ns 0000008c : ADD R3 R3 R4 ; R3 =0x00000009=9 1270ns 00000090 : CMP R3 R1 ; SW=0x80000000 1290ns 00000094 : JLE 0x00fffff0 ; PC=0x00000088 1310ns 00000088 : ADD R2 R2 R3 ; R2 =0x0000002d=45 1330ns 0000008c : ADD R3 R3 R4 ; R3 =0x0000000a=10 1350ns 00000090 : CMP R3 R1 ; SW=0x40000000 1370ns 00000094 : JLE 0x00fffff0 ; PC=0x00000088 1390ns 00000088 : ADD R2 R2 R3 ; R2 =0x00000037=55 1410ns 0000008c : ADD R3 R3 R4 ; R3 =0x0000000b=11
1430ns 00000090 : CMP R3 R1 ; SW=0x00000000 1450ns 00000094 : JLE 0x00fffff0 ; PC=0x00000098 1470ns 00000098 : ST R2 R15 0x000d ; R2 =0x00000037=55 1490ns 0000009c : STB R3 R15 0x000d ; R3 =0x0000000b=11 1510ns 000000a0 : POP R14 ; R14=0x00000070, SP=0x000000b6 1530ns 000000a4 : RET ; PC=0x00000070 1550ns 00000070 : POP R14 ; R14=0xffffffff, SP=0x000000ba 1570ns 00000074 : RET ; PC=0xffffffff 結 語 從 本 章 的 內 容 中, 您 應 該 可 以 瞭 解 到 直 接 使 用 高 階 的 Verilog 流 程 式 語 法 來 設 計 處 理 器, 像 是 cpu0mc.v 與 cpu0sc.v, 都 是 相 當 容 易 的 事, 這 完 全 是 因 為 verilog 支 援 了 相 當 高 階 的 運 算, 像 是 +, -, *, /, &,, ^, <<, >> 等 運 算 的 原 故 不 過 在 上 述 程 式 當 中, 我 們 並 沒 有 支 援 硬 體 中 斷 的 功 能, 也 沒 有 實 作 軟 體 中 斷 SWI 的 函 數 呼 叫, 這 樣 CPU0sc.v 就 只 能 是 一 顆 單 工 (Single Task) 的 處 理 器, 而 無 法 支 援 多 工 (Multi Task) 的 功 能 了 在 下 一 章 當 中, 我 們 將 繼 續 擴 充 CPU0sc.v 這 個 程 式, 加 入 支 援 軟 硬 體 中 斷 的 功 能, 以 及 支 援 浮 點 運 算 的 功 能, 該 程 式 稱 為 CPU0ic.v (i 代 表 Interrupt, c 代 表 cache memory) 然 後 我 們 將 再 度 用 16 進 位 的 方 式, 寫 出 一 個 機 器 語 言 的 程 式, 可 以 同 時 執 行 兩 個 行 程 (Task), 並 且 每 隔 一 小 段 時 間 就 利 用 硬 體 中 斷 進 行 行 程 切 換, 以 示 範 如 何 設 計 一 個 可 以 支 援 多 工 能 力 的 CPU 處 理 器 參 考 文 獻 陳 鍾 誠 的 網 站 : 使 用 Verilog 設 計 CPU0 處 理 器 陳 鍾 誠 的 網 站 :CPU0-Mini 處 理 器 設 計
速 度 議 題 乘 法 與 除 法 for 迴 圈 的 實 作 方 法 最 簡 單 的 乘 法 器 是 移 位 乘 法 器, 這 種 乘 法 器 基 本 上 只 用 了 一 個 加 法 器 和 一 個 移 位 器 所 組 成, 電 路 上 而 言 相 當 簡 單, 但 缺 點 是 執 行 速 度 不 快, 以 下 是 一 個 32 位 元 的 移 位 乘 法 器 之 程 式 碼 參 考 :http://www.edaboard.com/thread235191.html 檔 案 :shift_mult.v module multiplier(output reg [63:0] z, input [31:0] x, y); reg [31:0] a; integer i; always @(x, y) begin a=x; z=0; // 初 始 化 為 0 for(i=0;i<31;i=i+1) begin if (y[i]) z = z + a; // 請 注 意, 這 是 block assignment =, 所 以 會 造 成 延 遲, 長 度 越 長 延 遲 越 久 a=a << 1; module module main; reg [31:0] a, b; wire [63:0] c; multiplier m(c, a, b); initial begin a = 17; b = 7; #10;
$display("a=%d b=%d c=%d", a, b, c); module 其 編 譯 執 行 結 果 如 下 : D:\Dropbox\Public\web\oc\code>iverilog -o mul32a mul32a.v D:\Dropbox\Public\web\oc\code>vvp mul32a a= 17 b= 7 c= 119 但 是 以 上 的 這 個 程 式, 採 用 了 for 迴 圈, 而 且 使 用 = 這 種 阻 隔 式 的 方 式, 應 該 會 相 當 耗 費 電 路 資 源, 筆 者 認 為 上 述 電 路 應 該 會 用 到 32 組 32 位 元 的 加 法 器, 這 樣 實 在 是 難 以 接 受 的 一 種 情 況, 所 以 我 們 接 下 來 將 採 用 只 有 一 組 加 法 器, 然 後 用 多 個 時 脈 的 方 式 來 設 計 乘 法 器 參 考 :http://www.edaboard.com/thread86772.html 1. for loop verilog synthesis It is synthesizable but it is always advised that for loops are not to be used in RTL coding. This is because it consumes lot of resources (like area etc.etc). However u can use it in behavioral coding becuse we do not synthesize behavioral codes. 2. verilog for loop syntax In verilog,synthesizable of for loop and while loop deps on which tools you are using. But it is better dont use it in RTL because it reflects replica of hardware. 32 個 時 脈 的 實 作 方 法 module multiplier(output reg [63:0] z, output ready, input [31:0] x, y, input start, clk); reg [5:0] bit; reg [31:0] a; wire ready =!bit; initial bit = 0; always @( posedge clk ) begin
if( ready && start ) begin bit = 32; z = 0; a = x; else begin bit = bit - 1; z = z << 1; if (y[bit]) z = z + a; stime, x, y, z, ready, bit); module module main; reg [31:0] a, b; wire [63:0] c; reg clk, start; wire ready; multiplier m(c, ready, a, b, start, clk); initial begin a = 17; b = 7; start = 1; clk = 0; #200; start = 0; #200; $finish; always #5 clk =!clk; // always @(posedge clk) stime, a, b, c, r eady);
module 執 行 結 果 D:\Dropbox\Public\web\oc\code>iverilog -o mul32b mul32b.v D:\Dropbox\Public\web\oc\code>vvp mul32b 5ns : x= 17 y= 7 z= 0 ready=0 b it=32 15ns : x= 17 y= 7 z= 0 ready=0 b it=31 25ns : x= 17 y= 7 z= 0 ready=0 b it=30 35ns : x= 17 y= 7 z= 0 ready=0 b it=29 45ns : x= 17 y= 7 z= 0 ready=0 b it=28 55ns : x= 17 y= 7 z= 0 ready=0 b it=27 65ns : x= 17 y= 7 z= 0 ready=0 b it=26 75ns : x= 17 y= 7 z= 0 ready=0 b it=25 85ns : x= 17 y= 7 z= 0 ready=0 b it=24 95ns : x= 17 y= 7 z= 0 ready=0 b it=23 105ns : x= 17 y= 7 z= 0 ready=0 b it=22 115ns : x= 17 y= 7 z= 0 ready=0 b it=21 125ns : x= 17 y= 7 z= 0 ready=0 b it=20 135ns : x= 17 y= 7 z= 0 ready=0 b it=19 145ns : x= 17 y= 7 z= 0 ready=0 b it=18 155ns : x= 17 y= 7 z= 0 ready=0 b
it=17 it=16 it=15 it=14 it=13 it=12 it=11 it=10 it= 9 it= 8 it= 7 it= 6 it= 5 it= 4 it= 3 it= 2 it= 1 it= 0 it=63 165ns : x= 17 y= 7 z= 0 ready=0 b 175ns : x= 17 y= 7 z= 0 ready=0 b 185ns : x= 17 y= 7 z= 0 ready=0 b 195ns : x= 17 y= 7 z= 0 ready=0 b 205ns : x= 17 y= 7 z= 0 ready=0 b 215ns : x= 17 y= 7 z= 0 ready=0 b 225ns : x= 17 y= 7 z= 0 ready=0 b 235ns : x= 17 y= 7 z= 0 ready=0 b 245ns : x= 17 y= 7 z= 0 ready=0 b 255ns : x= 17 y= 7 z= 0 ready=0 b 265ns : x= 17 y= 7 z= 0 ready=0 b 275ns : x= 17 y= 7 z= 0 ready=0 b 285ns : x= 17 y= 7 z= 0 ready=0 b 295ns : x= 17 y= 7 z= 0 ready=0 b 305ns : x= 17 y= 7 z= 17 ready=0 b 315ns : x= 17 y= 7 z= 51 ready=0 b 325ns : x= 17 y= 7 z= 119 ready=1 b 335ns : x= 17 y= 7 z= 238 ready=0 b
it=62 it=61 it=60 it=59 345ns : x= 17 y= 7 z= 476 ready=0 b 355ns : x= 17 y= 7 z= 952 ready=0 b 365ns : x= 17 y= 7 z= 1904 ready=0 b 375ns : x= 17 y= 7 z= 3808 ready=0 b 除 法 器 檔 案 : div32.v // 參 考 :http://www.ece.lsu.edu/ee3755/2002/l07.html // 參 考 :http://answers.google.com/answers/threadview/id/109219.html // a/b = q ; a%b = r; module divider(output reg [31:0] q, output [31:0] r, output ready, input [31:0] a,b, input start, clk); reg [63:0] ta, tb, diff; reg [5:0] bit; wire ready =!bit; initial bit = 0; assign r = ta[31:0]; always @( posedge clk ) if( ready && start ) begin bit = 32; q = 0; ta = {32'd0, a}; tb = {1'b0, b,31'd0}; else begin diff = ta - tb; q = q << 1; if(!diff[63] ) begin ta = diff; q[0] = 1'd1;
tb = tb >> 1; bit = bit - 1; module module main; reg clk, start; reg [31:0] a, b; wire [31:0] q, r; wire ready; divider div(q,r,ready,a,b,start,clk); initial begin clk = 0; a = 90; b = 13; start = 1; #200; start = 0; #200; $finish; always #5 clk =!clk; always @(posedge clk) stime, a, b, q, r, ready); module 執 行 結 果 D:\Dropbox\Public\web\oc\code>iverilog -o div32 div32.v D:\Dropbox\Public\web\oc\code>vvp div32 5ns : a= 90 b= 13 q= 0 r= 90 ready= 0 15ns : a= 90 b= 13 q= 0 r= 90 ready= 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 25ns : a= 90 b= 13 q= 0 r= 90 ready= 35ns : a= 90 b= 13 q= 0 r= 90 ready= 45ns : a= 90 b= 13 q= 0 r= 90 ready= 55ns : a= 90 b= 13 q= 0 r= 90 ready= 65ns : a= 90 b= 13 q= 0 r= 90 ready= 75ns : a= 90 b= 13 q= 0 r= 90 ready= 85ns : a= 90 b= 13 q= 0 r= 90 ready= 95ns : a= 90 b= 13 q= 0 r= 90 ready= 105ns : a= 90 b= 13 q= 0 r= 90 ready= 115ns : a= 90 b= 13 q= 0 r= 90 ready= 125ns : a= 90 b= 13 q= 0 r= 90 ready= 135ns : a= 90 b= 13 q= 0 r= 90 ready= 145ns : a= 90 b= 13 q= 0 r= 90 ready= 155ns : a= 90 b= 13 q= 0 r= 90 ready= 165ns : a= 90 b= 13 q= 0 r= 90 ready= 175ns : a= 90 b= 13 q= 0 r= 90 ready= 185ns : a= 90 b= 13 q= 0 r= 90 ready= 195ns : a= 90 b= 13 q= 0 r= 90 ready= 205ns : a= 90 b= 13 q= 0 r= 90 ready=
0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 215ns : a= 90 b= 13 q= 0 r= 90 ready= 225ns : a= 90 b= 13 q= 0 r= 90 ready= 235ns : a= 90 b= 13 q= 0 r= 90 ready= 245ns : a= 90 b= 13 q= 0 r= 90 ready= 255ns : a= 90 b= 13 q= 0 r= 90 ready= 265ns : a= 90 b= 13 q= 0 r= 90 ready= 275ns : a= 90 b= 13 q= 0 r= 90 ready= 285ns : a= 90 b= 13 q= 0 r= 90 ready= 295ns : a= 90 b= 13 q= 0 r= 90 ready= 305ns : a= 90 b= 13 q= 1 r= 38 ready= 315ns : a= 90 b= 13 q= 3 r= 12 ready= 325ns : a= 90 b= 13 q= 6 r= 12 ready= 335ns : a= 90 b= 13 q= 13 r= 6 ready= 345ns : a= 90 b= 13 q= 27 r= 3 ready= 355ns : a= 90 b= 13 q= 55 r= 2 ready= 365ns : a= 90 b= 13 q= 111 r= 2 ready= 375ns : a= 90 b= 13 q= 223 r= 2 ready= 385ns : a= 90 b= 13 q= 447 r= 2 ready=
0 395ns : a= 90 b= 13 q= 895 r= 2 ready= Booth 乘 法 器 參 考 : 維 基 百 科 : 布 斯 乘 法 演 算 法 以 下 範 例 來 自 維 基 百 科 Booth 算 法 範 例 : 考 慮 一 個 由 若 干 個 0 包 圍 著 若 干 個 1 的 正 的 [[ 二 進 制 ]] 乘 數, 比 如 00111110, 積 可 以 表 達 為 : 其 中,M 代 表 被 乘 數 變 形 為 下 式 可 以 使 運 算 次 數 可 以 減 為 兩 次 : 事 實 上, 任 何 二 進 制 數 中 連 續 的 1 可 以 被 分 解 為 兩 個 二 進 制 數 之 差 :. 因 此, 我 們 可 以 用 更 簡 單 的 運 算 來 替 換 原 數 中 連 續 為 1 的 數 字 的 乘 法, 通 過 加 上 乘 數, 對 部 分 積 進 行 移 位 運 算, 最 後 再 將 之 從 乘 數 中 減 去 它 利 用 了 我 們 在 針 對 為 零 的 位 做 乘 法 時, 不 需 要 做 其 他 運 算, 只 需 移 位 這 一 特 點, 這 很 像 我 們 在 做 和 99 的 乘 法 時 利 用 99=100-1 這 一 性 質 這 種 模 式 可 以 擴 展 應 用 於 任 何 一 串 數 字 中 連 續 為 1 的 部 分 ( 包 括 只 有 一 個 1 的 情 況 ) 那 麼, 布 斯 算 法 遵 從 這 種 模 式, 它 在 遇 到 一 串 數 字 中 的 第 一 組 從 0 到 1 的 變 化 時 ( 即 遇 到 01 時 ) 執 行 加 法, 在 遇 到 這 一 串 連 續 1 的 尾 部 時 ( 即 遇 到 10 時 ) 執 行 減 法 這 在 乘 數 為 負 時 同 樣 有 效 當 乘 數 中 的 連 續 1 比 較 多 時 ( 形 成 比 較 長 的 1 串 時 ), 布 斯 算 法 較 一 般 的 乘 法 算 法 執 行 的 加 減 法 運 算 少 浮 點 數 運 算 浮 點 運 算 單 元 FALU // 輸 入 a, b 後 會 執 行 op 所 指 定 的 運 算, 然 後 將 結 果 放 在 暫 存 器 y 當 中 module falu(input [63:0] ia, input [63:0] ib, input [1:0] op, output reg [63:0] oy); real a, b, y;
always @(a or b or op) begin a = $bitstoreal(ia); b = $bitstoreal(ib); case (op) 2'b00: y = a + b; 2'b01: y = a - b; 2'b10: y = a * b; 2'b11: y = a / b; case $display("falu32:op=%d a=%f b=%f y=%f", op, a, b, y); oy = $realtobits(y); module module main; reg [63:0] a64, b64; wire [63:0] y64; reg [1:0] op; real a, b; // 測 試 程 式 開 始 falu falu1(a64, b64, op, y64); // 建 立 一 個 alu 單 元, 名 稱 為 alu1 initial begin // 測 試 程 式 的 初 始 化 動 作 a = 3.14159; a64 = $realtobits(a); b = 2.71818; b64 = $realtobits(b); op = 0; always #50 begin // 每 個 50 奈 秒 就 作 下 列 動 作 op = op + 1; // 讓 op 的 值 加 1 initial #1000 $finish; // 時 間 到 1000 奈 秒 就 結 束 module 執 行 結 果
D:\Dropbox\Public\web\oc\code>iverilog -o flu64 flu64.v D:\Dropbox\Public\web\oc\code>vvp flu64 falu32:op=0 a=3.141590 b=2.718180 y=5.859770 falu32:op=1 a=3.141590 b=2.718180 y=0.423410 falu32:op=2 a=3.141590 b=2.718180 y=8.539407 falu32:op=3 a=3.141590 b=2.718180 y=1.155770 falu32:op=0 a=3.141590 b=2.718180 y=5.859770 falu32:op=1 a=3.141590 b=2.718180 y=0.423410 falu32:op=2 a=3.141590 b=2.718180 y=8.539407 falu32:op=3 a=3.141590 b=2.718180 y=1.155770 falu32:op=0 a=3.141590 b=2.718180 y=5.859770 falu32:op=1 a=3.141590 b=2.718180 y=0.423410 falu32:op=2 a=3.141590 b=2.718180 y=8.539407 falu32:op=3 a=3.141590 b=2.718180 y=1.155770 falu32:op=0 a=3.141590 b=2.718180 y=5.859770 falu32:op=1 a=3.141590 b=2.718180 y=0.423410 falu32:op=2 a=3.141590 b=2.718180 y=8.539407 falu32:op=3 a=3.141590 b=2.718180 y=1.155770 falu32:op=0 a=3.141590 b=2.718180 y=5.859770 falu32:op=1 a=3.141590 b=2.718180 y=0.423410 falu32:op=2 a=3.141590 b=2.718180 y=8.539407 falu32:op=3 a=3.141590 b=2.718180 y=1.155770 使 用 Verilog 預 設 的 浮 點 數 運 算 檔 案 : float.v module main; real x, y, x2, y2, xyadd, xysub, xymul, xydiv; reg [63:0] x1, y1; initial begin x=3.14159; y=-2.3e4; x1 = $realtobits(x); x2 = $bitstoreal(x1);
y1 = $realtobits(y); y2 = $bitstoreal(y1); xyadd = x+y; xysub = x-y; xymul = x*y; xydiv = x/y; $display("x=%f x1=%b x2=%f", x, x1, x2); $display("y=%f y1=%b y2=%f", y, y1, y2); $display("x+y=%f xyadd=%f", x+y, xyadd); $display("x-y=%f xysub=%f", x-y, xysub); $display("x*y=%f xymul=%f", x*y, xymul); $display("x/y=%f xydiv=%f", x/y, xydiv); module 執 行 結 果 : D:\Dropbox\Public\web\oc\code>iverilog -o float float.v D:\Dropbox\Public\web\oc\code>vvp float x=3.141590 x1=01000000000010010010000111111001111100000001101110000110011 01110 x 2=3.141590 y=-23000.000000 y1=110000001101011001110110000000000000000000000000000000 0000000 000 y2=-23000.000000 x+y=-22996.858410 xyadd=-22996.858410 x-y=23003.141590 xysub=23003.141590 x*y=-72256.570000 xymul=-72256.570000 x/y=-0.000137 xydiv=-0.000137 自 行 設 計 浮 點 運 算 器 // https://instruct1.cit.cornell.edu/courses/ece576/studentwork/ss868/fp/ Reg27FP/FpMul.v // https://instruct1.cit.cornell.edu/courses/ece576/studentwork/ss868/fp/ Reg27FP/FpAdd.v // https://instruct1.cit.cornell.edu/courses/ece576/floatingpoint/index.h
tml#schneider_fp `define F_SIGN 63 `define F_EXP 62:52 `define F_FRAC 51:0 // a = (-1)^a.s (1+a.f) * 2 ^ {a.e-1023} // b = (-1)^b.s (1+b.f) * 2 ^ {b.e-1023} // a*b = (-1)^(a.s xor b.s) (1+a.f) (1+b.f) * 2^{ (a.e+b.e-1023) - 1023} // z.s = a.s xor b.s z.f = tail(...) z.e = a.e+b.e-1023 module fmul(input [63:0] a, input [63:0] b, output [63:0] z); wire a_s = a[`f_sign]; wire [10:0] a_e = a[`f_exp]; wire [51:0] a_f = a[`f_frac]; wire b_s = b[`f_sign]; wire [10:0] b_e = b[`f_exp]; wire [51:0] b_f = b[`f_frac]; wire z_s = a_s ^ b_s;// 正 負 號 z.s = a.s xor b.s wire [105:0] f = {1'b1, a_f} * {1'b1, b_f}; // 小 數 部 分 : f = {1, a.f } * {1, b.f} wire [11:0] e = a_e + b_e - 12'd1023; // 指 數 部 份 : e = a.e + b.e - 1023 wire [51:0] z_f = f[105]? f[104:53] : f[103:52]; // 若 最 高 位 f[105] == 1, 則 取 z.f[104:53], 否 則 取 z.f[103:52] wire [10:0] z_e = f[105]? e[10:0]+1 : e[10:0]; // 若 最 高 位 f[105] = = 1, 則 取 z.e 要 上 升 1 (???), 否 則 不 變 wire underflow = a_e[10] & b_e[10] & ~z_e[10]; // underflow assign z = underflow? 64'b0 : {z_s, z_e, z_f}; // 若 underflow, 則 傳 回 零, 否 則 傳 回 z={z.s, z.e, z.f} module module main; real x, y, z; reg [63:0] x1, y1; wire [63:0] z1;
fmul f1(x1, y1, z1); initial begin // x=7.00; y=-9.00; // x=6.00; y=8.00; // x=301.00; y=200.00; x=1.75; y=1.75; x1 = $realtobits(x); y1 = $realtobits(y); #100; $display("a.s=%b a.e=%b a.f=%b", f1.a_s, f1.a_e, f1.a_f); $display("a.s=%b b.e=%b b.f=%b", f1.b_s, f1.b_e, f1.b_f); $display("e=%b \nf=%b \nunderflow=%b", f1.e, f1.f, f1.underflow); $display("z.s=%b z.e=%b z.f=%b", f1.z_s, f1.z_e, f1.z_f); z = $bitstoreal(z1); $display("x=%f y=%f z=%f", x, y, z); $display("x1=%b \ny1=%b \nz1=%b", x1, y1, z1); module 執 行 結 果 D:\Dropbox\Public\web\oc\code>iverilog -o fpu64 fpu64.v D:\Dropbox\Public\web\oc\code>vvp fpu64 a.s=0 a.e=01111111111 a.f=11000000000000000000000000000000000000000000000 00000 a.s=0 b.e=01111111111 b.f=11000000000000000000000000000000000000000000000 00000 e=001111111111 f=11000100000000000000000000000000000000000000000000000000000000000000000 0000000 0000000000000000000000000000 underflow=0 z.s=0 z.e=10000000000 z.f=10001000000000000000000000000000000000000000000
00000 x=1.750000 y=1.750000 z=3.062500 x1=0011111111111100000000000000000000000000000000000000000000000000 y1=0011111111111100000000000000000000000000000000000000000000000000 z1=0100000000001000100000000000000000000000000000000000000000000000 參 考 文 獻 http://en.wikipedia.org/wiki/single-precision_floating-point_format http://en.wikipedia.org/wiki/double-precision_floating-point_format 繪 圖 加 速 功 能 (Graphics) 在 3D 的 遊 戲 或 動 畫 的 運 算 當 中, 經 常 要 計 算 向 量 的 加 法 內 積 的 浮 點 數 運 算, 而 且 這 些 運 算 通 常 獨 立 性 很 高, 可 以 採 用 平 行 的 方 式 計 算, 所 以 就 需 要 在 繪 圖 處 理 器 GPU 當 中 內 建 很 多 平 行 的 向 量 式 浮 點 運 算 器 來 加 速 此 一 計 算 過 程 舉 例 而 言, 當 我 們 想 計 算 下 列 的 N 維 向 量 加 法 時, 如 果 採 用 GPU 就 會 快 上 很 多 : for (i=0; i<n; i++) C[i] = A[i]+B[i]; 當 這 樣 的 程 式 被 放 到 GPU 去 執 行 時, 可 能 會 翻 譯 出 如 下 的 組 合 語 言 LOOP: LD R1, N LD R2, A LD R3, B LD R4, C VectorLoad V2, [R2] VectorLoad V3, [R3] VectorAdd V4, V2, V3 VectorStore V4, [R4] ADD R2, R2, 4*L ADD R3, R3, 4*L ADDd R4, R4, 4*L SUB R5, R5, L CMP R5, R0 JGE LOOP 如 果 上 述 GPU 的 一 次 可 以 計 算 的 浮 點 長 度 為 L 個, 那 麼 整 個 計 算 幾 乎 就 可 以 快 上 L 倍 了 GPU 通 常 有 著 與 CPU 不 同 的 指 令 集, 像 是 GPU 大 廠 NVIDIA 的 CUDA 就 是 一 種 GPU 指 令 集, NVIDIA
甚 至 還 為 CUDA 設 計 了 一 種 擴 充 式 的 C 語 言 -- 稱 為 CUDA C, 這 種 C 語 言 可 以 用 標 記 指 定 哪 些 部 份 應 該 展 開 到 CPU 當 中, 哪 些 應 該 展 開 到 GPU 當 中, 以 及 如 何 有 效 的 運 用 GPU 進 行 向 量 計 算 等 等 甚 至 CUDA 還 提 供 了 專 用 的 函 式 庫, 以 便 讓 CUDA C 能 夠 方 便 的 呼 叫 這 些 常 用 的 繪 圖 函 數 另 外 為 了 讓 各 家 廠 商 的 CPU 與 GPU 可 以 更 有 效 的 合 作, 甚 至 有 組 織 制 定 了 一 種 稱 為 OpenCL 的 標 準, 可 以 讓 各 家 廠 商 的 CPU 與 GPU 可 以 在 一 個 標 準 架 構 下 進 行 協 同 運 算, 以 便 有 效 的 運 用 這 些 異 質 處 理 器 的 效 能 平 行 處 理 (Parallel) 由 於 散 熱 等 因 素 無 法 克 服, 目 前 的 處 理 器 頻 率 已 經 不 容 易 再 快 速 往 上 調 整, 但 是 晶 片 的 密 度 與 容 量 還 再 持 續 增 加, 於 是 處 理 器 內 的 平 行, 也 就 是 多 核 心 的 情 況 就 愈 來 愈 普 遍 了 目 前 雙 核 心 四 核 心 八 核 心 甚 至 16 核 心 的 處 理 器 已 經 很 常 見 了, 在 可 見 的 未 來, 或 許 成 百 上 千 核 心 的 處 理 器 也 會 被 開 發 出 來, 因 此 如 何 運 用 平 行 技 術 充 份 利 用 這 麼 多 核 心 將 會 是 一 個 重 要 的 課 題 另 外 網 路 雲 端 運 算 的 需 求 不 斷 增 強, 也 會 進 一 步 增 加 多 處 理 器 技 術 的 重 要 性, 像 是 Google 提 出 的 MapReduce 就 在 大 數 據 的 潮 流 下 愈 來 愈 形 重 要, 而 MPI 與 Hadoop 等 分 散 式 平 行 處 理 技 術 也 就 在 這 種 情 況 下 逐 漸 普 及 了 對 於 平 行 計 算 的 架 構, 有 一 種 相 當 好 的 分 類 方 式, 是 根 據 指 令 與 資 料 是 單 數 還 複 數 所 進 行 的 分 類, 這 種 分 類 法 將 平 行 架 構 分 為 單 指 令 多 資 料 (SIMD), 多 指 令 單 資 料 (MISD), 多 指 令 多 資 料 (MIMD) 等 架 構, 在 加 上 傳 統 非 平 行 化 的 單 指 令 單 資 料 (SISD) 等 形 成 一 個 2*2 的 矩 陣 舉 例 而 言, 像 是 上 述 的 GPU 向 量 架 構, 是 屬 於 SIMD 的 平 行 架 構, 而 下 圖 中 的 UMA 與 NUMA 架 構, 則 是 屬 於 MIMD 的 平 行 架 構 當 然 在 上 述 架 構 下, 每 個 處 理 器 理 又 有 自 己 的 cache, 如 何 保 持 這 些 cache 的 一 至 性 就 會 是 個 問 題, 而 解 決 這 些 問 題 所 依 賴 的 方 法 仍 然 是 與 快 取 一 至 性 中 的 所 使 用 的 寫 穿 (write-through) 和 寫 回 (write-back) 等 方 法 類 似, 只 是 規 模 要 放 大 到 整 個 網 路 上 的 快 取 而 已 結 語 目 前 在 本 書 當 中, 我 們 對 於 高 階 處 理 器 的 實 作 還 很 缺 乏, 像 是 快 取 MMU 與 pipeline 等 部 份 的 實 作 都 還 沒 有 完 成, 因 此 還 無 法 完 全 實 現 開 放 電 腦 計 畫 硬 體 部 份 的 目 標
不 過 在 探 索 過 程 當 中, 筆 者 對 硬 體 領 域 開 始 有 了 更 深 一 層 的 瞭 解, 希 望 未 來 能 夠 將 這 些 部 份 實 作 好 之 後, 盡 快 加 入 到 本 書 裏 面 到 時 隨 著 本 書 進 化 成 第 二 版 第 三 版..., 相 信 讀 者 與 我 對 電 腦 硬 體 的 理 解 也 會 變 得 愈 來 愈 深 入 了
附 錄 本 書 內 容 與 相 關 資 源 主 題 投 影 片 實 作 程 式 教 學 影 片 第 1 章. 開 放 電 腦 計 畫 http://youtu.be/goi-vuwunji 第 2 章. 電 腦 硬 體 架 構 co01_overview.pdf http://youtu.be/goi-vuwunji 計 算 機 結 構 : 簡 介 1 計 算 機 結 構 : 簡 介 2 http://youtu.be/px2jdlovlmg http://youtu.be/_tuge1gtj6a 第 3 章. 硬 體 描 述 語 言 -- Verilog - 區 塊 式 設 計 - 流 程 式 設 計 Icarus http://youtu.be/wikqxz-9ci0 Verilog xor3.v http://youtu.be/m_pm57k1g80 Quartus II DE2-70 http://youtu.be/raf1ignofik http://youtu.be/toz4gyuuoga 第 4 章. 組 合 邏 輯 多 工 器 mux.v http://youtu.be/tmdq_wcmlmi 全 加 器 http://youtu.be/ud3o5xqnkfq 4 位 元 加 法 器 [adder4.v] http://youtu.be/t_0q5ddi4xe 4 位 元 加 減 器 [addsub4.v] 4 位 元 快 速 加 法 器 N 位 元 加 法 器 乘 法 器 移 位 乘 法 器 mult4.v shift_mult.v 除 法 器 浮 點 乘 法 器 [fmult4.v]
第 5 章. 算 術 邏 輯 單 元 算 術 邏 輯 單 元 ALU [alu.v] http://youtu.be/tmdq_wcmlmi 第 6 章. 記 憶 單 元 正 反 器 栓 鎖 器 latch.v http://youtu.be/2esdl8aghgi 計 數 器 counter.v http://youtu.be/blwfoppsl1a 狀 態 機 多 工 器 mux.v http://youtu.be/kcbuo_vrq0u 暫 存 器 暫 存 器 群 regbank.v http://youtu.be/6zlpnv-puba 8 位 元 記 憶 體 memory8.v 32 位 元 記 憶 體 memory32.v http://youtu.be/dgmy6yhtbnw 第 7 章. 控 制 單 元 第 8 章. 微 處 理 器 - MCU0 指 令 集 - MCU0 迷 你 版 - MCU0 迷 你 版 ( 區 塊 設 計 ) - MCU0 完 整 版 第 9 章. 中 斷 與 輸 出 入 - MCU0 中 斷 處 理 - MCU0 的 輸 出 入 第 10 章. 記 憶 系 統 - 記 憶 體 階 層 快 取 記 憶 體 第 11 章. 高 階 處 理 器
哈 佛 架 構 流 水 線 - CPU0 指 令 集 - CPU0 迷 你 版 - CPU0 完 整 版 第 12 章. 速 度 議 題 乘 法 與 除 法 浮 點 運 算 單 元 FPU 繪 圖 加 速 功 能 平 行 處 理 結 語