การใช้งานซอฟต์แวร์ Gowin IDE Standard Edition สำหรับบอร์ด Sipeed Tang Nano#

Keywords: Gowin FPGA, Gowin EDA Software, Sipeed Tang Nano


บอร์ด Sipeed Tang Nano#

บริษัท Sipeed ได้พัฒนาบอร์ด FPGA ในกลุ่มที่เรียกว่า Sipeed Tang FPGA และมีบอร์ดรุ่นแรกคือ Sipeed Tang Nano ที่มีชิป FPGA เป็นรุ่น GW1N-1-LV (LittleBee FPGA family, 55nm LP technology) ของบริษัท Guangdong Gowin Semiconductor (GOWINSEMI) จากประเทศจีน และมีราคาไม่แพง (ราคาไม่เกิน $6 หรือ 250 ~ 300 บาท โดยประมาณ) เหมาะสำหรับผู้เริ่มต้นเรียนรู้เกี่ยวกับ FPGA หลังจากนั้นจึงมีบอร์ด FPGA รุ่นอื่นของ Sipeed ทยอยออกมาสู่ตลาด

คำแนะนำ: สำหรับผู้ที่สนใจจะหาซื้อบอร์ด Tang Nano (GW1N-1 QN48) มาลองใช้งาน แนะนำให้เลือกใช้ Tang Nano 1K (GW1NZ-1 QN48) แทน เนื่องจากบอร์ด Tang Nano ไม่มีการผลิตและจำหน่ายแล้ว

รูป: บอร์ด Sipeed Tang Nano FPGA

รูป: แผนผังแสดงตำแหน่งขาต่าง ๆ ของบอร์ด Sipeed Tang Nano FPGA

รูป: ตัวอย่างบอร์ด Tang Nano Series: 1K / 4K / 9K / 20K (Source: Sipeed)

รูป: Tang Primer 20K - Core Board (Source: Sipeed)

รูป: Tang Primer 20K - Lite Board (Source: Sipeed)

 

ข้อมูลเกี่ยวกับบอร์ด Sipeed Tang Nano (Released: October 2019)

  • FPGA: Gowin GW1N-1-LV
    • LUTs: 1,152
    • FFs: 864
    • Block SRAM: 72Kbits
    • User Flash: 96Kbits
    • PLL: 1
    • I/O banks: 4
    • Core Voltage: 1.2V
    • IC Package: QN48
  • Storage: Quad-SPI PSRAM, 64Mbits (8MB), 3.3V
  • USB Type-C port
  • WCH CH552T: USB-JTAG & USB-Serial
  • 3x RGB LED (active-low):
    • LED_G: IOB7A / 16 pin
    • LED_B: IOB10A / 17 pin
    • LED_R: IOB10B / 18 pin
  • 2x User Buttons:
    • Button A: IOB3B / 14 pin
    • Button B: IOB6B / 15 pin
  • 24MHz Crystal Oscillator:
    • XTAL_IN: GCLKT_2 / 35 pin
  • Standard 40-pin RGB LCD interface
  • Breadboard-compatible Pin Headers (2x20 pins)


การใช้งานซอฟต์แวร์ Gowin EDA สำหรับบอร์ด Sipeed Tang Nano#

บริษัท Gowin ได้เปิดให้ผู้ใช้สามารถดาวน์โหลดซอฟต์แวร์ Gowin (EDA) IDE เพื่อนำใช้ในการออกแบบวงจรดิจิทัลสำหรับชิป FPGAs แบ่งเป็น 2 เวอร์ชัน สำหรับระบบปฏิบัติการ Windows และ Linux

  • Gowin Standard Edition: ผู้ใช้จะต้องลงทะเบียนและเข้าไปกรอกข้อมูลบนเว็บไซต์ของบริษัท เพื่อขอรับไฟล์ลิขสิทธิ์ (Apply License File) ทางอีเมล์ และทดลองใช้ซอฟต์แวร์ได้ฟรี เป็นระยะเวลา 1 ปี ถ้าเลือกใช้บอร์ด Sipeed Tang Nano จะต้องเลือกใช้เวอร์ชันนี้
  • Gowin Education Edition: เป็นซอฟต์แวร์เหมือน Gowin EDA ไม่ต้องใช้ไฟล์ลิขสิทธิ์ใด ๆ แต่จะใช้ได้กับชิป Gowin FPGAs บางรุ่นเท่านั้น ถ้าเลือกใช้บอร์ด เช่น Sipeed Tang Nano 1K / 4K / 9K / 20K (เรียงตามความจุเชิงลอจิกจากน้อยไปมาก) และ Tang Primer 20K ก็สามารถใช้ซอฟต์แวร์ในเวอร์ชันนี้ได้

รูป: Gowin FPGA Design Flow (Source: Gowin Semiconductor)

ถัดไปเป็นการทดลองใช้งานซอฟต์แวร์ Gowin IDE - Standard Edition สำหรับระบบปฏิบัติการ Linux (Ubuntu Desktop 22.04 LTS)

