Z2PCBA@gmail.com +66-897725026
เริ่มต้นทำความรู้จักกับ ZYNQ VIVADO ของ Xilinx – Basic PL
Sunday February 25th, 2018
0

Zynq Tutorial : Basic PL

เริ่มต้นทำความรู้จักกับ ZYNQ Vivado ของ Xilinx – Basic PL โดยเป็นตัวอย่างของการใช้งาน โดยเริ่มต้นด้วยการพัฒนาโปรแกรมควบคุมไฟกระพริบ โดยใช้ภาษา VHDL
Zynq เป็น SOC ของ xilinx ที่มีการรวมของ PS (Processing System) และ PL (Programmable Logic) เข้ามาไว้ด้วยกัน จึงมีความซับซ้อนในการใช้งานเพิ่มขึ้น โดยการสอน Zynq ในบทความนี้จะใช้โปรแกรม Vivado ซึ่งเป็น tool อันหนึ่งของ Xilinx ที่ใช้เขียน FPGA ทั้งหลาย ซึ่งรวมถึงตระกูล ZYNQ ด้วย

ในบทความนี้จะเป็นการแนะนำการใช้งาน Zynq โดยมาเริ่มจากฝั่ง PL ก่อน

  1. สร้างโปรเจคใหม่ (Create project)
    1. เริ่มต้นการเปิดใช้โปรแกรม โดยโปรแกรมที่จะใช้ในตัวอย่างนี้ใช้เป็น version 2017.4 ทำการเลือก “create project”

    2. ตั้งชื่อโปรเจคและตำแหน่งที่จะเก็บโปรเจคของเราไว้ หลังจากเลือกเสร็จเรียบร้อยแล้ว ให้กดปุ่ม “next” เพื่อดำเนินการในขั้นต่อไป

    3. ทำการเลือกชนิดของโปรเจคเป็น “RTL Project” เพื่อให้สามารถสร้างได้ระดับ RTL ตามรูปครับ

    4. เราสามารถนำไฟล์ที่เคยพัฒนาไว้แล้วเข้ามาในใช้โปรเจคได้(import) แต่เนื่องจากตัวอย่างนี้เรายังไม่มีไฟล์ที่จะนำเข้า เราจึงข้ามส่วนนี้ไป และส่วนด้านล่าง จะมีช่อง “Target language” ซึ่งตรงนี้จะเป็นส่วนให้เราเลือกเป็นภาษาที่เราต้องการจะเขียน ในที่นี้ผมขอใช้ VHDL นะครับ แล้วก็กดปุ่ม “next” เพื่อไปยังขั้นตอนต่อไป

    5. การใส่ข้อบังคับต่างๆ(Constraints) ตัวอย่างเช่น การ mapping pin ต่างๆของชิป(Chip) หรือ พวก Timing Constraints ซึ่งเรายังไม่มีเราก็กดปุ่ม “next” เพื่อไปขั้นตอนต่อไป

    6. การเลือก FPGA ตัวที่เราจะใช้งาน ซึ่งตรงนี้ต้องระบุให้ตรงกับตัวฮาร์ดแวร์ที่เราต้องใช้งานจริง โดยในที่นี้ผมใช้ “Zynq xc7Z020clg400-1” ซึ่งถ้าใครสงสัยว่าต้องระบุเป็นตัวไหน ก็ให้ดูรหัสบนตัวชิปที่กำลังจะพัฒนาครับ จะมีระบุไว้

    7. หน้าสรุปรายละเอียดของโปรเจคที่เรากำลังจะสร้างครับ หากข้อมูลตรงตามที่เราต้องการแล้ว ให้กดปุ่ม “Finish” เพื่อทำการสร้างโปรเจคใหม่ได้เลยครับ

    8. หลังจากสร้างโปรเจคเสร็จแล้วเราก็จะได้โปรเจคหน้าตาดังรูป ซึ่งเป็นการสิ้นสุดขั้นตอนการสร้างโปรเจคใหม่ ขั้นตอนต่อไปจะเป็นการเขียนตัวโค้ด(code)สำหรับควบคุมการทำงาน เพื่อให้ Zynq นั้นสามารถทำงานตามที่เราต้องการนะครับ

  2. Blinking LED Project
    โปรเจคตัวอย่างที่เราจะมาพัฒนากันในบทความนี้คือโปรเจคทำไฟ LED กระพริบ(Blinking LED) ซึ่งถ้าเปรียบเทียบกับ การเริ่มต้นพัฒนาซอฟท์แวร์ ก็คล้ายจะเป็นการเขียนโปรแกรมให้ได้ผลลัพท์แสดงค่าออกมาเป็น “Hello world” นั่นเองครับ
    เราจะมาเริ่มเขียนโปรแกรมโดยเริ่มต้นรู้จักการทำงานในภาพรวมกันก่อน โดยโปรแกรมที่เขียนกันในบทความนี้จะทำงานโดยใช้วงจรที่รับข้อมูลเข้า(input) เป็น clock แล้วจึงทำการหารค่าความถี่ clock แล้วค่อยนำค่าสัญญาณที่หารออกมาได้นั้นไป ขับ LED เพื่อให้เกิดไฟกระพริบ ดังรูป

  3. Create Design Files

    1. เริ่มต้นขั้นตอนจากการสร้างไฟล์ใหม่ที่จะทำการเขียนโปรแกรมลงไป โดยให้ไปกดเลือกเมนู “Project” ด้านซ้ายบน แล้วหน้าต่างตรงกลางจะเป็นหน้าต่างดังรูปด้านล่าง จากนั้นให้ทำการกดปุ่ม +

    2. ทำการเลือก “add or create design sources” เพื่อสร้างไฟล์ design

    3. เลือก “Create File” เพื่อสร้างไฟล์ใหม่ ส่วนใครมีไฟล์อยู่แล้ว ต้องการนำเข้ามาใช้ก็ทำการกด “Add Files” ก็ได้

    4. เลือกภาษาและชื่อไฟล์พร้อมตำแหน่งที่จะทำการบันทึกไฟล์นี้ไว้ ก็เลือกได้ตามสะดวกเลยครับ(ในตัวอย่างนี้ตั้งชื่อไฟล์ว่า Blinking_LED)

    5. หน้าถัดไปจะบอกว่า design ตัวนี้จะมีพอร์ต(port) อะไรที่ต้องการใช้งานบ้าง ซึ่งในที่นี้ให้กดปุ่ม “ok” เพื่อข้ามไปก่อนได้เลย เพราะเราสามารถไปกำหนดทีหลังได้เหมือนกัน (แต่อย่าลืมกำหนดชื่อ “Entity name” นะครับ เพราะเป็นชื่อที่บอกว่า “Design Module” เราชื่ออะไร)

    6. เมื่อเสร็จสิ้นก็จะมีไฟล์ ใหม่เพิ่มเข้ามาในโปรเจค(project) ดังรูป ซึ่งก็คือไฟล์ที่เราเพิ่งสร้างในขั้นตอนที่แล้วนั่นเองครับ(ในที่นี้มีชื่อไฟล์ว่า “Blinking_LED.vhd”)

    7. ทำการเปิดไฟล์แล้วเริ่มทำการเขียนโปรแกรมกันครับ โดยในบทความนี้ใช้โค้ด(code) ตามตัวอย่างด้านล่าง
      library IEEE;
      use IEEE.STD_LOGIC_1164.ALL;
      use IEEE.STD_LOGIC_UNSIGNED.ALL;
      
      entity Blinking_LED is
          port (
              iRESET_L      	   	: in STD_LOGIC;
              iPL_CLK         		: in STD_LOGIC;   
              oLED		      	: out STD_LOGIC
          );
      end Blinking_LED;
      architecture Behavioral of Blinking_LED is
          signal sBLINKING_CNT  : std_logic_vector(27 downto 0):= x"0000000";	
      begin
      	oLED  		<= sBLINKING_CNT(25);
      	
        U_Blinking_LED : process (iPL_CLK,iRESET_L) 
          begin 
            if rising_edge(iPL_CLK) then
      		if iRESET_L = '0' then
      			sBLINKING_CNT <= x"0000000";
      		else
      			sBLINKING_CNT   <= sBLINKING_CNT + '1';
      		end if;
            end if ;
        end process U_Blinking_LED;
        
      end Behavioral;
      

      โดยรายละเอียดของโปรแกรมด้านบนที่เราเขียนไปนั้น จะขออธิบายในบทหน้านะครับ ไม่งั้นจะยาวมาก ตอนนี้ให้เข้าใจเพียงว่า เป็นวงจรไฟกระพริบ LED โดยการหารความถี่ โดยที่ใช้พอร์ต port ดังนี้

      iRESET_L เป็น input reset เอาไว้ reset วงจรที่เราเขียนนี้ ซึ่งจะ Active Low นะครับ
      iPL_CLK เป็น clock input ที่จะเอามาหารความถี่
      oLED เป็น output ที่จะเอาไว้ขับ LED
  4. Create Design
    โดยธรรมดาแล้วเมื่อเราเขียนรายละเอียดตัวโปรแกรม(coding) เสร็จ เราจะต้องมากำหนดรายละเอียดในส่วนของ "Create Design" ซึ่งในส่วนนี้จะเป็นเหมือน "schematic" ที่จะทำการวาดวงจรที่เราออกแบบในส่วนย่อยๆมาต่อกัน ซึ่งในการทำงานจริงจะไม่มีใครสร้างวงจรด้วยไฟล์ๆเดียวแน่ๆ แต่จะเป็นการนำโปรแกรมจากหลายๆส่วน มาเชื่อมต่อเข้าด้วยกัน การเชื่อมต่อนี้ก็จะทำโดยการกำหนดภายในส่วน "Create Design" นี้ครับ แต่สำหรับบทความนี้ของเราวงจรนั้นมีแค่วงจรเดียว ดังนั้นส่วนการสร้าง Block Design ก็อาจจะไม่เห็นภาพเท่าไหร่ ขอยกรายละเอียดส่วนนี้ไว้อธิบายเพิ่มเติมใน Tutorial ต่อๆไปละกันนะครับ
  5. RTL Analysis
    เมื่อเราสร้างวงจรเสร็จเรียบร้อย ต่อไปก็จะเป็นส่วนการตรวจสอบวงจร "Block Design" ของเรา ว่าสามารถจะนำมาสร้างเป็นวงจรได้จริงหรือไม่ เขียนถูกต้องหรือเปล่า ซึ่งเมื่อเราเขียนวงจรเสร็จ ก็ต้องทำการตรวจสอบวงจรก่อนเสมอว่าเราได้ทำถูกจริงหรือไม่ โดยในการตรวจสอบให้ทำการกดฟังก์ชั่น "RTL Analysis" แล้วจากนั้นจึงกด "Schematic" เพื่อให้สังเคราะห์วงจรที่เราเขียนออกมา


  6. Create Constraints Files

    1. หลังจากที่ทำการตรวจสอบวงจรที่เราเขียนไว้นั้นว่าถูกต้องแล้ว ต่อไปเราก็จะมาทำการสร้างไฟล์ Constraints ซึ่งจะเป็นไฟล์ที่บอกว่าพอร์ต(port) แต่ละพอร์ต(port) นั้นจะไปเชื่อมต่อ(mapping) ไว้ที่ขา(pin)ไหนของ ZYNQ โดยในวงจรของเรานั้นจะมีพอร์ต(port) ทั้งหมด 3 อันให้เชื่อม ก็ให้เราเชื่อมต่อ(mapping) ขาต่างๆ ซึ่งอันนี้ขึ้นกับบอร์ดที่เราใช้อยู่ว่าควรจะต่ออย่างไรนะครับ โดย

      iRESET_L ให้ map ที่ขาที่ต่อกับ Switch และเมื่อกดจะเป็น Logic ‘0’
      IPL_CLK ให้ทำการต่อขา Clock input
      oLED ต่อกับขาที่จะไปขับ LED

      เพิ่มเติม: ถ้าบอร์ดไม่มี External clock จากภายนอกเข้า PL ก็อาจจะต้องรอไปทำ Tutorial ถัดๆไปที่มีการทำงานร่วมกัน PS ก่อนนะครับ ถึงจะทำงานได้ โดยวิธีนั้นในที่นี้จะใช้ "IO Planning" นะครับ เพราะง่ายกับมือใหม่ โดยให้ไปกดที่ฟังก์ชั่น "Layout IO Planning" จะได้หน้าตาดังรูปด้านล่าง

    2. จากนั้นจะให้ดูหน้าต่างด้านล่างจะเห็นหน้าตาดังรูป ซึ่งก็คือจะระบุพอร์ต(port) ต่างๆ ที่เราได้กำหนดไว้ โดยในที่นี้สิ่งสำคัญที่ต้องกำหนดให้ถูกต้องคือ "Package Pin" ซึ่งเป็นการเชื่อมต่อ(mapping) ว่าเราจะต่อพอร์ต(port) ต่างๆไว้ที่ขา(pin) ไหน อันนี้ขึ้นอยู่กับตัวบอร์ด ส่วนอีกอันหนึ่งก็คือ "IO Std" ที่จะเป็นการตั้งค่า "voltage level" ที่จะใช้ของพอร์ต(port) นั้นๆว่าเท่าไหร่ อันนี้ก็ต้องกำหนดให้ถูกนะครับ ซึ่งโดยส่วนใหญ่บอร์ดทดลองนั้นจะใช้ "LVCMOS33" ครับ

    3. จากนั้นให้กดปุ่ม "save" จะพบว่ามีไฟล์เพิ่มมา 1 ไฟล์ ดังรูปด้านล่างครับ ก็จะเป็นการเสร็จสิ้นส่วนของการเชื่อมต่อ(Mapping) ครับ

  7. Synthesis
    ทำการตรวจสอบวงจรที่ออกแบบด้วย "Synthesis" โดยให้กดปุ่มดังรูป

    ซึ่งถ้าไม่มีอะไรผิดพลาด ก็จะเสร็จสมบูรณ์โดยกินเวลาเพียงเล็กน้อย จากนั้นก็ให้ทำการกดปุ่ม "Run implementation" ได้เลยครับ

  8. Implementation
    เป็นส่วนที่นำวงจรที่ได้หลัง RTL มาจำลองการทำงานโดยการใส่ลงไปในชิป(Chip) ที่เราได้เลือกไว้ เพื่อดูว่าสามารถนำโปรแกรมที่เขียนไว้มาใส่ในอุปกรณ์ได้หรือไม่ เพราะวงจรที่เราออกแบบมาอาจจะใหญ่เกินไปสำหรับชิป(Chip) ตัวนั้นๆ ได้ ซึ่งถ้าทำตาม Tutorial ข้างต้นมาตามลำดับก็น่าจะรันโปรแกรมได้โดยไม่ติดปัญหานะครับ เพราะรายละเอียดของโปรแกรมในบทความนี้ไม่ซับซ้อนมากนัก โดยการสั่ง "run" นั้นนอกจากกดหลังจากทำการ Synthesis แล้วเสร็จ ก็สามารถเลือกกดที่ฟังก์ชั่น "Implementation" ด้านล่างซ้ายได้ดังรูป อีกทางหนึ่งครับ

  9. โดยเมื่อเสร็จแล้วจะได้ปรากฎหน้าต่างดังรูปด้านล่าง สามารถเลือก "Generate Bitstream" เพื่อทำการสร้างไฟล์ที่จะใช้โปรแกรมลงอุปกรณ์จริงครับ

    เพิ่มเติม: ก่อนจะเริ่มส่วนนี้อย่าลืมตรวจสอบว่าเราได้ใส่ไฟล์ "Constraint" ไปแล้ว ไม่งั้นโปรแกรมอาจจะรันไม่ผ่านนะครับ

  10. Program and DEBUG

    1. หลังจากที่เราออกแบบและตรวจสอบเสร็จแล้ว เราก็จะมาทำการโปรแกรม(program) วงจรของเราลงบนชิพ(Chip) จริง เพื่อดูว่าวงจรที่เราได้ออกแบบ(Design) ไว้แล้วนั้นได้ผลจริงตามที่เราต้องการหรือไม่ ซึ่งจริงๆแล้ว วงจรที่ออกแบบมาและได้ทำการตรวจสอบผ่านหมด ก็ไม่จำเป็นที่จะเป็นไปตามที่โค้ด(code) หรือวงจรออกแบบมาก็ได้ หรือว่าวงจรที่เคยทำงานได้บนบอร์ดๆหนึ่งด้วยโค้ด(code) เดียวกัน ก็อาจจะไม่สามารถทำงานได้เมื่ออยู่อีกบอร์ดนึงก็ได้ ซึ่งมันจะต่างจาก Software ที่ถ้ามันเคยทำงานได้แล้วละก็ ส่วนใหญ่จะทำงานได้ใน บอร์ดอื่นๆที่ใช้ชิป(Chip) ตัวเดียวกัน เว้นแต่ว่าเราต่อ pin ผิด
      วิธีการคือให้เสียบตัว "JTAG programming" ลงไปบนบอร์ดก่อนครับ จากนั้นให้ไปเลือกที่หัวข้อ "PROGRAM AND DEBUG" แล้วเลือก "open target" เพื่อให้ jtag ตรวจสอบการติดต่อสื่อสารระห่าง JTAG และ ZYNQ

    2. ซึ่งถ้าต่อได้โดยไม่ติดปัญหา จะได้ผลดังรูปด้านล่าง ส่วนถ้าใครไม่ได้ผลดังรูป ก็ให้ลองเช็คเรื่องการต่อ JTAG เลยครับ

    3. เมื่อเราเชื่อมต่อได้แล้ว ให้เราทำการกด "Program device" ตามรูปด้านล่างได้เลยครับ จะเป็นการ Program ZYNQ จากนั้นให้รอจนกระทั่งทำการโปรแกรมลงชิปเสร็จ มันจะทำการ reset บอร์ดโห้เราเองอัตโนมัติ

    4. ซึ่งในตรงนี้ถ้าไม่มีอะไรผิดพลาด เราก็จะพบว่าบอร์เรานั้นได้กระพริบ LED แล้วซึ่งเร็วช้า ขึ้นกับ Speed Clock ของเราครับ

ก็จบไปแล้วนะครับกับ Basic ZYNQ ทางด้าน PL ในส่บทความต่อไปจะเป็นการพูดถึงการใช้ PS นะครับ

เพิ่มเติม: Tutorial นี้ไม่ได้ใช้ PS ดังนั้นจะใช้สำหรับ debug mode เท่านั้น ไม่สามารถนำไปใช้จริงได้