<!-- 112.9.16 --> # **<font color="purple">"SoC Design" Course Study Journel</font>** This study journal is created by Yeh Cheng-Hong, 112501538, CoSR department, NTHU. Course: NTHU 11210EE525100 系統晶片設計(SOC Design) <br/><br/> ## Lab 0: Collaborative Learning Checklist - [x] Join in SoC lab Slack channel. - [x] Create README in [my personal Github](https://github.com/whywhytellmewhy). - [x] Subscribe [bol-edu/HLS-SOC-Discussions](https://github.com/bol-edu/HLS-SOC-Discussions)/Discussions on Github. - [x] Create a study journel on Hackmd.io. (This one!) <br/><br/> <!-- 112.9.28 --> ## Lab 1: From Vitis HLS to onlineFPGA ==環境安裝步驟==: * **安裝 VirtualBox:** 虛擬機器,用來跑VM,好處是可以有「擷取/恢復快照」的功能,可以記錄下不同版本,方便開發與管理 * **安裝Ubuntu VM以及Xilinx相關軟體(Vitis、Vitis HLS、Vivado):** 在[雲端硬碟](https://drive.google.com/drive/folders/1aVujPu872Siyw-uODWrvf4p0axVsdPsw)中已pack在一起成為映像檔。也可以按照[此處](https://github.com/bol-edu/course-lab_1/blob/2022.1/HLS%20Tools%20Installation%20Guide%202022.1.pdf)說明下載Windows版,或是按照[此處](https://github.com/bol-edu/course-lab_1/blob/2022.1/HLS%20Tools%20Installation%20Guide%202022.1_ubuntu.md)說明下載Ubuntu VM版。 ==安裝好上述環境後,就可以正式開始做這次的lab了!== The implementation flow to verify/validate an IP design is summarized as follows: (以**VM**為主要開發平臺) 1. **Vitis HLS:** 1. Type in `vitis_hls` in terminal 2. GUI介面上按下「Create Project」,輸入Project name:「hls_ip」以及Location:「/home/ubuntu/course-lab_1」 3. 跳過Add/Remove Design Files設定介面 4. 跳過Add/Remove Testbench Files設定介面 5. 來到Solution Configuration設定介面:Period:「10」(單位: ns) [註1];Part:「按下『...』-->Select:『Parts』、Search中輸入並點選『xc7z020clg400-1』」[註2] > [註1] 若PYNQ-Z2的FPGA可運行於時脈200MHz以上,可以選擇週期為10ns [(資料來源)](https://github.com/bol-edu/course-lab_1/blob/2022.1/2022.1-Workbook-Lab1.pdf) > [註2] 因vitis 2021.2版本讀不到PYNQ-Z2的board file,所以選用與PYNQ-Z2相同part [(資料來源)](https://github.com/bol-edu/course-lab_1/blob/2022.1/2022.1-Workbook-Lab1.pdf) 6. 匯入design檔案:hls_ip/Source按右鍵-->「New Source File...」-->選擇「Multiplication.cpp」及「Multiplication.h」 7. 匯入testbench檔案:hls_ip/Test Bench按右鍵-->「New Test Bench File...」-->選擇「MultipTester.cpp」 8. 指名IP匯出的top function:上方工具欄「Project-->Project Settings-->Synthesis-->Top function: 輸入『Multiplication.cpp的function名稱,即multip_2num』」 9. Directives Control:在Synthesis前必須先加入directive描述 * 方法1: inline方式加入`#pragma`,例如 ```csharp #pragma HLS INTERFACE s_axilite port=pn32ResOut #pragma HLS INTERFACE s_axilite port=n32In2 #pragma HLS INTERFACE s_axilite port=n32In1 #pragma HLS INTERFACE ap_ctrl_none port=return ``` * 方法2: 用directives.tcl [Lab2 Workbook](https://github.com/bol-edu/course-lab_2/blob/2022.1/2022.1-Workbook-Lab2.pdf)中有介紹,步驟如下: 1. 中上方的視窗選擇「MultipTester.cpp」的tab 2. 右上方的視窗選擇「Directive」的tab 3. 編輯directives: * 在此以「插入**interface**的directive組態」為例: 1. 在top function上按滑鼠右鍵-->選擇「Insert Directive...」 2. Directive:「INTERFACE」-->Options:「(填入要的值)」 3. 重複上述動作,將top function引入的引數(即top function下方的那些綠色點點)也插入interface的directive組態 :::warning :warning: 有些設置需要先把視窗拉大才看得到 [(資料來源)](https://github.com/bol-edu/course-lab_2/blob/2022.1/2022.1-Workbook-Lab2.pdf) ::: :::danger :warning: 若有需要設定depth,記得要設成跟test data筆數**相等** [(資料來源)](https://github.com/bol-edu/course-lab_2/blob/2022.1/2022.1-Workbook-Lab2.pdf) ::: 4. 最終結果會長得類似下圖: ![](https://hackmd.io/_uploads/BJZsW4YeT.png) </br></br> :::info 💡 最後可在左上方視窗中的「hls_ip/solution1/constraints/directives.tcl」看到Directives的設定結果 💡 在檔案總管的檔案「hls_ip/solution1/directives.tcl」中也可看到Directives的設定結果 ::: 10. C simulation:按工具列的綠色箭頭-->「C Simulation」。結果將顯示在下方console欄。 11. C Synthesis:按工具列的綠色箭頭-->「C Synthesis」-->設定Period。report出現後,檢查是否有slack/time violation。 12. Co-simulation:**<font color="red">必須先將</font>** ```csharp #pragma HLS INTERFACE ap_ctrl_none port=return ``` **<font color="red">這行註解掉,並且重跑一次C Synthesis!</font>** 接著按工具列的綠色箭頭-->「Cosimulation」-->第一個下拉式選單選擇「Vivado XSIM」,右側點選「Verilog」;第二個下拉式選單Dump Trace選擇「all」。完成後可按下工具列的![](https://hackmd.io/_uploads/H1jnL0Mxp.png)圖示檢視波形。 13. Export IP:**要先將** ```csharp //#pragma HLS INTERFACE ap_ctrl_none port=return ``` **這行還原,並且重跑一次C Synthesis!</font>** 接著按工具列的綠色箭頭-->「Export RTL」-->Export Format下拉式選單選擇「Vivado IP (.zip)」。 > 由[Lab2 Workbook](https://github.com/bol-edu/course-lab_2/blob/2022.1/2022.1-Workbook-Lab2.pdf),此步驟亦可稱為「**Export RTL**」 2. **Vivado:** 1. Type in `vivado` in terminal 2. GUI介面上按下「Create Project」 3. 按Next 4. 輸入Project name:「vivado」以及Location:「/home/ubuntu/course-lab_1」 5. 勾選「Do not specify sources at this time」 6. Parts輸入並選擇「xc7z020clg400-1」 7. 按Finish 8. Import IP(from Vitis HLS):點擊左側欄的「Settings-->IP展開-->Repository-->+ --> 點選/home/ubuntu/course-lab_1/hls_ip -->select-->展開IPs-->點選Multip_2num [註3]-->OK-->OK」 > [註3] 將IP repositories指定到前面的Vitis HLS專案目錄,接著匯入由Vitis HLS專案開發的IP [(資料來源)](https://github.com/bol-edu/course-lab_1/blob/2022.1/2022.1-Workbook-Lab1.pdf) 9. Block Design: 1. 點擊左側欄的IP INTEGRATOR下的「Create Block Design」-->OK-->在右上方Diagram視窗中按+ -->加入『ZYNQ7 Processing System』及『Multip_2num』這兩個components-->按下![](https://hackmd.io/_uploads/HysE2MXxa.png)重新整理-->按下上方綠色條中的<font style="background-color: Lime; color: blue">Run Block Automation</font>-->OK :::warning :warning: Run Block Automation會將ZYNQ的configuration重置,所以一定要**先「Run Block Automation」再完成底下的「ZYNQ的configure (例如PL Fabric clocks設定、PS-PL configuration設定)」** [(資料來源)](https://github.com/bol-edu/course-lab_2/blob/2022.1/2022.1-Workbook-Lab2.pdf) ::: 2. 設定ZYNQ的configuration:滑鼠左鍵雙擊ZYNQ7 Processing System的方塊圖-->點選Clock Configuration-->展開PL Fabric clocks-->FCLK_CLK0:『100』(單位: MHz) **<font color="red">[:warning:在按下OK前先注意下方說明]</font>** -->OK-->按下上方綠色條中的<font style="background-color: Lime; color: blue">Run Connection Automation</font> [註4]-->OK(若不能直接按OK則先勾選「All Automation」再按OK)-->若使用**AXI-Stream**的interface,則可能還會再出現<font color="blue">Run Connection Automation</font>的按鈕,一直按到沒有出現為止-->按下![](https://hackmd.io/_uploads/HysE2MXxa.png)重新整理-->檢查接線是否有誤-->手動接線(若需要的話)-->再點選Address Editor的tab檢視memory map > [註4] (For lab1) 因為此block design只有1個IP設計,故連線可由系統自動完成,直接執行Run Connection Automation即可 [(資料來源)](https://github.com/bol-edu/course-lab_1/blob/2022.1/2022.1-Workbook-Lab1.pdf) :::danger :warning: **特別注意**:若使用**AXI-Master / AXI-Stream**的interface,要特別設定下列configuration(這是因為AXI-Lite與AXI-Master / AXI-Stream在ZYNQ7 Processing System所使用的port不相同,所以必須開啟HP port設定 [(資料來源)](https://github.com/bol-edu/course-lab_2/blob/2022.1/2022.1-Workbook-Lab2.pdf)): 1. 滑鼠左鍵雙擊ZYNQ7 Processing System的方塊圖(前面的步驟已有做過) 2. 開啟HP port設定:點選PS-PL Configuration-->展開HP Slave AXI Interface-->將SAXI HP0 interface右方的方框打勾☑️</br></br> :warning: **特別注意**:若使用**AXI-Stream**的interface,除了上述2點外,還要特別做下列AXI-Master to stream的步驟(這是因為AXI-Master to stream是利用Xilinx的DMA IP來實現的,故需設定此DMA IP): 3. 在右上方Diagram視窗中按+ -->加入「AXI Direct Memory Access」這個component 4. 滑鼠左鍵雙擊AXI Direct Memory Access的方塊圖 5. 將「Enable Scatter Gather Engine」取消勾選-->Width of Buffer Length Register (8-26):「25」bits-->「『Enable Read Channel』及『Enable Write Channel』是否勾選:依照設計需求決定,可為『單一Read Channel』 or 『單一Write Channel』 or 『兩者』」-->OK 6. 更改DMA blocks的名稱:在中上方視窗中的Design tag中-->對要改名的block按滑鼠右鍵-->「Block Properties...」-->更改Name欄位-->按鍵盤Enter。記得要改成跟python code中相同的DMA in/DMA out名稱,例如:axi_dma_in_0、axi_dma_out_0 ::: 10. HDL Wrapper:在中上的視窗中點選Sources的tab-->展開Design Sources-->對design_1 (design_1.bd)按滑鼠右鍵-->Create HDL Wrapper...-->OK-->確認Tcl console(位於底下視窗)中有<br/><font color=blue>"add_files -norecurse /home/ubuntu/course-lab_1/vivado/vivado.gen/sources_1/bd/design_1/hdl/design_1_wrapper.v"</font><br/>這行產生 11. Synthesis + Placement + Routing + Generate Bit-stream:要先經過Synthesis + Placement + Routing的步驟,最後才能產生bit-stream file給FPGA用。Vivado IDE提供一鍵自動執行這些步驟:在工具列按下![](https://hackmd.io/_uploads/HkIRh7Qxa.png)即可-->按下Yes-->調整Number of jobs的數目 [註5]-->OK-->等到"Bitstream Generation Completed"的視窗出現,即可直接關閉此視窗並完成bitstream的產生 > [註5] Number of jobs越多則跑得越快 [(資料來源)](https://github.com/bol-edu/course-lab_1/blob/2022.1/2022.1-Workbook-Lab1.pdf) 12. 可在底下的路徑中找到.bit及.hwh的檔案: ```console cd ~/course-lab_1 cp ./vivado/vivado.runs/impl_1/design_1_wrapper.bit ./Multip2Num.bit cp ./vivado/vivado.gen/sources_1/bd/design_1/hw_handoff/design_1.hwh ./Multip2Num.hwh ``` In general, 可寫成下列pseudo command: ```console cd ~/course-lab_??? cp ./vivado_???/vivado_???.runs/impl_1/design_1_wrapper.bit ./?????.bit cp ./vivado_???/vivado_???.gen/sources_1/bd/design_1/hw_handoff/design_1.hwh ./?????.hwh ``` 3. **onlineFPGA:** <!-- 112.10.3補上 --> > Reference: [OnlineFPGA 系統使用者手冊](https://drive.google.com/file/d/1Vx0e3m9EviOuqPVhowYVbgVunZxD828i/view) 1. 使用MobaXterm將`Multip2Num.bit`以及`Multip2Num.hwh`下載到本機端 2. 租借onlineFPGA board 3. 複製租借到的板子的IP+port (`140.XXX.XXX.XXX:XXXX`),在瀏覽器上輸入網址"`http://140.XXX.XXX.XXX:XXXX`" 4. 輸入租借到的密碼 5. 上傳 `Multip2Num.bit`、`Multip2Num.hwh` 以及 `Multip2Num.ipynb` 6. 若有其他需要上傳的檔案(例如data)也上傳 7. 滑鼠左鍵點擊`Multip2Num.ipynb`進入 8. 執行程式碼並確認remote FPGA的執行結果正確 9. 歸還onlineFPGA board <br/> <!-- 112.10.1開始 --> ## Lab 2: FIR design using HLS 此lab分為4個模式: | board | interface | |:---------:|:----------:| | ZYNQ | AXI-Master | | ZYNQ | AXI-Stream (或僅稱"Stream") | | KV260 | AXI-Master | | KV260 | AXI-Stream (或僅稱"Stream") | 1. ZYNQ+AXI-Master: * 在Vitis HLS中要注意底下事項: :::danger :warning: 由[這個討論串](https://github.com/bol-edu/HLS-SOC-Discussions/discussions/77)及[詳細資訊](https://github.com/bol-edu/HLS-SOC-Discussions/discussions/42)得知:若使用Ubuntu VM,**FIRTester.cpp**中 ```csharp // For Windows: if (system("fc .\\out.dat ..\\..\\..\\out_gold.dat")) { cout << ">> Test failed!" << endl; return 1; } ``` 要改成 ```csharp // For Linux: if (system("diff ./out.dat ../../../out_gold_unix.dat")) { cout << ">> Test failed!" << endl; return 1; } ``` ::: <!-- :::danger :warning: 由[這個討論串](https://github.com/bol-edu/HLS-SOC-Discussions/discussions/69)得知:**FIR.h**中要加入 ```csharp #include <gmp.h> #define __gmp_const const ``` to avoid `error: 'mpfr_srcptr' has not been declared.` ::: --> :::danger :warning: 由[這個討論串](https://github.com/bol-edu/HLS-SOC-Discussions/discussions/69)得知:若在co-simulation時遇到`error: 'mpfr_srcptr' has not been declared.`,則在**FIR.h**中要加入 ```csharp #include <gmp.h> #define __gmp_const const ``` ::: :::danger :warning: 由[lab2 Workbook](https://github.com/bol-edu/course-lab_2/blob/2022.1/2022.1-Workbook-Lab2.pdf)以及[這個討論串](https://github.com/bol-edu/HLS-SOC-Discussions/discussions/42)的說明:由於out_golden.dat檔案來自Windows系統,所採用的換行符號(`\r\n`)和Linux系統所採用的(`\n`)不一樣,因此需先用 ```console dos2unix -n out_gold.dat out_gold_unix.dat ``` 指令做轉換,其中語法為`dos2unix -n old_filename new_filename`,其中`-n`表示不要覆蓋掉原檔案。 [(參考資料)](https://stackdiary.com/linux-docs/dos2unix/) ::: * 在Vivado以及onlineFPGA中要注意的事項已融合在[Lab 1](#Lab-1-From-Vitis-HLS-to-onlineFPGA)的說明中 2. ZYNQ+Stream: * Vitis HLS中要注意的事項: 在此處的Directives Control是使用[Lab 1](#Lab-1-From-Vitis-HLS-to-onlineFPGA)中的「方法1(`#pragma`的方式)」,若直接使用上方ZYNQ+AXI-Master所Export的IP檔案,會在之後手動接線時發現此FIR的IP少了兩個port * Vivado中要注意的事項: 1. 此lab中使用兩個DMA:其中一個設定為「單一Read Channel」作為DMA_in;另一個設定為「單一Write Channel」作為DMA_out 2. 由[這個討論串](https://github.com/bol-edu/HLS-SOC-Discussions/discussions/66)得知,DMA_out的block應命名為axi_dma_out_1(為了與python code中的名稱相同) 3. 在此要手動接2條線: * fir_n11_strm的「pstrminput」 port要接到axi_dma_in_0的「M_AXIS_MM2S」 port * fir_n11_strm的「pstrmoutput」 port要接到axi_dma_out_1的「S_AXIS_S2MM」 port <br/> <!-- 112.10.7開始 --> ## Lab 3: FIR design using Verilog ==✏ **Prerequisite**: Introduction to AXI interface== 1. AXI4-Lite - [x] [Introduction to AXI4-Lite interface](https://www.realdigital.org/doc/a9fee931f7a172423e1ba73f66ca4081) - [x] [AXI4-Lite user guide](https://docs.xilinx.com/r/en-US/pg202-mipi-dphy/AXI4-Lite-Interface) 2. AXI-Stream - [x] [AXI-Stream Protocol Specification Documentation](https://developer.arm.com/documentation/ihi0051/latest/) - [x] [AXI-Stream user guide](https://docs.xilinx.com/r/en-US/pg256-sdfec-integrated-block/AXI4-Stream-Interface) <br/> <!-- 112.12.9~112.12.10開始 --> ## Lab 6: UART + Matrix_multiplication/Q_sort/FIR integration 1. 合成`vivado_only_UART`時有hold time violation --> 透過[這個討論區](https://github.com/bol-edu/HLS-SOC-Discussions/discussions/158)的方法得以解決 <br/> <!-- 113.1.11~開始 --> ## Lab D: SDRAM - 觀察**sdram_controller.v**的發現: 1. 「a_d」在INIT state會傳workbook中所謂的「Mode Register」(第196行),在其他時候則是傳address 2. 「refresh_ctr」是指refresh counter;「delay_ctr」是指delay counter 3. 「dq」是指output給SDRAM的data 4. 其中有一些「9:8」的記得要改成「BA」(bank address);還有其他像是「22:10」之類的也要記得改 - 記錄一下有改到的部分: 1. sdr.v第552行 ```v /////Mode_reg <= 13'b000_1_00_011_0_000;//Addr; ⬇️⬇️⬇️ Mode_reg <= Addr; // Modified ``` 2. sdram_controller.v中有註記「Modified」或「Added by us」的部分 <br/> <!-- 112.9.30 --> ## 附註:使用Ubuntu VM上傳資料夾至Github的流程 ==📌 事先準備(只需做一次):== 1. 使用瀏覽器在Github上創建一個repository,在此我把它叫作SOC-design 2. 在Ubuntu的terminal上輸入 ```console git clone https://github.com/whywhytellmewhy/SOC-design.git ``` 3. 即可在Ubuntu上產生名為SOC-design的資料夾 <br/> ==📌 每當想要上傳資料夾至Github時:== 1. 在Ubuntu上SOC-design資料夾裡面創建名為「labXX」的資料夾 2. 將檔案丟進來「labXX」資料夾中 :::info :warning: 注意:若原檔案所在之資料夾已有連結到其他人的Github,則需將資料夾「裡面」的東西複製出來到labXX才可 ::: 3. 先將terminal到SOC-design所在的資料夾: ```console cd /SOC-design ``` 1. **(建議)** 先使用git pull確保local repository與remote repository兩者同步(會merge不同之處): ```console git pull ``` 若本來就已同步則會回傳`Already up to date.` 2. 將超過100MB的.vcd檔刪除:執行 ```console source run_delete_big_files ``` :::warning 💡 此時可使用`source run_list_big_files`指令,會回傳剩下哪些超過100MB的檔案,若有出現路徑中有.git的檔案可以不用理它。 ::: 3. 使用git add: ```console git add labXX ``` :::warning 💡 此時可使用`git status`指令,會回傳有哪些檔案/資料夾被增加/刪除/修改。 ::: :::warning 💡 git add的相反指令為「git rm」 ::: 4. 接著用git commit: ```console git commit -m "一些註記(雙引號要留著)" ``` 將這些更新上傳到local repository 5. 最後用git push: ```console git push ``` 將這些更新上傳到remote repository(也就是Github的repository中)。 :::danger :warning: **在git push後若有發生error而導致沒有成功上傳到remote repository時**:即使local端刪除掉導致error的檔案並再commit,仍會將這些檔案嘗試上傳而再度error。此時可透過 1. **先備份檔案再說!!!** 2. 使用`git status`得到有多少commit沒有成功上傳,例如下圖為「**6**個」 ![](https://hackmd.io/_uploads/ryh0BtCGp.png) 3. 使用`git reset --hard HEAD~「數目」`,例如上例中為`git reset --hard HEAD~6` 4. 使用`git status`確認目前狀態為behind remote端 ![](https://hackmd.io/_uploads/H1X1tFCz6.png) 5. 使用`git pull`將remote端檔案pull到local端 6. 確認要加入的資料夾有在本地端,若無則從備份複製過來 7. 回到上方git add,重新執行上述步驟即可 ([參考資料](https://gist.github.com/cutiko/0b1615c63504a940877541362cc51211)) ::: :::success ✨:star: <font color="blue">**整理如下**</font> :star:✨ ```console cd /SOC-design git pull git add labXX git commit -m "一些註記(雙引號要留著),例如「add labXX」" git push ``` ::: <!-- 113.1.11 --> ## 附註(二):多人協作Github的流程 參考資料:https://myapollo.com.tw/blog/git-tutorial-collaboration/ ==📌 事先準備(只需做一次):== 1. 使用瀏覽器在Github上的repository新增協作者,如下圖的位置 ![image](https://hackmd.io/_uploads/BJWlCfaOp.png) 2. 協作者在Ubuntu的terminal上輸入 ```console git clone https://github.com/whywhytellmewhy/SOC-design.git ``` 產生名為SOC-design的資料夾 3. 將terminal移到SOC-design所在的資料夾: ```console cd /SOC-design ``` 4. (建議) 新增分支以避免與main分支衝突: ```console git checkout -b "???" ``` 其中`???`填入新分支的名稱,例如`final_project/myname` :::warning 💡 此時可使用`git branch`指令,會回傳所有分支,**其中開頭有`*`的為目前所在分支**。 ::: <br/> ==📌 每當**協作者**想要上傳資料夾至Github時:== 1. 先將terminal移到SOC-design所在的資料夾 ```console cd /SOC-design ``` 2. 使用`git branch`指令,會在螢幕上回傳所有分支,其中開頭有`*`的為目前所在分支。**確認所在的分支是否正確,若不正確,則使用 `git checkout ???`(???填入分支的名稱)轉換過去** 3. **<font color="red">刪除超過100MB的波形檔(.vcd):</font>** 執行底下的指令即可 ```console source run_delete_big_files ``` :::warning 💡 此時可使用`source run_list_big_files`指令,會回傳剩下哪些超過100MB的檔案,若有出現路徑中有.git的檔案可以不用理它。 ::: 4. 依序執行 ```console git pull origin ???(分支名稱) git add . git commit -m "一些註記(雙引號要留著),例如「add labXX」" ``` 5. 最後執行 ``` git push origin ??? ``` 其中`???`填入新分支的名稱,不需要加雙引號 :::danger :warning: **在git push後若有發生error而導致沒有成功上傳到remote repository時**: 1. **先備份檔案再說!!!** 2. 使用`git reset --hard HEAD` 3. 使用`git pull`將remote端檔案pull到local端 4. 確認要加入的資料夾有在本地端,若無則從備份複製過來 5. 回到上方git add,重新執行上述步驟即可 ([參考資料](https://gist.github.com/cutiko/0b1615c63504a940877541362cc51211)) ::: 6. 協作者使用瀏覽器登入Github,會看到類似底下提示 ![messageImage_1704977468011](https://hackmd.io/_uploads/BkPuNvT_p.jpg) 點進去後可以在右側欄位選擇Reviewer,接著點選綠色底的<font style="background-color: green; color: white"> Create pull request </font>,再點選<font style="background-color: green; color: white"> Merge pull request </font>。 <br/> ## Reference 1. [Hackmd.io支援的特殊格式](https://hackmd.io/s/features-tw#%E6%AA%A2%E8%A6%96) 2. [How to Commit and Push Changes from Ubuntu 18.04 to GitHub](https://www.liquidweb.com/kb/committing-pushing-github-ubuntu-18-04/) 3. [支援的程式碼種類](https://support.codebasehq.com/articles/tips-tricks/syntax-highlighting-in-markdown) 4.