ไฟล์สำหรับการติดตั้งใช้งานคือ Gowin_V1.9.9Beta-2_linux.tar.gz และนำมาแตกไฟล์ จะได้ไดเรกทอรีย่อยชื่อ ./IDE และ ./Programmer

$ wget -c https://cdn.gowinsemi.com.cn/Gowin_V1.9.9Beta-2_linux.tar.gz
$ tar xvfz Gowin_V1.9.9Beta-2_linux.tar.gz
$ cd Gowin_V1.9.9Beta-2_linux/

เปิดใช้งานโปรแกรมโดยทำคำสั่ง

$ ./IDE/bin/gw_ide &

เริ่มต้นจะมีการตรวจสอบไฟล์ลิขสิทธิ์โดย Gowin License Manager เมื่อผู้ใช้ได้รับไฟล์ .lic ผ่านช่องทางอีเมล์มาแล้ว ให้คลิกเลือกไฟล์ดังกล่าว และกดปุ่ม Check หากตรวจสอบแล้วถูกต้อง ให้กดปุ่ม Save

รูป: การตรวจสอบไฟล์ลิขสิทธิ์ก่อนใช้งานโดย Gowin License Manager

เปิดใช้งาน Gowin EDA Standard Edition ภายใต้การทำงานของ Ubuntu 22.04 LTS

คลิกเลือกทำขั้นตอน New Project ...

รูป: หน้าต่างเริ่มต้น (Start Page) สำหรับการใช้งาน Gowin IDE

รูป: กดปุ่ม OK เพื่อสร้างโปรเจกต์ใหม่สำหรับการออกแบบวงจรดิจิทัลเพื่อใช้งานกับชิป FPGA

รูป: ระบุไดเรกทอรีของโปรเจกต์ใหม่ และชื่อของโปรเจกต์ (ในรูปตัวอย่าง ได้ใช้ชื่อโปรเจกต์ led_blink)

รูป: เลือกชิป FPGA ที่ต้องการใช้งาน (เลือก Device: GW1N-1, IC Package: QFN48, Speed: C6/I5, Voltage: LV)

รูป: สรุปรายละเอียดในภาพรวม ก่อนการสร้างโปรเจกต์ใหม่

รูป: หน้าต่างหลักของ Gowin IDE หลังจากที่ได้มีการสร้างและเปิดโปรเจกต์ใหม่แล้ว

 

ถัดไปให้เพิ่มไฟล์ Source Code เช่น VHDL หรือจะเป็น Verilog / SystemVerilog แต่ในตัวอย่างนี้ จะเป็นโค้ดภาษา VHDL ที่ทำให้ RGB LED ซึ่งมีขาควบคุม 3 ขา มีแสงสีแดง เขียว และ น้ำเงิน ติดดับสลับกันไปตามลำดับ

รูป: การสร้างไฟล์ใหม่ โดยเลือกเป็นไฟล์ VHDL ใส่ไว้ในโปรเจกต์ แล้วเพิ่มโค้ดตามตัวอย่าง

------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
------------------------------------------------------------------

entity LED_BLINK is
  generic (
     CLK_HZ : natural := 24000000
  );
  port(
     CLK  : in std_logic; -- system clock
     nRST : in std_logic; -- global asynchronous reset (active-low)
     LEDS : out std_logic_vector(2 downto 0) -- RGB LED Pins
  );
end LED_BLINK;

architecture SYNTH of LED_BLINK is
  constant COUNT_PERIOD : integer := CLK_HZ/10;
  subtype count_t is integer range 0 to (COUNT_PERIOD-1); 
  signal  count : count_t := 0;
  signal  leds_reg : std_logic_vector(2 downto 0);
  signal  shift_en : std_logic;

begin

  LEDS <= not leds_reg; -- use the register's bits (inverted) for LEDs

  process (nRST, CLK) begin
    if nRST = '0' then
      count <= 0;
      shift_en <= '0';
    elsif rising_edge(CLK) then
      -- check whether the counter reaches the max. value.
      if count = (COUNT_PERIOD-1) then 
        count <= 0;       -- reset the counter.
        shift_en <= '1';  -- enable register shift.
      else
        count <= count+1; -- increment counter by 1.
        shift_en <= '0';  -- disable register shift.
      end if;
    end if;
  end process;

  process (nRST, CLK) begin
    if nRST = '0' then
      leds_reg <= "001"; -- initialize the RGB_LED register
    elsif rising_edge(CLK) then
      if  shift_en='1' then -- register shifting is enabled.  
        -- shift left
        leds_reg <= leds_reg(1 downto 0) & leds_reg(2); 
      end if;
    end if;
  end process;

end SYNTH;

รูป: ทำขั้นตอน Synthesize เพื่อแปลงโค้ด VHDL ให้เป็นวงจรดิจิทัลในระดับล่าง โดยใช้ทรัพยากรภายในชิป FPGA

รูป: เพิ่มไฟล์ Physical Constraint File (.cst) สำหรับวงจรดิจิทัลในโปรเจกต์ เพื่อกำหนดคุณสมบัติหรือการเลือกใช้ขา I/O Pins ของชิป FPGA

