---
title: FPGA 模擬 Apple ][
image: https://imgur.com/gallery/z9wh39H
description: 關於 Apple ][ 的資料整理, 參考臉書社團 "台灣 Apple II 同學會" https://www.facebook.com/groups/taiwanappleii
---
# FPGA 模擬 Apple ][
## <span style="color:blue">用 FPGA 模擬 6502 開機</span>
<span style="color:green">進度: 100% 7/19 (2021/7/15 started)</span>
* <span style="color:green">2021/7/15: 收到 Ben Eater 的 6502 CPU kit, 完成開機</span>
* <span style="color:green">2021/7/17: 下載 65C02 CPU VHDL 程式到 QMtech Cyclone IV Starter kit, 還不確定是否運作正常, Address bus 沒有訊號</span>
* <span style="color:green">2021/7/18: 將 65C02 VHDL code 下載到 QMtech Cyclone IV Starter kit 用 breadboard 接出來看訊號</span>
* <span style="color:green">2021/7/19: 用 Arduino Mega 2560 可以看得出 FPGA 上的 6502 power on 從 FFFC:FFFD 開始執行</span>
之前的動作都是站在巨人的肩膀上實作, 接下來就要靠自己了.
### 用 FPGA 模擬 6502 CPU
* 到 [FPGA-Based 6502 Processor in VHDL](https://www.allaboutcircuits.com/ip-cores/processor/6502vhdl/) 下載檔案
* 失敗
* <span style="color:green">改用另一個設計 [OpenCores.org - cpu65c02_true_cycle](https://opencores.org/projects/cpu65c02_true_cycle)</span>
* <span style="color:green">有好幾個檔案, top level 是 r65c02_tc</span>
* <span style="color:green">解壓縮後, 到目錄 cpu65c02_true_cycle/trunk/released/rtl/vhdl 下, 會有好幾個 .vhd 檔案, 找到 r65c02_tc.vhd (一開始, 有幾次在 r65c02_tc.vhd 或 core.vhd 間掙扎, 熟悉了 VHDL 的 hierarchical 架構才找到 r65c02_tc 是 top level)</span>
* 參考 [VHDL: Creating a Hierarchical Design with multiple entities](https://www.intel.com/content/www/us/en/programmable/support/support-resources/design-examples/design-software/vhdl/v_hier.html)
* <span style="color:green">打開 Quartus</span>
* <span style="color:green">建立新的 project, 命名為 r65c02_tc (project 名稱得跟 r65c02_tc.vhd 定義的 entity r65c02_tc 同名)</span>
* <span style="color:green">到 Quartus 的 Processing - Start Compilation</span>
* <span style="color:green">到 Quartus 的 Assignments - Pin Planner</span>
* 設定以下的 pin definition
* 將 I/O Standard 全部設定為 3.3-V LVTTL
* <span style="color:green">Pin Planner 後需要再一次 Quartus 的 Processing - Start Compilation</span>
* <span style="color:green">到 Tools 的 programmer</span>
* 類似 [Ben Eater 的 6502 Hello World](https://www.youtube.com/watch?v=LnzuMJLZRdU) 接上 Arduino Mega 2560, [Mega 2560 接腳圖](http://www.robotigs.com/robotigs/documentatie/plaatjes/megapins.png) 可以看得到 6502 power on sequence 從 FFFC:FFFD 位址開始
Cyclone IV EP4CE15F23C8N
| 6502 CPU pin name / no.| PCB | PCB pin | FGPA location | FPGA PIN |
|:---------------------- |:----|:-----------|:--------------|:---------|
|A15 / pin / output | J12 | 33 | A10 |IO,DIFFIO_T14N|
|A14 / pin | J12 | 31 | A13 |FPGA J12 pin 31 的 A14 燒掉, 暫時改成 JP1 pin 6 的 B7 |
|A13 / pin | J12 | 29 | A14 |FPGA J12 pin 29 的 A13 燒掉, 暫時改成 pin 35 的 A9|
|A12 / pin | J12 | 27 | A15 ||
|A11 / pin | J12 | 25 | A16 ||
|A10 / pin | J12 | 23 | A17 ||
|A9 / pin | J12 | 21 | A18 ||
|A8 / pin | J12 | 19 | A19 ||
|A7 / pin | J12 | 17 | A20 ||
|A6 / pin | J12 | 15 | C17 ||
|A5 / pin | J12 | 13 | C19 ||
|A4 / pin | J12 | 11 | C20 ||
|A3 / pin | J12 | 9 | F20 ||
|A2 / pin | J12 | 7 | H20 ||
|A1 / pin | J12 | 5 | B22 ||
|A0 / pin | J12 | 3 | C22 ||
|clk_clk_i / pin / input| J12 | 36 | B9 | B9 unconnected, use internal clock source clock_out|
|irq_n_i / pin / input | J12 | 34 | B10 ||
|nmi_n_i / pin / input | J12 | 32 | B13 ||
|rdy_i / pin / input | J12 | 30 | B14 |FPGA J12 pin 30 的B14 燒掉, connected to Vcc, 暫時先接到 J12 pin 24 B17|
|rst_rst_n_i / pin /input| J12 | 28 | B15 ||
|so_n_i / pin / input | J12 | 26 | B16 ||
|D7 / pin / input | J12 | 18 | B20 ||
|D6 / pin / input | J12 | 16 | D17 |FPGA J12 pin 16 的 D17 燒掉, 暫時改成 pin 22 的 B18|
|D5 / pin / input | J12 | 14 | D19 |FPGA J12 pin 14 的 D19 燒掉, 暫時改成 pin 20 的 B19|
|D4 / pin / input | J12 | 12 | D20 ||
|D3 / pin / input | J12 | 10 | F19 ||
|D2 / pin / input | J12 | 8 | H19 ||
|D1 / pin / input | J12 | 6 | B21 ||
|D0 / pin / input | J12 | 4 | C21 ||
|:---------------------- |:----|:-----------|:--------------|:---------|
|rd_o / pin / output | JP1 | 5 | A7 ||
|sync_o / pin / output | JP1 | 7 | A6 ||
|wr_n_o / pin / output | JP1 | 9 | A5 ||
|wr_o / pin / output | JP1 | 11 | A4 ||
|Result (Clock step debounce) |<strike>JP1</strike>|<strike>13</strike>|<strike>A3</strike>|unconnected, 訊號接到 clock_out|
|Clock | | | T2 |unconnected, debounce clock 輸入, 接到 50MHz T2 訊號|
|Clock 1Hz |<strike>JP1</strike>|<strike>14</strike>|<strike>B3</strike> |unconnected, 訊號經過邏輯運算接到 clock_out|
|Clock 1MHz |<strike>JP1</strike>|<strike>15</strike>|<strike>B1</strike>|unconnected, 訊號經過邏輯運算接到 clock_out|
|sw2_in | JP1 | 13 | A3 ||
|sw1_in | JP1 | 14 | B3 ||
|clock_out | JP1 | 15 | B1 ||
|Button (for step clock) | JP1 | 16 | B2 ||
|Clock_reset_n | JP1 | 17 | C1 | connected to Vcc|
|:---------------------- |:----|:-----------|:--------------|:---------|
|d_o[0-7] / pin / output | J10 | | ||
## <span style="color:blue">用 FPGA 來模擬 Ben Eater 的可調式 clock</span>
<span style="color:red">進度: 100% 7/21 (started on 2021/7/18)</span>
* [Youtube - “Hello, world” from scratch on a 6502 — by Ben Eater](https://www.youtube.com/watch?v=LnzuMJLZRdU)
* Debounce circuit
* [Debounce circuit - VHDL - from DigiKey](https://forum.digikey.com/t/debounce-logic-circuit-vhdl/12573)
* Clock with various frequency
* [How to write a VHDL code for 1Hz signal?](https://www.thecodingforums.com/threads/how-to-write-a-vhdl-code-for-1hz-signal.583231/page-2)
* [intel / Altera - AN 496: Using the Internal Oscillator IP Core](https://www.intel.com/content/dam/www/programmable/us/en/pdfs/literature/an/an496.pdf)
* 參考 [How combine multiple VHDL codes to make one system](https://stackoverflow.com/questions/65670200/how-combine-multiple-vhdl-codes-to-make-one-system) 文章, 修改 vhdl 檔案如下,
* [目錄](./cpu65c02_true_cycle/trunk/released/rtl/vhdl_clocks/)
* [top level vhdl, 包含 button debounce 設計](./cpu65c02_true_cycle/trunk/released/rtl/vhdl_clocks/clock_various.vhd)
* 在 module 的 entity 定義裡面, 用到 word. 的 keyword, 參考 [Synario Design Automation - VHDL Reference Manual](https://www.ics.uci.edu/~jmoorkan/vhdlref/Synario%20VHDL%20Manual.pdf)
* [VHDL1Hz.vhd](./cpu65c02_true_cycle/trunk/released/rtl/vhdl_clocks/VHDL_1Hz.vhd)
* [VHDL1MHz.vhd](./cpu65c02_true_cycle/trunk/released/rtl/vhdl_clocks/VHDL_1MHz.vhd)
<span style="color:green">應用於 65C02 CPU 的 single step, 1Hz 都可以在 Arduino Mega 2560 上抓到精準的資料, 跑到 1MHz 時, Arduino 抓到的資料就亂了 (正常, Mega 的速度不夠快到足以處理 1MHz CPU 的頻率)</span>
* 曾經 single step 時會跳行, 嘗試了
* 加了電容到地 ==> 有幫助, 但無法徹底解決
* 修改 debounce 的 stable 值, 發現值越大反而有問題, 最後用原始設定的 10ms 反而是最好.....
```
stable_time : INTEGER := 10); --time button must remain stable in ms
```
## <span style="color:blue">整合 65C02 CPU 跟 clock source </span>
<span style="color:green">進度: 100% 7/25 (started on 2021/7/21)</span>
* <span style="color:red">期間出現無法執行, 位址都是 0001, 嘗試 debug 了幾天</span>
* <span style="color:red">在 debug 過程中, FPGA 的 I/O Standard 改成 3.3-V LVCMOS 也行 (後來還是改回來 3.3-V LVTTL)</span>
* <span style="color:red">發現 FPGA 很燙, 似乎 Vcc 有接錯, 重新接線後, 發現還是有幾個地方可以改善</span>
* clock/reset 線路的 3.3V 電壓改由 Arduino Mega 2560 提供, 不由 QMTech 板子提供 (應該沒有較大影響)
* 多按幾次 FPGA 上 6502 CPU 的 reset, 才可以順利 reset 從 FFFC:FFFD 開始執行
* 最後發現, FPGA 的 2 個 port 被我燒掉, 換個 port 就可以順利進行.
* 後來, 又不行了, address 一直停留在 0001h, 原來又有幾個 port 燒掉 (其中一條是 rdy_i), 換了 port 後也好了.
* 有一次不小心, 發現 address 都是 0000h 時, 以為又是哪裡出問題, 原來是 Clock 線脫開了, 接回去就好了.
* 有一次, 把 Arduino 的 3.3V 接到地, 也都是 address 都是 0001h
## <span style="color:blue">整合 65C02 CPU 跟 clock source 之加強版</span>
<span style="color:green">進度: 100% 7/27 (started on 2021/7/25)</span>
* 改用 DIP switch 來決定 clock 頻率 (1Hz / 1MHz) 或 single step
* 且 clock_out 輸出, 也同時在 FPGA 內部連接至 65c02 CPU clk_clk_i
| Clock 選擇 | | Switch 1 | |
|:--------|:------|:------|:------|
| | |On | Off |
|Switch 2 | On |Single step | 1Hz |
| | Off |Single step | 1MHz |
## <span style="color:blue">整合 65C02 CPU 及 ROM (使用 Block RAM)</span>
<span style="color:green">進度: 100% 8/4 (started on 2021/7/27)</span>
* 關於 FPGA 內部 Block RAM 資料的參考文件: [FPGA design usin VHDL - ROM implementation using block RAM](https://vhdlguide.readthedocs.io/en/latest/vhdl/dex.html#read-only-memory-rom)
* <span style="color:green">Altera 建議使用內建的 IP, 透過 Quartus 的 MegaWizard Plug-In Manager, 使用 library of parameterized modules (LPM) 及 Soft IP [MegaWizard Plug-In Manager](https://www.intel.com/content/www/us/en/programmable/quartushelp/13.0/mergedProjects/hdl/mega/mega_view_megawiz.htm)</span>, 但是遇到了幾個問題:
* <span style="color:red">在 Quartus 的 menu - edit 無法找得到 MegaWizard Plug-In Manager. 解決方式: 選 menu - Tools - IP Catalog, 會在主畫面右側顯示 IP Catalog 子畫面, 選擇 (double click) Installed IP - Library - On Chip Memory - ROM 1-PORT</span>
* 選擇 VHDL
* 選擇檔案儲存的目錄, 輸入檔案名稱, 要注意檔案名稱, 有錯誤時會提示 (我輸入 r65c02_rom_8kb.vhd)
* <span style="color:red">這時候電腦反應很慢, 甚至當機, </span> 查到[intel 網頁說明 - MegaWizard extremely slow and unstable](https://community.intel.com/t5/Intel-Quartus-Prime-Software/MegaWizard-with-LPM-MUX-runs-extremely-slowly-and-is-unusable/td-p/252872)
```
# 原來執行
$ quartus
# 或是執行
$ /home/ubuntu/intelFPGA_lite/20.1/quartus/bin/quartus
# 會造成 MegaWizard 出問題, 應該改用以下執行方式
$ ssh -Y /home/ubuntu/intelFPGA_lite/20.1/quartus/bin/quartus
# 但是執行時, 出現問題
connect to host localhost port 22: Connection refused
# 這時候, 重新安裝一次 ssh
$ sudo apt-get install ssh
# 再執行一次
$ ssh -Y /home/ubuntu/intelFPGA_lite/20.1/quartus/bin/quartus
$
```
* <span style="color:green">接下來, 設定 ROM: 1-PORT</span>
* how wide: 8 bits
* how many 8-bits (menu 會隨著上一個設定而更新) words of memory: 32768 words
* 參考前面的 [Apple II ROM Listings](#appleiiromlistings) 的 APPLE2.ROM \$C000-\$FFFF 需要用到 20KB (還不清楚如何設定將 20KB 的資料映射到對的位址, 因為 Quartus 是先填 20KB, 再將其餘補零, 我們需要在前面的 12KB 補零, 20KB 的資料留在最後)
* <span style="color:green">Quartus 需要 ROM 的 hex 檔案是 [intel hex 格式]</span>(https://www.intel.com/content/www/us/en/programmable/quartushelp/13.0/mergedProjects/reference/glossary/def_hexfile.htm), 找到了個轉換程式 [ARM Keil - BINARY to Intel HEX Converter Utility](https://www.keil.com/download/docs/113.asp) BIN2HEX.EXE 但只能在 Windows XP 下執行, 只得根據 [How to Download and Install Windows XP for Free](https://www.makeuseof.com/tag/download-windows-xp-for-free-and-legally-straight-from-microsoft-si/), 在 VirtualBox 建立個 Windows XP 的 VM, 雖然網路設定不通, 我只需要設定 shared folders 將 BIN2HEX.EXE 及 APPLE2.ROM 在 Windows XP 上執行即可產生 APPLE2.HEX.
* 因為 APPLE2.ROM 只有 20KB, 而 Quartus 上的 ROM 只能選 32KB, 因此 Quartus 會先把 APPLE2.ROM 的 20KB 填在前面, 後面補 0. 而我們需要的是前面補 0. 因此, 先修改 APPLE2.ROM, 先補上 12KB 的 0, 再執行一次 BIN2HEX.EXE, 這樣就對的上了.
* <span style="color:red">執行 single step 時, 發現 CPU 讀到 ROM 的 data 會慢半拍, 原來, 我 ROM 的 clock 跟 CPU clock 一致造成的, 把 ROM 的 clock 拉到 FPGA 主頻率的 50MHz clk 來解決這問題.</span>
* 照 Apple II 的 ROM 逐步執行, 發現 CPU 在 return 時, 跳到 0000h 的位址, 以為是哪裡有問題, 才注意到 jsr 的時候, CPU 先將目前的位址 push 到 1FF 的位置 (一開始還懷疑 CPU 的 soft core 設計有問題, 錯怪了), 但是我還沒有裝 RAM, return 時讀回來的是 0000h, 接下來就是安裝 RAM 了.
## <span style="color:blue">將 ROM data out 與 CPU data_in, data_out 連接, 且設定為 tri-state</span>
<span style="color:green">進度: 100% 08/08 (started on 2021/08/05)</span>
* [2021/08/08 - Connectoring CPU data_out and data_in with tri-state](https://github.com/marconi1964/FPGA_65c02_CPU/tree/bf6daf4754a02978f0bee60bc0c8bbe60a130944)
## <span style="color:blue">將 clock source 獨立成一個 module</span>
<span style="color:green">進度: 100% 08/08 (started on 2021/08/08)</span>
* [2021/08/08 - Modulize clock source and selection](https://github.com/marconi1964/FPGA_65c02_CPU/tree/67a857a58744eccb477011a726373f590e3bb6d5)
## <span style="color:blue">整合 65C02 CPU 及 RAM (使用 FPGA IP)</span>
<span style="color:green">進度: 100% 8/21 (started on 2021/8/4)</span>
* 本來是想使用 QMTech 上的 DRAM, 看了幾天, 還是看不懂.
* 發現 FPGA 也是可以實現 RAM 的功能 (而且不需要設計 DRAM 必要的 refresh 線路), 參考[intel VHDL Single Port RAM 設計](https://www.intel.com/content/www/us/en/programmable/support/support-resources/design-examples/design-software/vhdl/vhd-single-port-ram.html)
* 還沒搞清楚 single port 跟 dual port 的差異 (有 read/write 2 個 address port)
* 也沒搞清楚 synchronous 跟 asynchronus 的差異
* 先試範例的 single port ram
* 稍微改選範例程式, 因為原始程式 addr 的設定是 unsigned integer,
* 關於 [VHDL Predefined Types from the package standard](https://www.csee.umbc.edu/portal/help/VHDL/types.html)
* Integer : signed value
* Natural : unsigned value starting at 0
* Positive: unsigned value starting at 1
* 修改範例參考 [FGPA4Student VHDL code for single port RAM](https://www.fpga4student.com/2017/08/vhdl-code-for-single-port-ram.html),
```
addr: in natural range 0 to 32767;
-- 應該是配合 RAM 使用的定址方式 以 0 開始的 unsigned integer type
-- 改成以下
addr: in std_logic_vector(14 downto 0);
-- 存取到 ram 時, 需要做 type 轉換
ram(addr) <= data;
-- 從 std_logic_vector 轉成 unsigned integer
ram(to_integer(unsigned(addr))) <= data;
```
* 如果是倒過來, 應該用這個 function, # 不過, 我自己還沒驗證過.
```
my_vector <= std_logic_vector(to_unsigned(my_int, my_vector'length)));
```
* 整合進 r65c02_tc 時, Quartus 報錯, 無法容納 RAM size (32768 Bytes x 8 bits), 改成 16384 Bytes
* [2021/08/21 - Adding FPGA RAM -32768-Bytes x 8 bit FGPA 容量不夠 ==> 16384Bytes x 8 bits](https://github.com/marconi1964/FPGA_65c02_CPU/tree/486b1699e01bc61cd25c4944d1aa28bd7f9d5dc0)