--- 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)