รูป: ตัวอย่างข้อความในไฟล์ led_blink.cst เพื่อเลือกใช้ขา I/O Pins

IO_LOC "CLK"     35;
IO_LOC "nRST"    15;
IO_LOC "LEDS[0]" 16;
IO_LOC "LEDS[1]" 17;
IO_LOC "LEDS[2]" 18;

IO_PORT "CLK" IO_TYPE=LVCMOS33; 
IO_PORT "nRST" IO_TYPE=LVCMOS33 PULL_MODE=UP; 
IO_PORT "LEDS[0]" IO_TYPE=LVCMOS33 DRIVE=8; 
IO_PORT "LEDS[1]" IO_TYPE=LVCMOS33 DRIVE=8; 
IO_PORT "LEDS[2]" IO_TYPE=LVCMOS33 DRIVE=8;

จากนั้นให้ทำขั้นตอน Synthesize และตามด้วย Place & Route แล้วลองดูรายงานสรุปผลการดำเนินการ เช่น Synthesis Report และ Place & Route Report

รูป: ตัวอย่างตารางแสดงข้อมูลเกี่ยวกับการใช้ทรัพยากรของชิป FPGA ในการสร้างวงจรดิจิทัล

ถ้าต้องการกำหนดเงื่อนไขเชิงเวลาสำหรับการสังเคราะห์และวิเคราะห์วงจรดิจิทัล เช่น กำหนดคาบเวลาหรือความถี่ของสัญญาณอินพุต CLK ของวงจร ก็มีตัวอย่างดังนี้

เลือกจากเมนูคำสั่ง Tools > Timing Constraints Editor

รูป: เปิดใช้งาน Timing Constraints Editor

รูป: คลิกเลือกสัญญาณ CLK จากรายการชื่อสัญญาณอินพุต-เอาต์พุตของวงจร

รูป: กำหนดคาบสัญญาณ (Period) หรือ ความถี่ (Frequency) ของสัญญาณ CLK (ในตัวอย่างได้กำหนดความถี่ไว้ 25 MHz ซึ่งสูงกว่า 24MHz Crystal Oscillator บนบอร์ด Tang Nano)

จากนั้นให้ทำขั้นตอน Synthesize และ Place & Route ใหม่อีกครั้ง แล้วเปิดดูไฟล์รายงาน Timing Analysis Report เช่น ดูว่าวงจรดิจิทัลที่ได้สามารถทำงานด้วยความถี่สูงกว่า 25MHz หรือไม่ และได้ความถี่สูงสุดเท่าไหร่ (Max Frequency: Fmax)


การอัปโหลดไฟล์ Bitstream โดยใช้โปรแกรม OpenFPGALoader#

จากการทดลองใช้งานซอฟต์แวร์ของ Gowin ในเวอร์ชัน Linux ในขั้นตอนการอัปโหลดไฟล์ Bitstream ไปยังบอร์ด FPGA โปรแกรม Gowin Programmer ไม่สามารถมองเห็นบอร์ด Tang Nano ที่เชื่อมต่อกับคอมพิวเตอร์ผู้ใช้ ในกรณีนี้จึงแนะนำให้ใช้ซอฟต์แวร์ Open Source ที่สามารถใช้งานแทนได้คือ openFPGALoader

อีกทางเลือกหนึ่งคือ การดาวน์โหลดโปรแกรม Gowin Programmer for Windows โดยสำเนาไฟล์ .fs ในโปรเจกต์ จาก Ubuntu มาใช้ใน Windows

ขั้นตอนการติดตั้ง openFPGALoader สำหรับ Linux / Ubuntu โดยการคอมไพล์จากซอร์สโค้ด มีดังนี้

$ sudo apt install -y libftdi1-2 libftdi1-dev \
  libhidapi-hidraw0 libhidapi-dev libudev-dev \
  zlib1g-dev cmake pkg-config make g++

$ git clone https://github.com/trabucayre/openFPGALoader.git
$ cd openFPGALoader && mkdir -p build && cd build
$ cmake ../ && cmake --build .
$ sudo make install

# Show the openFPGALoader version (v0.10.0)
$ openFPGALoader -V

ขั้นตอนถัดไปคือ การเชื่อมต่อบอร์ด Tang Nano เข้ากับพอร์ต USB ของคอมพิวเตอร์ผู้ใช้ แล้วทำคำสั่งใน Linux เพื่อตรวจสอบดูว่า พบอุปกรณ์หรือไม่ (มองเห็นเป็นอุปกรณ์ USB Device ที่ทำหน้าที่ "เลียนแบบ" ชิป FTDI FT2232)

$ lsusb | grep "0403:6010"

Bus 002 Device 003: ID 0403:6010 Future Technology Devices International, 
Ltd FT2232C/D/H Dual UART/FIFO IC

ทำคำสั่งเพื่อตรวจสอบดูว่า พบชิป FPGA ที่เชื่อมต่อผ่านทาง JTAG Interface หรือไม่

$ sudo openFPGALoader --detect --freq 2000000

No cable or board specified: using direct ft2232 interface
Jtag frequency : requested 2.00MHz   -> real 2.00MHz  
index 0:
    idcode 0x900281b
    manufacturer Gowin
    family GW1N
    model  GW1N-1
    irlength 8

ทำคำสั่งเพื่ออัปโหลดไฟล์ Bitstream ไปยังชิป FPGA ในโหมด SRAM

# Upload the bitstream file to the SRAM of the FPGA.
$ sudo openFPGALoader -b tangnano --freq 2000000 -m \
  ./led_blink/impl/pnr/led_blink.fs

รูป: บอร์ด Tang Nano FPGA ที่ได้นำมาทดลองจริง (ควบคุมการทำงานของ Onboard RGB LED)

ข้อสังเกต:

  • การอัปโหลดไฟล์ Bitstream ด้วย openFPGALoader ไปยัง Embedded Flash ภายในชิป GW1N-1 FPGA (ชิป CH552T บนบอร์ดทำหน้าที่เป็น USB-JTAG) ดูเหมือนว่าจะใช้ไม่ได้ ดังนั้นแนะนำให้ใช้ซอฟต์แวร์ Gown Programmer 2 v2.09426 Beta (Windows), updated: 2019-12-06
  • บอร์ด Sipeed Tang Nano 1K และรุ่นอื่น ๆ ที่ใหม่กว่า ได้มีการเปลี่ยนมาใช้ชิป เช่น BL702 ซึ่งทำงานได้มีประสิทธิภาพดีกว่า CH552T

 

หากต้องการจะลองใช้โมดูล RGB LED แทนการใช้งาน Onboard RGB LED ก็ทำได้เช่นกัน ในตัวอย่างนี้ได้เลือกใช้โมดูล RGB LED ที่ทำงานแบบ Active-High และได้เลือกใช้ขาหมายเลข 38, 39 และ 40 ของบอร์ด Tang Nano FPGA เป็นขาสัญญาณควบคุม และต่อขา GND ร่วมกันในวงจรบนเบรดบอร์ด เมื่อกำหนดหมายเลขขาให้ตรงตามรูปแบบการต่อวงจรจริงแล้ว ให้ทำขั้นตอน Synthesize และ Place & Route อีกครั้ง เพื่อสร้างไฟล์ .fs ใหม่อีกครั้ง แล้วจึงนำไปทดลองใช้กับบอร์ด FPGA

รูป: ตัวอย่างการใช้โมดูลภายนอก RGB LED (active-high)

 


การใข้งาน Gowin EDA ภายใต้ Ubuntu VM#

อีกทางเลือกหนึ่งคือ การสร้าง Ubuntu VM (Virtual Machine) โดยใช้ Oracle VirtualBox สำหรับผู้ใช้ Windows (Host OS) และติดตั้งซอฟต์แวร์ Gowin IDE (Linux / Ubuntu)

รูป: ตัวอย่างการใช้งาน Gowin EDA ใน Ubuntu Desktop 22.04 LTS VM

ผู้ใช้สามารถกำหนดค่า MAC Addresss (NIC) ของ Ubuntu VM สำหรับอินเทอร์เฟสชื่อ enp0s3 (VirtualBox Host-Only Network) ได้ตามต้องการ เช่น ตั้งค่าให้ตรงกับ NIC ที่มีการระบุไว้ในไฟล์ลิขสิทธิ์ โดยใช้คำสั่งในลักษณะนี้ (XX:XX:XX:XX:XX:XX หมายถึง MAC Address)

# Install the 'net-tools for 'ifconfig'
$ sudo apt install net-tools -y

# Turn off the 'enp0s3' network interface
$ sudo ifconfig enp0s3 down

# Change the MAC address of the 'enp0s3' interface
$ sudo ifconfig enp0s3 hw ether XX:XX:XX:XX:XX:XX

# Turn on the 'enp0s3' interface
$ sudo ifconfig enp0s3 up

นอกจากนั้นแล้วแนะนำให้ตั้งค่าการแชร์ไดเรกทอรี ระหว่าง Host OS (Windows) กับ Ubuntu VM เพื่อทำให้การสำเนาไฟล์ระหว่างระบบไฟล์ทั้งสองทำได้ง่ายขึ้น

รูป: ตัวอย่างการตั้งค่าเพื่อแชร์ไฟล์สำหรับ VirtualBox / Ubuntu VM

รูป: ตัวอย่างการใช้ Gowin Programmer Standard Edition for Windows เพื่ออัปโหลดไฟล์ Bitstream (.fs) ไปยังบอร์ด Tang Nano FPGA ในโหมด SRAM Access Mode: SRAM Program

 


ตัวอย่างโค้ด: 8x LED Bargraph #

อีกตัวอย่างหนึ่งเป็นโค้ด VHDL สาธิตการกำหนดค่าลอจิกให้อาร์เรย์ของ LED จำนวน 8 ตำแหน่ง (8x LED Bar) และทำงานแบบ Active-Low เริ่มต้นทุกดวงจะมีสถานะเป็น OFF และจะเริ่มเปลี่ยนเป็นสถานะ ON ทีละดวงตามลำดับจนครบ 8 ดวง แล้วดับลงทุกดวง และทำซ้ำ

------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
------------------------------------------------------------------

entity LED_BARGRAPH is
  generic (
     CLK_HZ : natural := 24000000;
     WIDTH  : natural := 8 
  );
  port(
     CLK  : in std_logic; -- system clock
     nRST : in std_logic; -- acive-low asynchronous reset
     LEDS : out std_logic_vector(WIDTH-1 downto 0)
  );
end LED_BARGRAPH;

architecture SYNTH of LED_BARGRAPH is
  constant ALL_ONES     : unsigned := to_unsigned(2**WIDTH-1,WIDTH);
  constant COUNT_PERIOD : integer := CLK_HZ/8;
  subtype count_t is integer range 0 to (COUNT_PERIOD-1); 
  signal  count : count_t := 0;
  signal  reg   : std_logic_vector(WIDTH-1 downto 0);
  signal  shift_en : std_logic;

begin

  LEDS <= not reg; -- Use the register's bits (inverted) for LEDs.

  process (nRST, CLK) begin
    if nRST = '0' then
      count <= 0;
      shift_en <= '0';
    elsif rising_edge(CLK) then
      -- check whether the counter reaches the max. value.
      if count = (COUNT_PERIOD-1) then 
        count <= 0;       -- reset the counter.
        shift_en <= '1';  -- enable register shift.
      else
        count <= count+1; -- increment counter by 1.
        shift_en <= '0';  -- disable register shift.
      end if;
    end if;
  end process;

  process (nRST, CLK) begin
    if nRST = '0' then
      reg <= (others => '0'); -- clear the shift register.
    elsif rising_edge(CLK) then
      if  shift_en='1' then -- register shifting is enabled.  
        if reg = std_logic_vector( ALL_ONES ) then
          -- clear the shift register.
          reg <= (others => '0'); 
        else
          -- shift left, insert '1' as LSB.
          reg <= reg(reg'left-1 downto 0) & '1'; 
        end if;
        end if;
    end if;
  end process;

end SYNTH;

ตัวอย่างการเลือกใช้ขา I/O Pins ของบอร์ด Tang Nano FPGA เพื่อเชื่อมต่อกับโมดูล 8x LED Bar มีดังนี้ (สร้างไฟล์ led_bargraph.cst ในโปรเจกต์ แล้วใส่ข้อความต่อไปนี้ลงในไฟล์)

IO_LOC "CLK"      35;
IO_LOC "nRST"     15;
IO_LOC "LEDS[7]"  38;
IO_LOC "LEDS[6]"  39;
IO_LOC "LEDS[5]"  40;
IO_LOC "LEDS[4]"  41;
IO_LOC "LEDS[3]"  42;
IO_LOC "LEDS[2]"  43;
IO_LOC "LEDS[1]"  44;
IO_LOC "LEDS[0]"  45;

IO_PORT "CLK" IO_TYPE=LVCMOS33; 
IO_PORT "nRST" IO_TYPE=LVCMOS33 PULL_MODE=UP; 
IO_PORT "LEDS[0]" IO_TYPE=LVCMOS33 DRIVE=8; 
IO_PORT "LEDS[1]" IO_TYPE=LVCMOS33 DRIVE=8; 
IO_PORT "LEDS[2]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "LEDS[3]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "LEDS[4]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "LEDS[5]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "LEDS[6]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "LEDS[7]" IO_TYPE=LVCMOS33 DRIVE=8;

รูป: การคอมไพล์โค้ดตัวอย่าง LED Bargraph และแปลงให้เป็นไฟล์ Bitstream

รูป: ตัวอย่างการต่อวงจรโดยใช้โมดูล 8x LED Bar บนเบรดบอร์ด

 

อีกตัวอย่างหนึ่งที่มีการใช้ LED จำนวน 8 ดวง แต่มีพฤติกรรมที่แตกต่างไปเล็กน้อยคือ การเลื่อนตำแหน่งของ LED ที่อยู่ในสถานะ ON ไปตามลำดับ เริ่มต้นจากบิต LSB เลื่อนไปทางบิต MSB

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity LED_RUNNING is
  generic (
     CLK_HZ : natural := 24000000;
     WIDTH  : natural := 8
  );
  port(
     CLK  : in std_logic; -- system clock
     nRST : in std_logic;  -- acive-high asynchronous reset
     LEDS : out std_logic_vector(WIDTH-1 downto 0)
  );
end LED_RUNNING;

architecture SYNTH of LED_RUNNING is
  constant COUNT_MAX: integer := (CLK_HZ/10) - 1;
  subtype count_t is integer range 0 to COUNT_MAX; 
  signal  count : count_t := 0;
  signal  leds_reg : std_logic_vector(2*WIDTH-1 downto 0);

begin

  LEDS <= not leds_reg(WIDTH-1 downto 0); 

  process (nRST, CLK) 
  begin
    if nRST = '0' then
      count <= 0;
      leds_reg(WIDTH-1 downto 0 ) <= (others => '0');
      leds_reg(2*WIDTH-1 downto WIDTH) <= (others => '1');
    elsif rising_edge(CLK) then
      if count = COUNT_MAX then 
        count <= 0;       -- reset the counter.
        leds_reg <= leds_reg(leds_reg'left-1 downto 0) 
                    & leds_reg(leds_reg'left); 
      else
        count <= count+1; -- increment counter by 1.
      end if;
    end if;
  end process;

end SYNTH;

 


ตัวอย่างโค้ด: 4-Digit 7-Segment Display#

ตัวอย่างถัดไปสาธิตการเขียนโค้ด VHDL เพื่อแสดงตัวเลข 4 หลัก ที่มีการเริ่มต้นนับจาก 0000 ไปจนถึง 9999 และมีการแสดงค่าตัวเลขโดยใช้โมดูล 7-Segment Display จำนวน 4 หลัก (4 Digits) โมดูลที่ได้เลือกมาใช้งาน มีวงจรทรานซิสเตอร์ควบคุมการทำงานของแต่ละหลักและทำงานแบบ Active-Low

เนื่องจากว่าทั้ง 4 หลัก ต้องใช้ขาสัญญาณ A, B, C, D, E, F, G, DP ร่วมกัน เพื่อใช้ในการกำหนดสถานะลอจิก ON/OFF ของ 7-Segment ในแต่ละหลัก ดังนั้นจึงต้องใช้วิธีการที่เรียกว่า Time-Multiplexing เพื่อควบคุมการทำงานของโมดูลแสดงผล

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity DISP_4X7SEG_DEMO is
  generic( 
    CLK_HZ  : natural := 24000000
  );
  port(
    CLK      : in std_logic;
    nRST     : in std_logic;
    SEGMENTS : out std_logic_vector(7 downto 0);
    DIGITS   : out std_logic_vector(3 downto 0) 
 );
end DISP_4X7SEG_DEMO;

architecture SYNTH of DISP_4X7SEG_DEMO is
  constant NUM_DIGITS : natural := 4;
  constant CNT_MAX1   : natural := (CLK_HZ/400)-1;
  constant CNT_MAX2   : natural := (CLK_HZ/10)-1;

  type bcd_counter_t is array(0 to NUM_DIGITS-1) of integer range 0 to 9;
  signal bcd_count  : bcd_counter_t; 

  signal data_buf     : std_logic_vector(7 downto 0);
  signal digits_sel   : std_logic_vector(NUM_DIGITS-1 downto 0);
  signal digit_index  : natural range 0 to NUM_DIGITS-1 := 0;

  subtype nibble is unsigned(3 downto 0);
  -- This function implements a BCD to 7-Segment decoder.
  function BCD2SEG7( data: nibble ) return std_logic_vector is
     variable seg7bits : std_logic_vector(6 downto 0);
  begin
     case data is
        when "0000" => seg7bits := "0111111"; -- 0
        when "0001" => seg7bits := "0000110"; -- 1
        when "0010" => seg7bits := "1011011"; -- 2
        when "0011" => seg7bits := "1001111"; -- 3
        when "0100" => seg7bits := "1100110"; -- 4
        when "0101" => seg7bits := "1101101"; -- 5
        when "0110" => seg7bits := "1111101"; -- 6
        when "0111" => seg7bits := "0000111"; -- 7
        when "1000" => seg7bits := "1111111"; -- 8
        when "1001" => seg7bits := "1101111"; -- 9
        when others => seg7bits := "0000000"; -- off
     end case;
     return seg7bits;
  end BCD2SEG7;

begin

  -- This process implements a N-digit BCD counter.
  process (nRST,CLK)
    variable wait_cnt    : natural range 0 to CNT_MAX2 := 0;
    variable clk_enabled : boolean;
    variable carry       : boolean; 
  begin
    if nRST = '0' then
      wait_cnt := 0;
      for i in bcd_count'range loop
         bcd_count(i) <= 0;
      end loop;
    elsif rising_edge(CLK) then 
       if wait_cnt = CNT_MAX2 then
          wait_cnt := 0;
          clk_enabled := true;
       else 
          wait_cnt := wait_cnt + 1;
          clk_enabled := false;
       end if;
       if clk_enabled then
          carry := true;
          for i in 0 to NUM_DIGITS-1 loop
             if carry then
                if bcd_count(i)=9 then
                   bcd_count(i) <= 0;
                   carry := true;
                else
                   bcd_count(i) <= bcd_count(i)+1;
                   carry := false;
                end if;
             else
                carry := false;
             end if;
          end loop;
       end if;
    end if;
  end process; 

  -- This process implements a N-digit 7-segment driver
  -- using time-multiplexing.
  process (nRST, CLK) 
    variable wait_cnt    : natural range 0 to CNT_MAX1 := 0;
    variable clk_enabled : boolean;
    variable bcd_value   : unsigned(3 downto 0);
  begin
    if nRST = '0' then
       wait_cnt := 0;
       data_buf    <= x"00";
       digits_sel  <= (others => '0');
       digit_index <= 0;
    elsif rising_edge(CLK) then     
       if wait_cnt = CNT_MAX1 then
          wait_cnt := 0;
          clk_enabled := true;
       else 
          wait_cnt := wait_cnt + 1;
          clk_enabled := false;
       end if;
       if clk_enabled then
          if digit_index = NUM_DIGITS-1 then 
             digit_index <= 0;
          else 
             digit_index <= digit_index + 1;
          end if;
          for i in 0 to NUM_DIGITS-1 loop
             if i = digit_index then
               digits_sel(i) <= '1';
             else 
               digits_sel(i) <= '0';
             end if;
          end loop;
          bcd_value := to_unsigned(bcd_count(digit_index),4);
          data_buf <= '0' & BCD2SEG7( bcd_value );
       end if;
    end if;
  end process;

  DIGITS   <= (others => '1') when nRST = '0' else (not digits_sel);
  SEGMENTS <= not data_buf; -- for common-anode 7-segment LEDs

end SYNTH;

ตัวอย่างการเลือกใช้ขา I/O Pins ของบอร์ด Tang Nano FPGA เพื่อเชื่อมต่อกับโมดูล 4-Digit 7-Segment Display มีดังนี้

IO_LOC "CLK"          35;
IO_LOC "nRST"         15;
IO_LOC "SEGMENTS[0]"  42;
IO_LOC "SEGMENTS[1]"  43;
IO_LOC "SEGMENTS[2]"  44;
IO_LOC "SEGMENTS[3]"  45;
IO_LOC "SEGMENTS[4]"  11;
IO_LOC "SEGMENTS[5]"  10;
IO_LOC "SEGMENTS[6]"  46;
IO_LOC "SEGMENTS[7]"   5;
IO_LOC "DIGITS[3]"    38;
IO_LOC "DIGITS[2]"    39;
IO_LOC "DIGITS[1]"    40;
IO_LOC "DIGITS[0]"    41;

IO_PORT "CLK"  IO_TYPE=LVCMOS33; 
IO_PORT "nRST" IO_TYPE=LVCMOS33 PULL_MODE=UP; 
IO_PORT "SEGMENTS[0]" IO_TYPE=LVCMOS33 DRIVE=8; 
IO_PORT "SEGMENTS[1]" IO_TYPE=LVCMOS33 DRIVE=8; 
IO_PORT "SEGMENTS[2]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "SEGMENTS[3]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "SEGMENTS[4]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "SEGMENTS[5]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "SEGMENTS[6]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "SEGMENTS[7]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "DIGITS[0]"   IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "DIGITS[1]"   IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "DIGITS[2]"   IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "DIGITS[3]"   IO_TYPE=LVCMOS33 DRIVE=8;

 

ข้อสังเกต: หากต้องการใช้งานขา I/O บางขาของ FPGA ที่ถูกจัดว่าเป็น Dual-Purpose Pins จะต้องมีการตั้งค่าเพื่อให้อนุญาตใช้งานเป็น I/O Pins ก่อน จึงจะใช้งานได้

รูป: ตัวอย่างการตั้งค่าสำหรับ Project Configuration > Place & Route > Dual-Purpose Pin

รูป: ตัวอย่างการต่อวงจรบนเบรดบอร์ดเพื่อใช้งานโมดูล 4-Digit 7-Segment Display ร่วมกับบอร์ด Tang Nano FPGA

 


ตัวอย่างโค้ด: Rotary Encoder Switch + 8x LED Bar#

ตัวอย่างถัดไปเป็นโค้ด VHDL สาธิตการตรวจสอบการหมุนปุ่มของโมดูล Rotary Encoder Switch ที่ให้ขาสัญญาณเอาต์พุต A และ B เมื่อไม่มีการหมุน ค่าลอจิกของ A และ B จะเป็น 1 (High) แต่ถ้ามีการหมุนปุ่ม จะทำให้ค่าลอจิกของขาสัญญาณ A และ B เปลี่ยนแปลง และสลับช่วงเวลากัน

ในตัวอย่างนี้ สัญญาณ A และ B ของโมดูลจะถูกนำไปใช้เป็นขาสัญญาณอินพุตของวงจรดิจิทัลใน FPGA การหมุนปุ่มของโมดูลตามเข็มหรือทวนเข็ม จะถูกนำมาใช้เป็นเงื่อนไขในการเพิ่มหรือลดค่าของระดับในการแสดงผลของ LED Bar ซึ่งมีทั้งหมด 8 ดวง และทำงานแบบ Active-Low

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ROTARY_SW_LEDS is
  generic( 
    CLK_HZ : natural := 24000000
  );
  port( 
    CLK   : in std_logic; 
    nRST  : in std_logic;
    SW_A  : in std_logic; 
    SW_B  : in std_logic;
    LEDS  : out std_logic_vector( 7 downto 0 ) 
  ); 
end ROTARY_SW_LEDS;

architecture behave of ROTARY_SW_LEDS is
  constant CNT_MAX : natural := (CLK_HZ/500)-1;
  signal capture   : std_logic_vector(3 downto 0) := (others => '1');
  signal update    : std_logic := '0';
  signal change    : std_logic := '0';
  signal inc, dec  : std_logic := '0';
  signal level     : unsigned( 7 downto 0) := (others => '0');

begin
  -- This process implements switch debouncing logic.
  P1: process (nRST, CLK)
    variable wait_cnt : natural range 0 to CNT_MAX := 0;
  begin
     if nRST = '0' then
        wait_cnt := 0;
        capture <= (others => '1');
        update <= '0';
     elsif rising_edge(CLK) then
        if wait_cnt = CNT_MAX then 
          wait_cnt := 0;
          update  <= '1';
          capture <= capture(1 downto 0) & (SW_A & SW_B);
        else
          wait_cnt := wait_cnt + 1;
          update <= '0';
        end if;

     end if;
  end process;

  -- Detect the falling edge on the captured SW_A signal.
  change <= capture(3) and (not capture(1));
  inc <= change and (not capture(0));  -- Enable counter increment.
  dec <= change and capture(0);        -- Enable counter decrement.

  -- This process implements the LED level indicator.
  P2: process (nRST, CLK)
  begin
     if nRST = '0' then
       level <= (others => '0');
     elsif rising_edge(CLK) then
       if update = '1' then   -- Update the LED level.
         if inc = '1' then    -- Increment the LED level.
            level <= level(6 downto 0) & '1';
         elsif dec = '1' then -- Decrement the LED level.
            level <= '0' & level(7 downto 1);
         end if;
       end if;
     end if;
  end process;

  -- Output the LED level to the 8-bit LED bar (active-low).
  LEDS <= not std_logic_vector(level);

end behave;

ตัวอย่างการเลือกใช้ขา I/O Pins ของบอร์ด Tang Nano FPGA เพื่อเชื่อมต่อกับโมดูล 8x LED Bar และโมดูล Rotary Encoder Switch มีดังนี้

IO_LOC "CLK"      35;
IO_LOC "nRST"     15;
IO_LOC "LEDS[7]"  38;
IO_LOC "LEDS[6]"  39;
IO_LOC "LEDS[5]"  40;
IO_LOC "LEDS[4]"  41;
IO_LOC "LEDS[3]"  42;
IO_LOC "LEDS[2]"  43;
IO_LOC "LEDS[1]"  44;
IO_LOC "LEDS[0]"  45;
IO_LOC "SW_A"     10;
IO_LOC "SW_B"     46;

IO_PORT "CLK" IO_TYPE=LVCMOS33; 
IO_PORT "nRST" IO_TYPE=LVCMOS33 PULL_MODE=UP; 
IO_PORT "LEDS[0]" IO_TYPE=LVCMOS33 DRIVE=8; 
IO_PORT "LEDS[1]" IO_TYPE=LVCMOS33 DRIVE=8; 
IO_PORT "LEDS[2]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "LEDS[3]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "LEDS[4]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "LEDS[5]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "LEDS[6]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "LEDS[7]" IO_TYPE=LVCMOS33 DRIVE=8;
IO_PORT "SW_A"    IO_TYPE=LVCMOS33;
IO_PORT "SW_B"    IO_TYPE=LVCMOS33;

รูป: ตัวอย่างการต่อวงจรโดยใช้โมดูล 8x LED Bar และ Rotary Encoder Switch บนเบรดบอร์ด

รูป: ตัวอย่างการบันทึกสัญญาณด้วย USB Logic Analyzer และแสดงผลด้วย PulseView (แบ่งเป็น 2 กรณี คือ หมุนตามเข็ม และ หมุนทวนเข็มนาฬิกา)

 


 

แหล่งข้อมูลสำหรับอ้างอิงและศึกษาเพิ่มเติม


กล่าวสรุป#

บทความนี้ได้นำเสนอการใช้ซอฟต์แวร์ Gowin EDA - Standard Edition (V1.9.9Beta-2) ในระบบปฏิบัติการ Ubuntu 22.04 LTS เพื่อลองเขียนโค้ด VHDL แล้วแปลงให้เป็นไฟล์ Bitstream สำหรับบอร์ด Tang Nano FPGA และได้ลองใช้โปรแกรม OpenFPGALoader (Open Source) เพื่ออัปโหลดไฟล์ Bitstream ไปยังชิปบนบอร์ด FPGA

ในบทความนี้ยังได้นำเสนอตัวอย่างโค้ด VHDL ที่สามารถนำไปทดลองใช้งานได้ เช่น การใช้งานร่วมกับโมดูล RGB LED โมดูล 8-bit LED Bar โมดูล 4-Digit 7-Segment Display และโมดูล Rotary Encoder Switch เป็นต้น

จุดเด่นของ Gowin EDA จากที่ผู้เขียนได้ทดลองใช้งาน คือ ขั้นตอนการคอมไพล์โค้ดและแปลงให้เป็นไฟล์ Bitstream สามารถทำได้อย่างรวดเร็ว ภายในไม่มีวินาที และการติดตั้งซอฟต์แวร์ต้องการความจุในฮาร์ดดิสก์ประมาณ 1.2GB

 


This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

Created: 2023-07-21 | Last Updated: 2023-08-04