Está en la página 1de 198

Tiếp cận lập trình cho FPGA từ Spartan -3 1

CHƯƠNG I: CÔNG NGHỆ ASIC

1.1. Các hướng tiếp cận thiết kế ASIC


1.1.1. Các công nghệ lập trình thiết kế ASIC
Tóm tắt các công nghệ lập trình cho ASIC được trình bày trong bảng dưới
đây.
Bảng1.1. Các đặc tính công nghệ lập trình thiết kế ASIC

Công nghệ lập trình Tính Có thể Diện tích của Điện trở Điện
bay hơi lập trình ASIC (ohm) dung
(pF)

Các phần tử RAM tĩnh Có Trong Lớn 1 - 2K 10 - 20


mạch

Lập trình cầu chì Không Không Anti-fuse nhỏ 300 - 500K 3-5
nghịch PLICE (PLICE
Số tranzitor
Anti-fuse)
lớn

Lập trình cầu chì Không Không Anti-fuse nhỏ 50 - 80K 1-3
nghịch ViaLink
Số tranzitor
(ViaLink Anti-fuse)
lớn

EPROM Không Ngoài Nhỏ 2 - 4K 10 - 20


mạch

EEPROM Không Trong 2xEPROM 2 - 4K 10 - 20


mạch
Tiếp cận lập trình cho FPGA từ Spartan -3 2

1.1.2. Thiết kế logic ASIC đầu vào (Logic Design Entry)


Mục đích của thiết kế đầu vào là mô tả một hệ thống vi điện tử dựa trên các
công cụ của hệ tự động thiết kế điện tử EDA (Electronic-Design Automation). Các
hệ thống điện tử được xây dựng dựa trên các thành phần tĩnh, như là các IC TLL.
Thiết kế đầu vào đối với các hệ thống này chính là công việc vẽ các mạch và tổng
hợp dạng giản đồ. Giản đồ thể hiện các thành phần được kết nối với nhau như thế
nào, đó chính là liên kết của một ASIC. Phần này của quá trình thiết kế đầu vào
được gọi là đầu vào giản đồ, hoặc là bắt giản đồ. Một giản đồ mạch mô tả một
ASIC giống như là một bản thiết kế cho một công trình xây dựng.
Giản đồ mạch là một bản vẽ, là một khuôn dạng đơn giản để chúng ta có thể
hiểu và sử dụng, nhưng các máy tính cần làm việc với các phiên bản ASCII hoặc
các tệp nhị phân mà chúng ta gọi là netlist (đi dây). Đầu ra của công cụ thiết kế giản
đồ chính là một file netlist mà có chứa mô tả của tất cả các thành phần trong một
bản thiết kế và các đường kết nối của chúng.
Không phải tất cả các thông tin thiết kế có thể chuyển thành giản đồ mạch
hoặc netlist, vì không phải tất cả các chức năng của một ASIC đều được mô tả qua
thông tin kết nối. Ví dụ, giả sử chúng ta sử dụng một ASIC lập trình được cho một
vài chức năng logic ngẫu nhiên. Một phần của ASIC có thể được thiết kế bằng cách
sử dụng ngôn ngữ lập trình dạng văn bản. Trong trường hợp này thiết kế đầu vào
cũng gồm có cả viết mã nguồn. Vậy điều gì nếu một ASIC trong hệ thống của
chúng ta có chứa một PROM (Programmable Memory)? Phải chăng vi mã lệnh là
một phần của thiết kế đầu vào? Việc điều hành hệ thống của chúng ta chắc chắn là
phụ thuộc vào chương trình chuẩn của PROM. Vì vậy có lẽ mã lệnh PROM phải là
một phần của thiết kế đầu vào. Mặt khác không ai coi mã lệnh hệ điều hành đã được
nạp vào RAM trên một ASIC là một phần của thiết kế đầu vào. Rõ ràng là có nhiều
dạng thiết kế đầu vào khác nhau. Trong mỗi một trường hợp nó rất quan trọng để
bảo đảm là bạn hoàn thành chỉ định cho hệ thống - không chỉ là xây dựng cấu trúc
chính xác mà còn để bất kỳ ai cũng hiểu được là hệ thống làm việc như thế nào.
Thiết kế đầu vào là một trong những phần quan trọng nhất của công nghệ ASIC.
Tiếp cận lập trình cho FPGA từ Spartan -3 3

Cho đến hiện hay thì hầu hết các thiết kế đầu vào cho ASIC vẫn sử dụng
phương pháp giản đồ đầu vào. Do ASIC ngày càng trở nên phức tạp hơn, các
phương pháp thiết kế đầu vào khác ngày càng trở nên phổ biến. Các phương pháp
thiết kế đầu vào đều có thể sử dụng phương pháp đồ hoạ, chẳng hạn là một giản đồ,
hoặc các tệp dạng text dưới dạng ngôn ngữ lập trình. Việc sử dụng ngôn ngữ mô tả
phần cứng HDL (Hardware Description Language) cho mục đích thiết kế đầu vào
cho phép chúng ta tạo ra các netlist trực tiếp bằng cách tổng hợp logic. Chúng ta sẽ
đề cập đến các phương pháp thiết kế đầu vào mức thấp cùng với các ưu điểm cũng
như nhược điểm của chúng trong mục 2.3.
Thiết kế đầu vào bao gồm các thành phần thiết kế sau:
- Thiết kế thư viện ASIC.
- Thiết kế thư viện các vi mạch ASIC lập trình được (Programmable ASIC).
- Thiết kế phần tử logic ASIC lập trình được.
- Thiết kế phần tử vào/ra ASIC lập trình được.
- Thiết kế phần tử kết nối ASIC lập trình được.
Phần mềm thiết kế ASIC lập trình được:
- Thiết kế logic mức thấp đầu vào (low-level design entry) sử dụng
VHDL: Bộ Quốc Phòng Mỹ (The U.S. Department of Defence - DoD) đã
hỗ trợ việc phát triển ngôn ngữ VHDL (VHSIC Hardware Description
Language) như một phần của chương trình quốc gia VHSIC (Very High-
Speed IC) vào đầu thập kỷ 80.
- Tổng hợp logic (logic synthesis): Tổng hợp logic cung cấp liên kết giữa
một tệp HDL (VHDL hoặc Verilog) và một netlist tương tự như cách mà
một bộ biên dịch C cung cấp liên kết giữa mã lệnh chương trình C và ngôn
ngữ máy.
- Mô phỏng (simulation): Các kỹ sư đã quen với các hệ thống mẫu dùng để
kiểm tra sản phẩm thiết kế của họ, thương thường sử dụng một đế thử
mạch mẫu, cho phép cắm các IC và các dây dẫn lên. Đế thử mạch mẫu có
thể thực hiện được khi có cho phép xây dựng hệ thống từ một vài IC TTL.
Tiếp cận lập trình cho FPGA từ Spartan -3 4

Tuy nhiên điều này là phi thực tế đối với thiết kế ASIC. Do vậy hầu hết các
kỹ sư thiết kế ASIC đều sử dụng phương pháp mô phỏng tương đương
thay cho mô hình đế thử mạch.
- Thử nghiệm mức logic (test): Các ASIC được thử nghiệm theo hai giai
đoạn trong quá trình sản xuất bằng cách sử dụng các phương pháp thử
nghiệm sản xuất.
1.1.3. Thiết kế vật lý (Physical Design)
Hình 1.1 biểu diễn một phần của sơ đồ thiết kế, đó là các bước thiết kế vật lý
đối với một ASIC.

Hình 1.1. Một phần của thiết kế ASIC gồm có phân chia hệ thống, lên sơ
đồ mặt bằng, sắp xếp các phần tử và các bước định tuyến đường kết nối.
Đầu tiên chúng ta áp dụng việc phân chia hệ thống để chia một hệ thống vi
điện tử thành các ASIC. Trong phần lên sơ đồ mặt bằng chúng ta sẽ đánh giá kích
thước và đặt các vị trí liên quan của các khối trong ASIC (đôi khi còn được gọi là
xếp chip - chip planning). Cùng thời điểm này chúng ta định vị khoảng trống cho
Tiếp cận lập trình cho FPGA từ Spartan -3 5

đường xung nhịp và nguồn và quyết định vị trí của cổng I/O. Việc sắp xếp định
nghĩa vị trí của các phần tử logic cùng với sự linh hoạt của các khối và khoảng
trống dành cho việc nối các phần tử logic. Việc sắp xếp đối với thiết kế ma trận
cổng (gate-array) hoặc phần tử tiêu chuẩn (standard-cell) bố trí mỗi một phần tử
logic vào vị trí trong cùng một hàng. Việc lên sơ đồ mặt bằng và sắp xếp phần tử
đôi khi có thể sử dụng công cụ CAD. Việc định tuyến thực hiện đường kết nối giữa
các phần tử logic. Việc định tuyến là một vấn đề rất khó và thường được phân chia
thành các bước riêng biệt được gọi là định tuyến toàn cục và định tuyến cục bộ.
Định tuyến toàn cục xác định các kết nối giữa các phần tử logic đã đặt chỗ và các
khối sẽ đặt chỗ ở đâu. Còn định tuyến cục bộ là mức định tuyến cụ thể và chi tiết
đến từng phần tử.
1.1.4. Các công cụ CAD (CAD Tools)
Để phát triển một công cụ CAD cần thiết phải chuyển đổi mỗi một bước trong
thiết kế vật lý thành các vấn đề có mục đích và định hướng rõ ràng. Mục đích là
những gì chúng ta cần phải thực hiện, còn định hướng là cách thực hiện mục đích.
Ví dụ trong các bước thiết kế vật lý ASIC thì các mục đích và định hướng như sau:
Phân chia hệ thống (System partitioning):
- Mục đích: Phân chia một hệ thống thành một số các ASIC.
- Định hướng: Tối thiểu hoá số lượng các kết nối ngoài giữa các ASIC. Giữ
cho mỗi ASIC nhỏ hơn kích thước cực đại.
Lên sơ đồ mặt bằng (Floorplanning):
- Mục đích: Tính toán kích thước của tất cả các khối và sắp xếp vị trí của
chúng.
- Định hướng: Bảo đảm sự liên kết cao giữa các khối về mặt tự nhiên càng
gần càng tốt.
Sắp xếp các phần tử (Placement):
- Mục đích: Sắp xếp việc kết nối giữa các vùng và vị trí của tất cả các phần
tử logic cùng với các khối linh hoạt.
- Định hướng: Tối thiểu hoá các vùng ASIC và mật độ kết nối.
Tiếp cận lập trình cho FPGA từ Spartan -3 6

Định tuyến toàn cục (Global routing):


- Mục đích: Quyết định vị trí của tất cả các kết nối.
- Định hướng: Tối thiểu hoá toàn bộ vùng kết nối được sử dụng.
Định tuyến chi tiết (Detailed routing):
- Mục đích: Hoàn thành định tuyến tất cả các kết nối trên chip.
- Định hướng: Tối thiểu hoá tổng số độ dài kết nối được sử dụng.
1.2. Thiết kế ASIC đầu vào (design entry)
1.2.1. Thiết kế thư viện ASIC
Thư viện phần tử là một phần chính trong thiết kế ASIC. Đối với một ASIC
lập trình được thì một công ty chuyên về PLD, FPGA cung cấp cho chúng ta một
thư viện các phần tử lôgic dưới hình thức một bộ kit thiết kế, thường là chúng ta
không có một sự lựa chọn nào và giá của nó nói chung khoảng vài nghìn đô la. Đối
với MGAs và CBICs chúng ta có ba lựa chọn : nhà cung cấp ASIC (công ty sẽ xây
dựng ASIC cho chúng ta) sẽ cung cấp một thư viện phần tử, hoặc chúng ta có thể
mua một thư viện phần tử từ một nhà cung cấp thư viện thứ ba, hoặc chúng ta có thể
tự xây dựng thư viện phần tử của chính mình.
Sự lựa chọn đầu tiên, đó là sử dụng một thư viện ASIC của nhà cung cấp, yêu
cầu chúng ta phải sử dụng một tập các công cụ thiết kế đã được cung cấp bởi nhà
cung cấp ASIC để đưa vào và mô phỏng thiết kế của chúng ta. Tức là chúng ta phải
mua các công cụ và thư viện phần tử. Một vài nhà cung cấp ASIC (đặc biệt cho
MGAs) đã cung cấp các công cụ được phát triển theo yêu cầu.
Thư viện của nhà cung cấp ASIC thông thường là một thư viện ảo - các phần
tử chỉ là các khối trống rỗng, nhưng nó bao gồm đủ thông tin để bố trí sơ đồ mạch.
Sau khi chúng ta hoàn thành việc bố trí sơ đồ mạch, chúng ta đưa ra netlist đến nhà
cung cấp ASIC để họ bổ sung vào các phần tử ảo trước khi bắt đầu sản xuất chip
cho chúng ta.
Các lựa chọn thứ hai và ba yêu cầu chúng ta thực hiện một quyết định mua
bán. Nếu chúng ta hoàn thành việc thiết kế một ASIC sử dụng thư viện phần tử mà
chúng ta mua, thì chúng ta đã sở hữu việc chế tạo chip được sử dụng để sản xuất
Tiếp cận lập trình cho FPGA từ Spartan -3 7

ASIC của chính mình. Những thư viện phần tử như vậy thường đắt (có thể lên đến
vài trăm nghìn đô la). Tuy nhiên điều này có nghĩa rằng việc mua một thư viện đắt
có thể rẻ về lâu dài nếu chúng ta sản xuất nhiều hơn là các giải pháp khác.
Lựa chọn thứ ba sẽ phát triển một thư viện phần tử theo yêu cầu. Nhiều công
ty máy tính và công ty điện tử lớn chọn phương án này. Hầu hết thư viện phần tử
thiết kế hiện nay vẫn tiếp tục được phát triển theo hình thức “yêu cầu” mặc dù thực
tế quá trình phát triển thư viện rất phức tạp và đắt.
Tuy nhiên để tạo ra mỗi phần tử trong một thư viện phần tử ASIC phải bao
gồm các yếu tố sau:
- Sơ đồ bố trí vật lý
- Mô hình hoạt động
- Mô hình Verilog/VHDL
- Mô hình tính toán thời gian chi tiết
- Chiến lược thử nghiệm, kiểm tra
- Sơ đồ mạch
- Biểu tượng của phần tử
- Mô hình tải
- Mô hình định tuyến
1.2.2. Các vi mạch ASIC lập trình được
Có hai loại ASIC lập trình được: Thiết bị logic lập trình được - PLD
(Programmable Logic Device) và Ma trận cổng lập trình được theo hàng - FPGA
(Field-Programmable Gate Array). Việc phân biệt giữa hai loại ASIC này chưa
được chuẩn hoá. Sự khác nhau thực tế chỉ là sự kế thừa của chúng. PLDs bắt đầu từ
những thiết bị nhỏ dùng để thể thay thế một một phần của họ IC TTL, và chúng đã
được phát triển tương tự như người “anh em” FPGA của chúng, chỉ khác nhau về
công nghệ chế tạo. Trong mục này, chúng ta sẽ coi lại cả hai loại ASICs đều là các
ASIC lập trình được.
Một ASIC lập trình được chính một chip mà chúng ta, như một người thiết kế
hệ thống, có thể tự lập trình. Chúng ta tiến hành thiết kế đầu vào và mô phỏng. Tiếp
Tiếp cận lập trình cho FPGA từ Spartan -3 8

theo, một phần mềm đặc biệt tạo ra một chuỗi các bit mô tả thêm mở rộng các kết
nối theo yêu cầu để thực hiện thiết kế của chúng ta - gọi là tệp cấu hình. Sau đó,
chúng ta kết nối máy tính tới chip và lập trình cho chip đó tuân theo tệp cấu hình.
Tuy nhiên công nghệ lập trình có thể có hoặc có thể không lâu dài. Do vậy
chúng ta không thể xoá bỏ những lập trình trong các ASIC lập trình được một lần.
Vì thế, ngày nay người ta thường sử dụng các loại PLD và FPGA có khả năng lập
trình lại được.
1.2.3. Các phần tử logic ASIC lập trình được
Tất cả các ASIC (hoặc PLD hoặc FPGAs) đều chứa một phần tử lôgic cơ bản.
Đó là ba kiểu phần tử lôgic cơ bản khác nhau: (1) Bộ dồn kênh cơ sở; (2) Bảng sự
thật cơ sở; (3) Phần tử logic ma trận lập trình được. Việc lựa chọn giữa các phần tử
đó phụ thuộc vào công nghệ lập trình.
1.2.4. Các phần tử vào/ra ASIC lập trình được
Tất cả ASICs lập trình được đều chứa một vài kiểu phần tử Vào/Ra (I/O) nào
đó. Các phần tử Vào/Ra đó điều khiển mức lôgic tín hiệu vào - ra của chip, nhận và
kiểm tra điều kiện của các đầu vào từ bên ngoài, cũng như bảo vệ tĩnh điện cho
chip.
Sau đây là các yêu cầu khác nhau của các loại phần tử Vào/Ra:
- Nguồn đầu ra DC: điều khiển trở kháng tải tại đầu ra DC hoặc tần số thấp
(thấp hơn 1 MHz). Ví dụ các loại trở kháng tải như LED, rơ le, mô-tơ loại nhỏ…
- Nguồn đầu ra AC: điều khiển dung kháng tải tốc độ cao (lớn hơn 1 MHz). Ví
dụ dung kháng tải các chip logic khác, bus dữ liệu hoặc bus địa chỉ, cáp ruy băng.
- Nguồn đầu vào DC: ví dụ các nguồn như chuyển mạch, cảm biến, hoặc các
chip logic khác.
- Nguồn đầu vào AC: ví dụ các nguồn như tín hiệu logic tốc độ cao (lớn hơn 1
MHz) từ các chip khác.
- Nguồn tạo xung nhịp đầu vào: ví dụ là đồng hồ xung nhịp hệ thống hoặc các
tín hiệu trên bus đồng bộ.
Tiếp cận lập trình cho FPGA từ Spartan -3 9

- Nguồn cung cấp đầu vào: chúng ta cần cấp nguồn cho phần tử Vào/Ra và các
phần tử lôgic bên trong chip, mà điện áp không bị sụt hoặc nhiễu. Ngoài ra chúng ta
có thể cũng cần một nguồn cung cấp riêng biệt để lập trình cho chip.
Các tùy chọn đối với phần tử Vào/Ra là: sự khác nhau về mức độ của nguồn,
tính tương thích với TTL, các đầu vào trực tiếp hoặc phải được điều chỉnh, các đầu
ra trực tiếp hoặc phải được điều chỉnh, phối hợp trở kháng, bảo vệ quá điện áp, điều
khiển tốc độ tròn, và quét hạn chế.
1.2.5. Các phần tử ASIC liên kết nối lập trình được
Tất cả ASIC đều chứa các phần tử liên kết nối lập trình được. Cấu trúc và sự
phức tạp của các phần tử liên kết nối phần lớn được xác định thông qua công nghệ
lập trình và kiến trúc của các phần tử lôgic cơ bản. Chất liệu mà chúng ta dùng để
xây dựng các phần tử liên kết nối là hợp kim nhôm, loại hợp kim có thể chịu được
xấp xỉ 50 mW/1 đơn vị diện tích và dung kháng là 0.2 pFcm -1. Các loại ASIC lập
trình được đời đầu tiên được xây dựng sử dụng công nghệ hai lớp kim loại; còn các
ASIC hiện nay sử dụng ba lớp kim loại hoặc nhiều hơn.
1.2.6. Phần mềm thiết kế ASIC lập trình được
Có năm thành phần cấu thành một ASIC: (1) Công nghệ lập trình, (2) Phần tử
lôgic cơ bản, (3) Phần tử Vào/Ra, (4) Phần tử liên kết nối, và (5) Phần mềm thiết kế
cho phép chúng ta lập trình ASIC. Phần mềm thiết kế thường là bị ràng buộc gần
gũi hơn với kiến trúc PLD và FPGA hơn các kiểu ASICs khác.
Đối với bất kỳ ASIC nào thì một nhà thiết kế cũng cần phần mềm thiết kế đầu
vào, một thư viện phần tử, và phần mềm thiết kế vật lý. Mỗi một nhà cung cấp
ASIC thường bán các bộ kit thiết kế bao gồm tất cả phần mềm và phần cứng mà
một người thiết kế cần đến. Rất nhiều bộ kit thiết kế này sử dụng phần mềm thiết kế
đầu vào của một công ty khác. Thường thì người thiết kế mua luôn phần mềm đó từ
nhà cung cấp ASIC. Phần mềm này được gọi là phần mềm OEM (Original
Equipment Manufacturer). Tất cả các nhà cung cấp ASIC đều có phần mềm thiết kế
vật lý của riêng mình - vì các phần mềm thiết kế như vậy mới có thể phù hợp với
các giải thuật tương ứng với kiến trúc họ.
Tiếp cận lập trình cho FPGA từ Spartan -3 10

Giản đồ đầu vào không phải là phương pháp duy nhất thiết kế đầu vào cho các
ASIC lập trình được. Một số nhà thiết kế mô tả việc điều khiển lôgic và trạng thái
máy dưới dạng các phương trình lôgic và giản đồ trạng thái. Một giải pháp khác nữa
cho thiết kế ASIC là sử dụng một trong số các ngôn ngữ mô tả phần cứng (HDL)
dựa theo một số tiêu chuẩn. Có hai dạng ngôn ngữ thông dụng. Thứ nhất là các
phần mềm được phát triển từ việc lập trình cho các ASIC loại PLD. Đó là ABEL,
CUPL, và PALASM, đó là các ngôn ngữ đơn giản và dễ học. Các ngôn ngữ này rất
mạng trong việc mô tả các máy trạng thái và tổ hợp lôgic. Thứ hai là các ngôn ngữ
HDL bao gồm VHDL và Verilog, đó là các ngôn ngữ bậc cao hơn và sử dụng phức
tạp hơn nhưng chúng có khả năng mô tả hoàn chỉnh các ASICs và cả một hệ thống.
Sau khi hoàn thành thiết kế đầu vào và tạo ra một netlist, bước tiếp theo là việc
mô phỏng. Có hai kiểu mô phỏng thường được sử dụng cho thiết kế ASIC. Kiểu mô
phỏng đầu tiên là mô phỏng lôgic theo hoạt động, chức năng, và mô phỏng thời
gian. Công cụ này có thể phát hiên bất kỳ lỗi thiết kế nào. Người thiết kế cung cấp
các tín hiệu đầu vào để mô phỏng và kiểm tra đầu ra theo yêu cầu.
Kiểu mô phỏng thứ hai, là kiểu thường sử dụng nhất trong thiết kế ASIC, đó là
một công cụ phân tích - tính toán thời gian. Công cụ phân tích - tính toán thời gian
là một thiết bị mô phỏng tĩnh và bỏ qua việc cung cấp các tín hiệu đầu vào. Thay
vào đó công cụ phân tích - tính toán thời gian kiểm tra các đường giới hạn mà ở đó
làm hạn chế tốc độ hoạt động - các đường tín hiệu gây ra độ trễ lớn.
1.3. Thiết kế logic mức thấp đầu vào (low-level design entry)
1.3.1. Giản đồ đầu vào (Schematic Entry)
Giản đồ đầu vào là phương pháp phổ biến nhất của thiết kế đầu vào đối với
các ASIC. Các ngôn ngữ HDL đang thay thế cho các giản đồ đầu vào mức cổng
thông thường, nhưng các công cụ đồ hoạ mới dựa trên các giản đồ đầu vào ngay nay
cũng đang được sử dụng để tạo ra một số lượng lớn các mã nguồn HDL.
Các giản đồ mạch được vẽ trên các sheet giản đồ. Kích thước tiêu chuẩn của
các sheet giản đồ tuân theo tiêu chuẩn ANSI A-E (chủ yếu dùng ở Mỹ) và ISO A4-
A0 (chủ yếu dùng ở châu Âu). Như trên hình 2.2 thể hiện 2 hình giống cái “cái mai”
Tiếp cận lập trình cho FPGA từ Spartan -3 11

và “cái xẻng”, đó là các biểu tượng đã được công nhận của các cổng AND, NAND,
OR và NOR.

Hình 1.2. IEEE khuyến nghị kích thước và các ký hiệu cho các cổng logic.
(a) Cổng NAND; (b) Cổng OR-độc nhất.

Hình 1.3. Các thuật ngữ được dùng trong các giản đồ mạch
Các công cụ vẽ giản đồ đầu vào cho thiết kế ASIC tương tự như thiết kế bo
mạch in PCB (Printed-Circuit Board). Trên một PCB thường chỉ có vài trăm thành
phần hoặc phần tử TTL hoặc các điện trở, tranzitor hoặc tụ điện, cuộn cảm... Nếu
chúng ta coi một cổng logic trên một ASIC tương đương với một thành phần trên
một PCB, thì một ASIC cỡ lớn chứa hàng trăm ngàn thành phần như vậy. Do vậy
để vẽ toàn bộ các phần tử của một ASIC là điều không tưởng.
1.3.1.1. Thiết kế theo thứ bậc (Hierarchical Design)
Việc thiết kế theo thứ bậc sẽ làm giảm kích thước và độ phức tạp của một giản
đồ đầu vào. Một giản đồ điện tử có thể chứa các giản đồ con. Các giản đồ con cũng
có thể chứa các giản đồ nhỏ hơn nữa.
Tiếp cận lập trình cho FPGA từ Spartan -3 12

Việc lựa chọn để thiết kế theo thứ bậc là để có thể vẽ được tất cả các thành
phần của một ASIC trên một giản đồ cực lớn không có thứ bậc dạng thiết kế phẳng.
Đối với một ASIC đời mới có chứa hàng ngàn hoặc nhiều hơn nữa các cổng logic
bằng cách sử dụng thiết kế phẳng hoặc giản đồ phẳng là điều không thể thực hiện
được. Do vậy người ta phải phân cấp thiết kế cho các giản đồ thiết kế đầu vào.
1.3.1.2. Thư viện phần tử (The Cell Library)
Các thành phần trong một giản đồ ASIC thường được chọn từ một thư viện
các phần tử logic. Các phẩn tử của thư viện cho tất cả các loại ASIC đôi khi còn
được biết đến như là các khối modul (module).
Hầu hết các công ty trong lĩnh vực ASIC đều cung cấp một thư viện các phần
tử với các cổng cơ bản được sử dụng trong giản đồ đầu vào.
Có hai vấn đề cần đặt ra đối với các thư viện giản đồ ASIC là không có qui
ước về đặt tên và không có tiêu chuẩn dành riêng cho hoạt động của phần tử.
Trong thư viện các phần tử thì các cổng logic là các phần tử cơ bản, chẳng hạn
như cổng NAND. Trong một thiết kế phân cấp ASIC thì một phần tử có thể là một
cổng NAND, một mạch flip-flop, một bộ nhân hoặc thậm chí có thể là một bộ vi xử
lý. Chính vì vậy mà chúng ta thấy rằng các thuật ngữ về phần tử đều được chấp
nhận một cách chung chung trong một giản đồ đầu vào nhiều khi gây ra sự lầm lẫn.
Thuật ngữ phần tử được dùng để biểu diễn cả các phần tử cơ bản và cả các giản đồ
con. Mặc dù chúng có khác nhau trên thực tế như chúng vẫn có mối liên quan gần
gũi, và được chấp nhận dùng chung.
1.3.1.3. Các tên gọi (Names)
Mỗi một phần tử, có thể là phần tử cơ bản hoặc không phải, khi được đặt vào
một giản đồ thiết kế ASIC đều phải có tên. Mỗi phần tử khi sử dụng đều dùng theo
một tên duy nhất và không được trùng lặp trong giản đồ đầu vào mặc dù chúng có
thể là bản sao chép của nhau từ cùng một thư viện.
Tiếp cận lập trình cho FPGA từ Spartan -3 13

1.3.1.4. Các biểu tượng và ký hiệu trên giản đồ (Schematic Icons and
Symbols)
Hầu hết các chương trình vẽ giản đồ đầu vào đều cho phép người thiết kế sử
dụng các biểu tượng đặc biệt hoặc biểu tượng tự tạo. Ngoài ra công cụ vẽ giản đồ
đầu vào cũng thường tự động tạo ra biểu tượng cho các giản đồ con để dùng trong
các giản đồ mức cao hơn. Đây được gọi là các biểu tượng gốc hoặc ký hiệu gốc.
Các đường kết nối ngoài của giản đồ con được tự động gắn thêm biểu tượng,
thường thường là một hình chữ nhật. Ví dụ về các biểu tượng và ký hiệu đối với
một phần tử có tên là DLAT được cho trong hình 2.4.

Hình 1.4. Một phần tử và các giản đồ con của nó. (a) Một thư viện giản đồ
chứa các biểu tượng dành cho các phần tử cơ bản; (b) Một giản đồ con cho một
phần tử DLAT, chứa tên của các phần tử cơ bản; (c) Biểu tượng cho phần tử
DLAT.
1.3.1.5. Các đường nối (Nets)
Các giản đồ trên hình 2.4 có chứa cả các đường nối cục bộ và đường nối bên
ngoài. Như trên hình 2.4.b đường nối cục bộ là n1, nối giữa một phần tử AND có
tên là and1 và một phần tử OR có tên là or1. Còn đường nối ngoài là đường nối
giữa một phần tử với một đường nối khác, trên hình 2.4.b là n3.
Để thuận tiện cho việc đặt tên các đường kết nối trong một giản đồ phân cấp
người ta sử dụng tên tiền tố của phần tử để đặt tên cho đường nối. Các ký tự đặc
biệt (như ‘;’ ‘/’ ‘$’ ‘#’ ...) không được dùng để đặt tên cho đường nối. Tuy nhiên
việc đặt tên thường là được thực hiện tự động thông qua công cụ vẽ giản đồ đầu
vào. Trong các ngôn ngữ HDL (VHDL và Verilog) có cách đặt tên cho đường nối
rất chính xác và chặt chẽ đã được tiêu chuẩn trong các kiến trúc phân cấp.
Tiếp cận lập trình cho FPGA từ Spartan -3 14

1.3.1.6. Các đầu nối (Connections)


Các phần tử có các đầu cuối (terminal) là các đầu vào hoặc đầu ra của phần tử
đó. Các đầu cuối (terminal) còn được biết đến dưới các tên như các chân (pin), các
đầu nối (connection), hoặc là các đầu tín hiệu (signal). Thuật ngữ chân (pin) được
sử dụng rất rộng rãi, tuy nhiên ở đây chúng ta chủ yếu sử dụng thuật ngữ đầu cuối
để tránh nhầm với thuật ngữ chân (pin) trong một ASIC đã đóng gói. Ngoài ra thuật
ngữ chân (pin) còn thường được dùng trong giản đồ đầu vào và chương trình định
tuyến đường nối chủ yếu cho các thiết kế PCB.

Hình 1.5. Ví dụ về việc sử dụng bus để đơn giản hoá một giản đồ. (a) Các
đầu nối A, B, C; (b) Các đầu nối A, B, C và DQ0 - DQ7...
1.3.2. Các ngôn ngữ thiết kế mức thấp (Low-level Design Languages)
Trong trường hợp thiết kế ASIC thì ngôn ngữ này rất quan trọng. Có hai vấn
cần đề cập đến là: việc thay đổi một giản đồ rất khó và vẫn chưa có tiêu chuẩn đối
với các ký hiệu và thông tin giản đồ dùng để lưu trữ trong một netlist. Điều này có
nghĩa là chúng ta cần phải chuyển đổi từ thiết kế mức thấp mà bạn đã sử dụng thiết
kế PLD thành một hoặc nhiều thiết kế ASIC tương ứng. Thông thường thì chúng ta
nhập nhiều PLD thành một PLD đơn lớn hơn chính là ASIC. Đó chính là ngôn ngữ
thiết kế mức thấp để chuyển đổi và đọc hiểu từ các PLD sang khuôn dạng mà bạn
có thể sử dụng được trong các hệ thống thiết kế ASIC khác.
Một số ngôn ngữ như sau:
- Ngôn ngữ ABEL: ABEL là một ngôn ngữ lập trình PLD từ các dữ liệu I/O
(Data I/O).
Tiếp cận lập trình cho FPGA từ Spartan -3 15

- Ngôn ngữ CUPL: CUPL là một ngôn ngữ thiết kế PLD từ các thiết bị logic
(Logical Devices).
Định dạng EDIF:
Đây là một tiêu chuẩn dùng để trao đổi thông tin giữa các công cụ EDA với
nhau đó là định dạng trao đổi lẫn nhau trong thiết kế điện tử - Electronic Design
Interchange Format - EDIF. Phiên bản hay được sử dụng nhất là EDIF 2 0 0 do
EIA (Electronic Industries Association - Hiệp hội công nghiệp điện tử) phát hành có
tên là Tiêu chuẩn ANSI/EIA 548-1988 - còn được gọi là EDIF 1988. Hiện nay đã
có phiên bản 3.0.0 và 4.0.0. Hầu hết các công ty trong lĩnh vực EDA đều hỗ trợ
chuẩn EDIF. Các công ty chuyên về ASIC - FPGA là Altera và Actel đều sử dụng
EDIF như là khuôn dạng netlist của họ và Xilinx cũng đã thông báo họ cũng đã đề
cập đến việc chuyển dần khuôn dạng XNF của họ sang khuôn dạng EDIF.
1.4. Tổng hợp logic (Logic Synthesis)
Tổng hợp logic cung cấp một liên kết giữa HDL và netlist tương tự như cách
một trình biên dịch C cung cấp liên kết giữa mã nguồn C và ngôn ngữ máy. Tuy
nhiên, việc so sánh song song như trên cung chỉ mang tính tương đối. C được phát
triển để sử dụng với các trình biên dịch, còn HDL thì không được phát triển để sử
dụng với các công cụ tổng hợp logic. Verilog thì được thiết kế như một ngôn ngữ
mô phỏng còn VHDL thì được thiết kế như một ngôn ngữ mô tả và dữ liệu. Cả
Verilog và VHDL đều được phát triển từ đầu thập niên 80, trước khi nó được giới
thiệu như một phần mềm thương mại dùng để tổng hợp logic. Do các ngôn ngữ
HDL đó hiện nay được sử dụng vào mục đích không phải như đúng ý đồ ban đầu,
nên hiện trạng của nó trong tổng hợp logic gần giống như các bộ biên dịch ngôn
ngữ máy tính. Do vậy tổng hợp logic buộc người thiết kế phải sử dụng một tập con
của cả Verilog và VHDL. Hiện nay, VHDL được sử dụng rộng rãi và chủ yếu ở
Châu Âu, còn Verilog thì được dùng chính ở Mỹ và Nhật. Việc này làm cho tổng
hợp logic là một vấn đề rất khó. Hiện trạng của các phần mềm tổng hợp giống như
việc một người học ngoại ngữ nhưng năm năm sau mới sử dụng đến.
Tiếp cận lập trình cho FPGA từ Spartan -3 16

Khi nói đến công cụ tổng hợp logic sử dụng HDL thì người ta thường nghĩ nó
liên quan đến phần cứng hơn là việc tổng hợp logic sẽ thực hiện trên netlist. Theo
đánh giá của các chuyên gia “ASIC học” thì phải 5 năm nữa chúng ta mới hoàn
thiện được quá trình tổng hợp logic như mong muốn.
Người thiết kế sử dụng thiết kế đầu vào dạng text hoặc đồ hoạ để tạo ra mô
hình hoạt động HDL không bao gồm bất kỳ tham chiếu nào đến các phần tử logic.
Các sơ đồ trạng thái, các mô tả đường dẫn dữ liệu đồ hoạ, các bảng sự thật, các mẫu
RAM/ROM, và các giản đồ mức cổng (gate-level) có thể sử dụng cùng với một mô
tả HDL. Mỗi khi hoàn thành một mô hình hoạt động HDL, hai thành phần yêu cầu
phải xử lý là: một bộ tổng hợp logic (bao gồm phần mềm và tài liệu đi kèm) và một
thư viện phần tử (bao gồm các phần tử logic chẳng hạn như cổng NAND, AND...)
được gọi là thư viện nguồn. Hầu hết các công ty phần mềm tổng hợp chỉ cung cấp
phần mềm. Còn hầu hết các nhà cung cấp ASIC thì chỉ cung cấp các thư viện phần
tử.
Mô hình hoạt động được mô phỏng để kiểm tra việc thiết kế theo tham số kỹ
thuật còn sau đó bộ tổng hợp logic sẽ được sử dụng để tạo ra một netlist, một mô
hình cấu trúc chỉ chứa tham chiếu đến các phần tử logic. Hiện nay không có khuôn
dạng tiêu chuẩn cho các netlist mà tổng hợp logic tạo ra, nhưng phổ biến nhất hiện
nay người ta vẫn sử dụng khuôn dạng EDIF. Một vài công cụ tổng hợp logic cũng
có thể tạo ra cấu trúc HDL (như Verilog và VHDL). Sau khi tổng hợp logic bản
thiết kế được thực hiện mô phỏng lại để so sánh với việc mô phỏng hoạt động trước
đó. Việc xếp lớp đối với bất kỳ ASIC nào đều có thể được tạo ra từ mô hình cấu
trúc sinh ra thông qua quá trình tổng hợp logic.
1.4.1. Ví dụ về tổng hợp logic
Trước hết chúng ta hãy tìm hiểu về một ví dụ của tổng hợp logic. Ở đây các
phần tử logic đều sử dụng công nghệ VLSI 1.0 m m. ASIC đầu tiên được thiết kế
bằng tay sử dụng các giản đồ đầu vào và một sổ tay dữ liệu. ASIC thứ hai sử dụng
Verilog cho thiết kế đầu vào và một bộ tổng hợp logic. Bảng 2.2 so sánh kết quả của
Tiếp cận lập trình cho FPGA từ Spartan -3 17

hai phương pháp trên. Việc tổng hợp ASIC theo phương pháp thứ hai cho kết quả là
ASIC nhỏ hơn 16% và tốc độ nhanh hơn 13% so với cách tổng hợp bằng tay.
Chúng ta cùng tìm hiểu tại sao lại có vấn đề trên. Hình 2.6 biểu diễn giản đồ
một bộ so sánh và dồn kênh được thiết kế bằng tay. Còn bên phải của hình 2.6 là mã
nguồn cũng của bộ so sánh và dồn kênh có cùng chức năng. Việc so sánh hai kết
quả cho trong bảng 2.3 đã chỉ ra lý do của phương pháp thứ hai cho sản phẩm có
kích thước nhỏ hơn, tốc độ nhanh hơn thậm chí còn sử dụng nhiều phần tử hơn.
Bảng1.2. So sánh thiết kế tổng hợp logic ASIC bằng tay và tổng hợp logic
sử dụng Verilog theo lý thuyết
Trễ đường Số các phần tử logic Số tranzitor Kích thước/
dẫn/ns (1) tiêu chuẩn tiêu chuẩn mils 2 (2)
Thiết kế bằng tay 41.6 1,359 16,545 21,877
Thiết kế tổng hợp
36.3 1,493 11,946 18,322
logic
Bảng 1.3. So sánh thiết kế tổng hợp logic ASIC bằng tay và tổng hợp logic
sử dụng Verilog trên bộ so sánh và dồn kênh trong thực tế
Trễ đường Số các phần tử logic Số tranzitor Kích thước/
dẫn/ns (1) tiêu chuẩn tiêu chuẩn mils 2 (2)
Thiết kế bằng tay 4.3 12 116 68.88
Thiết kế tổng hợp logic 2.9 15 66 46.43
// Mã nguồn chương trình
// comp_mux.v
module comp_mux(a, b, outp);
input [2:0] a, b;
output [2:0] outp;
function [2:0] compare;
input [2:0] ina, inb;
begin
if (ina <= inb) compare = ina;
Tiếp cận lập trình cho FPGA từ Spartan -3 18

else compare = inb;


end
endfunction
assign outp = compare(a, b);
endmodule
Hình 1.6. Thiết kế đầu vào bằng tay và sử dụng Verilog
Tiếp cận lập trình cho FPGA từ Spartan -3 19

1.4.2. VHDL và tổng hợp logic


Hầu hết các bộ tổng hợp logic bắt buộc chúng ta phải tuân theo một tập các
qui tắc khi chúng ta sử dụng một hệ thống logic để bảo đảm là chúng ta tổng hợp
tuân theo đúng như trong mô tả hoạt động của nó. Tập các qui tắc để sử dụng theo
tiêu chuẩn VHDL IEEE hệ thống 9 giá trị.
- Chúng ta có thể sử dụng các giá trị logic tương ứng với các trạng thái ‘1’,
‘H’, ‘0’ và ‘L’ trong bất kỳ cách nào.
- Một vài công cụ tổng hợp không chấp nhận trạng thái logic ‘U’ khởi động.
- Chúng ta có thể sử dụng các trạng thái logic ‘Z’, ‘X’, ‘W’ và ‘-’ trong tín
hiệu và các phép gán biến theo bất kỳ cách nào. ‘Z’ được tổng hợp thành 3
trạng thái logic.
- Các trạng thái ‘X’, ‘W’ và ‘-’ được xử lý như các giá trị không biết hoặc
không quan tâm.
Các giá trị ‘Z’, ‘X’, ‘W’ và có thể sử dụng trong các mệnh đề điều kiện chẳng
hạn như so sánh trong các lệnh if hoặc case. Tuy nhiên một vài công cụ tổng hợp sẽ
bỏ qua chúng và chỉ tuân theo quanh các bit ‘1’ và ‘0’. Do vậy, một thiết kế được
tổng hợp có thể hoạt động khác với khi mô phỏng nếu một tác nhân kích thích sử
dụng ‘Z’, ‘X’, ‘W’ hoặc ‘-’. Các đóng gói tổng hợp IEEE cung cấp một hàm dành
riêng cho việc so sánh là STD_MATCH.
Trong VHDL có các mô hình tổng hợp như sau:
- Khởi động và khởi động lại (Initialization and Reset)
- Tổ hợp Tổng hợp logic trong VHDL (Combinational Logic Synthesis in
VHDL)
- Các bộ dồn kênh trong VHDL (Multiplexers in VHDL)
- Các bộ giải mã trong VHDL (Decoders in VHDL)
- Các bộ công trong VHDL (Adders in VHDL)
- Logic tuần tự trong VHDL (Sequential Logic in VHDL)
- Thuyết minh trong VHDL (Instantiation in VHDL)
Tiếp cận lập trình cho FPGA từ Spartan -3 20

- Các thanh ghi dịch và tạo xung nhịp trong VHDL (Shift Registers and
Clocking in VHDL)
- Các bộ cộng và hàm thuật toán (Adders and Arithmetic Functions)
- Các bộ cộng/trừ (Adder/Subtracter)
1.4.3. Tổng hợp bộ nhớ
Có nhiều phương pháp tổng hợp bộ nhớ:
- Sử dụng thành phần logic ngẫu nhiên flip-flop hoặc bộ chốt dữ liệu.
- Sử dụng các tệp thanh ghi trong datapath.
- Sử dụng các thành phần RAM tiêu chuẩn.
- Sử dụng các bộ biên dịch RAM.
Phương pháp đầu tiên sử dụng các vector lớn hoặc các ma trận trong mã lệnh
HDL. Bộ tổng hợp sẽ ánh xạ các phần tử đó tới ma trận của các flip-flop hoặc các
bộ chốt dữ liệu. Phương pháp này độc lập với bất kỳ phần mềm nào hoặc kiểu ASIC
nào và dễ sử dụng nhất nhưng phạm vi ứng dụng không hiệu quả. Một flip-flop
chiếm diện tích sử dụng 10 đến 20 lần so với một cell RAM tĩnh có 6 tranzitor.
Phương pháp thứ hai sử dụng tổng hợp trực tiếp để tổng hợp bộ nhớ thành các
thành phần datapath. Phương pháp này hiệu quả hơn phương pháp trước, tuy nhiên
bị phụ thuộc vào phần mềm và công nghệ ASIC mà chúng ta sử dụng.
Phương pháp thứ ba sử dụng các thành phần tiêu chuẩn được các nhà sản xuất
ASIC cung cấp kèm. Phương pháp này rất hiệu quả nhưng phụ thuộc vào công nghệ
của từng hãng sản xuất.
Phương pháp cuối cùng, đó là sử dụng bộ biên dịch RAM, đây là phương pháp
hiệu quả nhất. Nó phụ thuộc vào khả năng của trình biên dịch cùng với công cụ tổng
hợp.
1.5. Mô phỏng (Simulation)
Thông thường chúng ta thường sử dụng các hệ thống mẫu để kiểm tra các thiết
kế của mình, mà thường là các đế thử (bread-board), sau đó lắp các IC và đấu dây
trên các đế thử đó. Tuy nhiên cách làm này chỉ áp dụng đối với các mạch nhỏ có vài
phần tử dạng TTL. Trong thực tế, để thực hiện việc này đối với một ASIC thì sử
Tiếp cận lập trình cho FPGA từ Spartan -3 21

dụng đế thử là điều không thể thực hiện được. Do vậy, đối với thiết kế ASIC, chúng
ta phải dùng đến các phương pháp mô phỏng.
1.5.1. Phân loại mô phỏng (Type of Simulation)
Có thể phân chia mô phỏng theo loại hoặc theo phương pháp:
- Mô phỏng hoạt động (Behavioral simulation)
- Mô phỏng chức năng (Functional simulation)
- Phân tích thời gian tĩnh (Static timing analysis)
- Mô phỏng mức cổng/logic (Gate-level/Logic simulation)
- Mô phỏng mức chuyển mạch (Switch-level simulation)
- Mô phỏng mức tranzitor hoặc mức mạch (Transistor-level or Circuit-level
simulation)
Trên đây là thứ tự các mức độ mô phỏng từ cao xuống thấp, các mức này có
độ chính xác tăng dần đồng thời độ phức tạp và thời gian cũng tăng theo.
Có nhiều cách để tạo ra một mô hình mô phỏng ảo cho một hệ thống. Phương
pháp thường dùng nhất là mô hình hoá các mô-đul lớn của hệ thống thành một khối
(giống như một hôp đen - black box) chỉ có các đầu vào và ra. Đây chính là mô
phỏng hoạt động (Behavioral simulation), chủ yếu là sử dụng ngôn ngữ VHDL và
Verilog.
Mô phỏng chức năng (Functional simulation) sẽ bỏ qua thời gian hoạt động và
các trễ trong từng mô-đul mà thiết lập mức trễ với các giá trị cố định (ví dụ là 1ns
chẳng hạn). Sau khi thực hiện mô phỏng hoạt động và chức năng hoàn thành, cho
kết quả tốt thì người mới kiểm tra thời gian hoạt động của từng khối.
Tại thời điểm này, hệ thống được phân chia thành các ASIC và việc mô phỏng
thời gian được thực hiện trên từng ASIC độc lập. Một trong các phương pháp mô
phỏng thời gian là sử dụng phương pháp phân tích thời gian tĩnh (Static timing
analysis) trong trường hợp hoạt động tĩnh, sau đó tính toán thời gian trễ cho từng
phần tử. Phương pháp này được gọi là phân tích thời gian tĩnh (Static timing
analysis) vì nó không đòi hỏi phải tạo ra một tập các phép thử nghiệm đối với các
ASIC lớn.
Tiếp cận lập trình cho FPGA từ Spartan -3 22

Tiếp theo là mô phỏng mức cổng/logic (Gate-level/Logic simulation) có thể


cũng được dùng để kiểm tra thời gian hoạt động của một ASIC. Trong mô phỏng
mức cổng/logic một cổng logic hoặc phần từ logic (NAND, NOR hoặc tương
đương) được kiểm tra hoạt động giống như mô hình hộp đen theo các chức năng
nhưng các giá trị tín hiệu đầu vào thay đổi được.
Mô phỏng mức chuyển mạch (Switch-level simulation) cung cấp mức độ chính
xác về thời gian cao hơn mô phỏng mức cổng/logic nhưng không có khả năng sử
dụng trễ của các phần tử logic làm tham số cho việc mô phỏng.
Chính xác nhất và cũng phức tạp nhất, mất nhiều thời gian nhất là mô phỏng
mức tranzitor hoặc mức mạch (Transistor-level or Circuit-level simulation).
Phương pháp này yêu cầu các mô hình tranzitor cụ thể, mô tả điện áp phi tuyến và
các tham số dòng cung cấp của chúng. Mỗi phương pháp thường đi kèm với một
công cụ phần mềm khác nhau. Tuy nhiên có thể kết hợp một số phương pháp mô
phỏng vào việc mô phỏng một ASIC.
Tiếp theo chúng ta sẽ giới thiệu về một số yêu cầu về hệ thống logic và mô
hình ứng dụng trong quá trình mô phỏng.
1.5.2. Lựa chọn các hệ thống logic (Logic Systems)
Như chúng ta đã biết trong thực tế, các tín hiệu số là các mức điện áp (hoặc
dòng) tương tự mà thay đổi liên tục như sự thay đổi của chúng. Việc mô phỏng số
giả sử là các tín hiệu số chỉ là một tập các giá trị logic (hoặc là các trạng thái logic)
từ một hệ thống logic. Do vậy khi lựa chọn một hệ thống logic để mô phỏng rất
quan trọng. Nếu quá nhiều giá trị logic thì sẽ làm cho việc mô phỏng trở nên phức
tạp và tốc độ bị chậm lại. Nếu ít giá trị quá thì sẽ làm giảm độ chính xác của mô
phỏng không phản ánh đúng hoạt động của phần cứng.
Ví dụ về một hệ thống 4 giá trị logic cho trong bảng 1.4.
Tiếp cận lập trình cho FPGA từ Spartan -3 23

Bảng 1.4. Hệ thống 4 mức logic


Trạng thái logic Mức logic Giá trị logic
0 Không (zero) Không (zero)
1 Một (one) Một (one)
X Không (zero) hoặc Một (one) Không rõ
Z Không (zero), Một (one) hoặc không có gì Trở kháng cao
1.5.3. Mô phỏng logic hoạt động như thế nào
Hầu hết các loại mô phỏng số đều sử dụng mô phỏng theo hoạt động sự kiện
(event-driven). Khi một nút mạch thay đổi giá trị theo thời gian, còn giá trị mới là
một tập được biết đến dứi dạng các sự kiện. Sự kiện được lập lịch bằng cách đưa
chúng vào một hàng đợi sự kiện hoặc danh sách sự kiện. Khi thời gian được chỉ
định đến, giá trị logic của nút được thay đổi. Sự thay đổi tác động đến các phần tử
logic ở nút đó như một giá trị đầu vào. Tất cả các phần tử logic bị tác động phải
được đánh giá, việc này để có thể thêm các sự kiện vào danh sách sự kiện. Mô
phỏng bảo đảm việc kiểm tra thời gian hiện thời, bước thời gian và danh sách sự
kiện dùng để giữ các sự kiện sắp xảy ra. Đối với mỗi một nút mạch thì mô phỏng
bảo đảm một bản ghi trạng thái logic và độ lớn của nguồn hoặc các nguồn cấp cho
nút. Khi một nút thay đổi trạng thái logic thì sẽ gây ra một sự kiện.
1.5.4. Các mô hình phần tử (Cell models)
Có nhiều loại mô hình phần tử khác nhau, đó là:
- Mô hình nguyên thuỷ, là mô hình sinh ra thông qua thư viện ASIC và mô
tả chức năng và đặc điểm của mỗi một phần tử logic (NAND, D flip-flop,
…) sử dụng các hàm nguyên thuỷ.
- Mô hình VHDL và Verilog sinh ra thông qua thư viện ASIC từ các mô
hình nguyên thuỷ.
Tiếp cận lập trình cho FPGA từ Spartan -3 24

- Các mô hình độc quyền sinh ra thông qua các thư viện dùng để mô tả các
phần tử logic nhỏ hoặc các chức năng lớn như các bộ vi xử lý.
1.5.5. Các mô hình trễ (Delay models)
Mô hình trễ phần tử được dùng để tính toán khoảng trễ của phần tử logic.
Chúng ta sử dụng thuật ngữ mô hình thời gian (timing model) để mô tả trễ bên
ngoài các phần tử logic và mô hình trễ (Delay models) để mô tả trễ bên trong mỗi
phần tử logic. Các thuật ngữ này hay được sử dụng lẫn lộn và không được chuẩn
hoá, thực tế chúng có một số điểm khác nhau như sau:
- Trễ từ chân đến chân (pin-to-pin delay) là một khoảng trễ giữa một chân
đầu vào và một chân đầu ra của một phần tử logic.
- Trễ chân (pin delay) là khoảng trễ dồn vào một chân cố định của một phần
tử logic (thường là một đầu vào).
- Trễ đường kết nối (net dalay) hoặc trễ đường dây (wire delay) là khoảng
trễ bên ngoài của một phần tử logic.
Tiếp cận lập trình cho FPGA từ Spartan -3 25

CHƯƠNG II: GIỚI THỆU MỘT SỐ CÔNG NGHỆ


MỚI LIẾN QUAN ĐẾN THIẾT KẾ ASIC HIỆN NAY

2.1. Giới thiệu công nghệ FPGA


2.1.1. Giới thiệu FPGA
Như đã giới thiệu trong chương 1 trước về môi trường thiết kế phần cứng,
phương pháp để thiết kế mạch và mối quan hệ hữu cơ giữa ngôn ngữ VHDL và
phần cứng, trong mục này chỉ có ý định giới thiệu về các mạch phần cứng được xây
dựng dựa trên cộng nghệ FPGA. Đây là công nghệ tiên tiến đã được nghiên cứu rất
nhiều và có ứng dụng rộng rãi trên thế giới.
Công nghệ FPGA (Field-Programmable Gate Array) đã xuất hiện như một
giải pháp cơ bản cho vấn đề tranh thủ thời gian và chi phí ban đầu thấp. Nó cho
phép chế tạo ngay và giá thành sản phẩm thấp, tạo nên sức cạnh tranh lớn trên thị
trường. FPGA là một thiết bị cấu trúc logic có thể được người sử dụng lập trình trực
tiếp mà không phải sử dụng bất kỳ một công cụ chế tạo mạch tích hợp nào.
2.1.1.1. FPGA là gì?
FPGA được công ty Xilinx giới thiệu đầu tiên vào năm 1985. Hiện nay FPGA
đã được nhiều công ty phát triển là AcTel, Altera, Plus Logic, AMD,…
Giống như MPGA, một FPGA gồm một dãy các phần tử rời rạc có thể được
kết nối với nhau theo một cách chung. Và cũng giống như PLD, các kết nối giữa các
phần tử có thể lập trình được. Hình 2.1. giới thiệu về mô hình tổng quát của một
FPGA. Trong đó có các khối:
- Các khối logic (logic block): cấu trúc và nội dung của được gọi là kiến trúc
của nó. Kiến trúc của khối logic có thể được thiết kế theo nhiều cách khác nhau.
Một số khối logic có thể chỉ là các cổng NAND 2 đầu vào, tuy nhiên cũng có thể nó
là một bộ dồn kênh (multiplexer). Trong một số loại FPGA các khối logic có thể có
Tiếp cận lập trình cho FPGA từ Spartan -3 26

cấu trúc hoàn toàn giống như PAL. Hầu hết các khối logic chứa một số loại flip-flop
để hỗ trợ cho việc thực hiện các mạch tuần tự.
- Các nguồn tài nguyên kết nối: cấu trúc và nội dung của các nguồn kết nối
trong FPGA được gọi là kiến trúc routing (routing architecture). Kiến trúc routing
gồm các đoạn dây nối và các chuyển mạch lập trình được. Các chuyển mạch lập
trình được có thể có nhiều cấu tạo khác nhau. Giống như khối logic, có nhiều cách
để thiết kế kiến trúc routing.
Tài
Logic nguyên
kt ni
I/O Cell

Hình 2.1. Mô tả mô hình lý thuyết của một FPGA


2.1.1.2. Ứng dụng của FPGA
FPGA có thể sử dụng trong hầu hết các ứng dụng hiện đang dùng MPGA,
PLD và các mạch tích hợp nhỏ.
1. Các mạch tích hợp ứng dụng đặc biệt: FPGA là một phương tiện tổng quát
nhất để thực hiện các mạch logic số. Chúng đặc biệt thích hợp cho việc thực hiện
các ASIC.
Tiếp cận lập trình cho FPGA từ Spartan -3 27

2. Thiết kế mạch ngẫu nhiên: mạch logic ngẫu nhiên thường được thực hiện
bằng PAL. Nếu tốc độ của mạch không đòi hỏi khắt khe thì mạch có thể thực hiện
thay thế bằng FPGA.
3. Thay thế các chíp tich hợp nhỏ cho mạch ngẫu nhiên: các mạch hiện tại
trong các sản phẩm thương mại thường chứa nhiều chíp SSI. Trong nhiều trường
hợp các chip này SSI này có thể được thay thế bằng FPGA và kết quả là giảm diện
tích của bo mạch đi đáng kể.
4. Chế tạo mẫu: FPGA rất lý tưởng cho các ứng dụng tạo sản phẩm mẫu. Giá
thành thực hiện thấp, thời gian ngắn chính là ưu điểm rất lớn của FPGA.
5. Máy tính dựa trên FPGA: một loại máy tính mới có thể được chế tạo với các
FPGA có thể tái lập trình ngay trên mạch FPGA. Các máy này có một bo mạch
chứa các FPGA với các chân nối với các chip lân cận giống như thông thường.
6. Tái cấu hình phần cứng trực tuyến: FPGA cho phép có thể thay đổi theo ý
muốn cấu trúc của một máy đang hoạt động. Ứng dụng thích hợp nhất là những
FPGA có chuyển mạch lập trình được.
2.1.2. Các loại FPGA và công nghệ lập trình
Có nhiều cách thực hiện các phần tử lập trình, các công nghệ lập trình hiện
đang được sử dụng là RAM tĩnh, cầu chì ngịch (anti-fuse) EPROM tranzitor và
EEPROM tranzitor. Mặc dù công nghệ lập trình khác nhau, tất cả các phần tử lập
trình đều có chung tính chất chung là có thể cấu hình được trong một trong hai trạng
thái ON hoặc OFF. Các phần tử lập trình được dùng để thực hiện các kết nối lập
trình được giữa các khối logic của FPGA, còn FPGA thông thường có thể hơn
100.000 phần tử lập trình. Có thể tùy thuộc vào ứng dụng cụ thể và có các số lượng
phần tử lập trình có thể có các đặc tính khác. Về mặt chế tạo, các phần tử lập trình
nếu có thể chế tạo theo công nghệ CMOS chuẩn là tốt nhất.
2.1.2.1. Công nghệ lập trình dùng RAM tĩnh (SRAM)
Công nghệ lập trình dùng SRAM được sử dụng trong các FPGA của nhiều
công ty như Xilinx. Trong các FPGA này, các kết nối lập trình được làm bằng các
Tiếp cận lập trình cho FPGA từ Spartan -3 28

tranzitor truyền (pass-transistor), các cổng cho phép truyền (pass-gate), hay các bộ
dồn kênh (multiplexer), tất cả đều được điều khiển bằng các ô nhớ (cell) SRAM.
Các chip được thực hiện theo công nghệ SRAM có diện tích khá lớn, bởi vì
cần ít nhất 5 tranzitor cho mỗi ô nhớ. Ưu điểm chính của công nghệ này cho phép
FPGA có thể tái cấu hình ngay trên mạch rất nhanh và nó có thể được chế tạo bằng
công nghệ CMOS chuẩn.
D©y nèi
RAM RAM
RAM Cell Cell
Cell
MUX

D©y nèi D©y nèi RAM


D©y nèi D©y nèi Cell
D©y nèi
a. Transistor truyÒn dÉn b. Cæng transistor c. Bé dån kªnh
Hình 6.2. Công nghệ lập trình dùng SRAM
2.1.2.2. Công nghệ lập trình dùng cầu chì nghịch (anti-fuse)
Công nghệ lập trình dùng cầu chì nghịch (anti-fuse) được sử dụng trong các
FPGA của Actel. Tuy anti-fuse được sử dụng trong các loại FPGA này có cấu tạo
khác nhau, nhưng chức năng của chúng là như nhau. Một Anti-fuse bình thường sẽ
ở trạng thái trở kháng cao, nhưng có thể bị biến thành trạng thái điện trở thấp khi
được lập trình ở điện thế cao.
Anti-fuse của Actel được gọi là PLICE (Programmable Low-Impedance
Circuit Element). Nó có cấu trúc hình chữ nhật gồm 3 lớp: lớp dưới cùng chứa silic
mang điện tích dương, lớp giữa là một lớp điện môi, lớp trên cùng là poly-silic.
Tiếp cận lập trình cho FPGA từ Spartan -3 29

Hình 2.3. Công nghệ lập trình cầu chì nghịch anti-fuse PLICE của Actel
(a) Mặt cắt ngang.
(b) Cấu trúc các lớp cầu chì nghịch: + Lớp điện môi ONO (Oxy-Nitơ-Oxy)
cách điện chiều dày nhỏ hơn 10nm; + Lớp dưới mang điện tích dương; + Lớp trên
là Poly-Silic.
(c) Một cầu chì nghịch nhìn từ phía trên xuống giống như một công tắc
Ưu điểm công nghệ cầu chì nghịch anti-fuse là diện tích của các chip rất nhỏ
so với các công nghệ khác. Tuy nhiên bù lại cần phải có không gian lớn cho các
tranzitor điện thế cao cần để giữ cho dòng và áp cao lúc lập trình. Nhược điểm của
công nghệ này là qui trình chế tạo chúng cần phải thay đổi so với qui trình chế tạo
CMOS.
2.1.2.3. Công nghệ lập trình dùng EPROM và EEPROM
Công nghệ lập trình dùng EPROM và EEPROM được sử dụng chủ yếu trong
các FPGA của Altera. Công nghệ này giống như sử dụng trong bộ nhớ EPROM.
Không giống như tranzitor COM, các tranzitor EPROM gồm hai cực, một cực treo
(floating-gate) và một cực chọn (select-gate). Cực treo được đặt giữa cực chọn và
kênh của tranzitor, nó được gọi như vậy vì nó không có kết nối điện đến bất kỳ
mạch nào.

Hình 2.4. Tranzitor dùng công nghệ lập trình EPROM và EEPROM
(a) Với điện áp lập trình cao (>12V) V PP cung cấp tới cực máng các điện tử có
đủ năng lượng để nhảy vào cổng treo gate 1; (b) Các điện tử ở cổng gate 1 tăng lên
Tiếp cận lập trình cho FPGA từ Spartan -3 30

ở điện áp giữ mà tạo cho các transistor khoá cho mức điện áp hoạt động bình
thường; (c) Ánh sáng cực tím cung cấp năng lượng đủ để các phần tử ở cổng gate 1
nhảy về vị trí cũ cho phép transistor hoạt động bình thường.
Ưu điểm của tranzitor EPROM là chúng có thể tái lập trình mà không cần bộ
nhớ bên ngoài. Tuy nhiên không giống như SRAM, tranzitor EPROM không thể
được lập trình lại ngay trên bo mạch (in-circuit).
Phương pháp dùng EEPROM tương tự như công nghệ EPROM, chỉ khác là
diện tích của tranzitor EEPROM chiếm diện tích gấp hai lần diện tích chíp so với
tranzitor EPROM và cần nhiều nguồn điện thế hơn các loại khác.
2.1.2.4. Tóm tắt các loại FPGA trên thị trường
Bảng 6.1. Tóm tắt các họ FPGA trên thị trường
Công ty Kiến trúc tổng quát Kiểu khối logic Công nghệ lập trình
Xilink Symmetrical Array Look-up Table Static RAM
Altera Hierachical-PLD PLD Block EPROM
Actel Row-based Multiplexers-Based Anti-fuse
Plessey Sea-of-gates NAND-gate Static RAM
Plus Hierachical-PLD PLD Block EPROM
AMD Hierachical-PLD PLD Block EPROM
QuickLogic Symmetrical Array Multiplexer-Based Anti-fuse
Algotronix Sea-of-gates Multiplexers & Static RAM
Based Gates
Concurent Sea-of-gates Multiplexers & Static RAM
Based Gates
Crosspoint Row-based Transistor Pairs & Anti-fuse
Multiplexers
2.1.2.5. Các loại FPGA và giới thiệu công nghệ lập trình
Có nhiều loại FPGA của các công ty khác nhau, tuy nhiên chúng có thể được
chia thành 4 loại chính như sau (xem hình 2.2):
- Cấu trúc mảng đối xứng (symmetrical array)
Tiếp cận lập trình cho FPGA từ Spartan -3 31

- Cấu trúc hàng (row-based)


- Cấu trúc PLD phân cấp (hierachical PLD)
- Cấu trúc đa cổng (sea-of-gate)
2.2. Giới thiệu phương pháp thiết kế ASIP cho các hệ thống nhúng
Một thách thức mà nhiều người đã từng đề cập đến trong quá trình thiết kế bộ
xử lý để nhận được những kết quả tốt nhất có thể đối với phạm vi ứng dụng điển
hình cuối cùng cần đạt được nói chung là mô tả theo những chuẩn đánh giá. Việc
thu được kết quả tốt nhất có thể lần lượt trở thành một sự thoả hiệpphức tạp giữa
tính tổng quát của những bộ xử lý và đặc trưng vật lý của chúng. Trong những năm
gần đây, các bộ vi xử lý có tập lệnh chuyên dụng - ASIP (Application-Specific
Intruction-Set Processor) có sự phát triển đặc biệt trong ngành sản xuất chip đã và
đang được nghiên cứu. Trong mục này, chỉ chủ yếu giới thiệu một kiểu kiến trúc và
phương pháp luận được dùng để thiết kế ASIPs trong phạm vi các bộ điều khiển.
2.2.1. Giới thiệu chung
Nhiều hệ thống nhúng bị hạn chế về ràng buộc đối với giá thành sản phẩm.
Giá biểu hiện theo hai đặc trưng chính : giá của chương trình nguồn và giá của bộ
xử lý. Do vậy việc dung hoà giữa giá thành sản phẩm và chất lượng cũng như ứng
dụng của sản phẩm rất dễ đối với các nhà thiết kế để có thể tìm ra giải pháp tốt nhất
cho bộ xử lý tương ứng với việc thực hiện một ứng dụng trung bình và công suất
tiêu thụ của nó. Do vậy, phải chọn giải pháp sử dụng bộ đồng vi xử lý ASIC (co-
procesor ASIC) hoặc là ứng dụng đó cần phải nâng cấp thành một bộ xử lý có khả
năng hoạt động cao hơn với giá thành nói chung cao hơn.
Nền công nghiệp bán dẫn nói chung chủ yếu nhằm vào các bộ xử lý mà được
sản xuất cho một phạm vi ứng dụng đặc biệt như các chip DSP. Các chip DSP
thông thường là những bộ vi xử lý với những đặc tính và những kiến trúc đặc biệt
chủ yếu phục vụ cho việc xử lý tín hiệu số. Tuy nhiên, việc thiết kế chuyên biệt về
các bộ xử lý không giải quyết được tất cả các vấn đề. Còn những ứng dụng phù hợp
với các đặc điểm riêng của một bộ xử lý, thì giá thành của bộ xử lý đó đắt hơn do
chỉ được sản xuất theo đơn đặt hàng.
Tiếp cận lập trình cho FPGA từ Spartan -3 32

Về mặt thuật ngữ nói chung, việc thiết kế ASIP chính là việc tạo ra một bộ vi
xử lý mới, với tập lệnh và kiến trúc được tùy biến cho phù hợp với một số những
ứng dụng chuyên biệt.
Mục đích của chúng ta là có thể tối ưu hóa hiệu quả chương trình nguồn và
khả năng hoạt động của một ứng dụng đã cho sao cho bảo đảm các yêu cầu sau:
- Hiệu quả hệ thống (giá thành, kích thước chương trình nguồn, khả năng thực
hiện, và điện năng tiêu thụ) bảo đảm chấp nhận được cho các hệ thống nhúng.
- Tuỳ biến theo yêu cầu khách hàng càng bản địa hoá càng tốt.
- Giảm tối đa sự thay đổi môi trường phần mềm càng tốt (bảo đảm tính tương
thích ngược của các phần mềm).
- Có thể linh hoạt thay đổi trong quá trình sản xuất (có thể tùy biến chương
trình, mặt nạ lập trình được, …)
2.2.2. Công nghệ thiết kế ASIP
Công nghệ thiết kế ở đây là việc “tùy biến” một bộ xử lý hiện tại hơn là tổng
hợp một bộ xử lý mới với một tập lệnh và kiến trúc mới đã được tối ưu hóa cho một
nhóm các ứng dụng chuẩn dùng để đánh giá.
2.2.2.1. Sơ đồ luồng thiết kế

Thiết kế theo phần mềm truyền thống

Chương trình định dạng tập lệnh mới

Quá trình gia công tạo tập lệnh mới

Chuỗi lệnh
Các hàm
thực hiện

Tuỳ biến bộ vi xử lý với tập lệnh mới

Cập nhật phần mềm với tập lệnh


mi
Tiếp cận lập trình cho FPGA từ Spartan -3 33

Hình 2.5. Sơ đồ luồng thiết kế ASIP


Tiếp cận lập trình cho FPGA từ Spartan -3 34

Sơ đồ luồng thiết kế ASIP được cho trong hình 2.5. Việc thiết kế ban đầu sử
dụng những phần mềm cơ sở (firmware) theo các phương pháp truyền thống
(Traditional Software Design), sau đó một chương trình định dạng tập lệnh mới
(New Instruction Identification) sẽ gia công để tạo ra tập lệnh mới chuyên biệt. Rồi
bộ xử lý đợc tùy biến thông qua việc bổ sung các lệnh chuyên biệt về ứng dụng
(Customize CPU). Cuối cùng, phần mềm cơ sở (firmware) được cập nhật để có thể
sử dụng được các lệnh mới.
2.2.2.2. Định dạng tập lệnh mới
Tập lệnh mới nói chung có một trong số hai đặc trưng sau. Chúng hoặc là một
tiến trình con trong phần mềm cơ sở (firmware) mà được sử dụng thường xuyên
hoặc là một chuỗi các lệnh dùng chung trong ứng dụng. Ví dụ về tiến trình con bao
gồm các trình điều khiển thiết bị (device driver), các phần tử tính toán cơ bản, các
bộ hẹn giờ, và các hệ điều hành nguyên thuỷ. Ví dụ chuỗi các lệnh thường dùng bao
gồm các lệnh dịch và cộng (shift-and-add) thường được sử dụng trong các bộ lọc
số, vòng qui không, bộ chuyển đổi kiểu dữ liệu (ADC hoặc DAC), dữ liệu định
dạng đối với cổng Vào/Ra (I/O), kiểm tra tín hiệu.
2.2.2.3. Kiến trúc bộ xử lý
Phương pháp thiết kế ở đây phụ thuộc vào kiến trúc bộ xử lý đã có cố định
một tập lệnh và đường dẫn dữ liệu, tuy nhiên vẫn cho phép bổ sung các phần tử
logic điều khiển và đường dẫn dữ liệu mới thông qua việc lập trình cho phần cứng.
Nhưng, việc này được hoàn thành mà không làm thay đổi kiến trúc của toàn bộ bộ
vi xử lý. Hai phương pháp tuỳ biến thiết kế kiến trúc bộ xử lý được giới thiệu trong
hình 2.6 và hình 2.7.
Những kiến trúc này không làm ảnh hưởng đến bất kỳ vùng cấm nào trong
khối giải mã lệnh, đường dẫn dữ liệu hoặc bus hệ thống. Do vậy, các kiến trúc này
phù hợp với đa số các bộ xử lý công nghiệp.
Việc tính toán chuyên biệt cũng có thể được thực hiện trên một thiết bị ngoại
vi hoạt động thông qua khối logic lập trình được.
Tiếp cận lập trình cho FPGA từ Spartan -3 35

a. Kiến trúc bộ giải mã tĩnh (Static decode Architecture)

Khối điều khiển Khối đường dẫn dữ


liu
Khối giải mã lệnh lôgic
cố định

Bộ chọn/Kết hợp

starti donei
Bus
điều
khiển
Khối logic lập trình được Bus 1 Bus N

Các tín hiệu điều kiện và dữ liệu


Lôgic cố định Lôgic lập trình
c
Hình 2.6. Kiến trúc giải mã tĩnh (Static-Decode Architecture)
Kiểu kiến trúc này (hình 2.6) chỉ cho phép một tập các mã lệnh đã được xác
định trước sẽ được sử dụng cho các lệnh mới, như vậy cần phải có một cấu trúc giải
mã lệnh hiệu quả. Nếu có bất kỳ mã lệnh nào được nạp vào và giải mã, thì tín hiệu
starti dành cho mã lệnh i được kích hoạt. Cùng lúc đó, bộ xử lý giao việc điều khiển
của các tín hiệu điều khiển đường dẫn dữ liệu đến khối logic lập trình được. Khi nào
mã lệnh i hoàn thành, nó kích hoạt tín hiệu donei riêng, sau đó điều khiển ngược lại.
Các lệnh thực không truy nhập đến các khối chức năng được thực hiện thông qua
khối logic lập trình được. Kiến trúc này cho hiệu quả thực hiện tốt hơn, nhưng lại
phải chi phí nhiều cho việc định vị trước số lệnh mới để có thể sử dụng khối logic
lập trình được.
b. Kiến trúc bộ giải mã động (Dynamic decode Architecture)
Kiểu kiến trúc này (hình 2.7) linh hoạt hơn và cho phép các mã lệnh mới được
định nghĩa trên cơ sở ứng dụng. Việc giải mã các lệnh mới được thực hiện thông
qua khối logic lập trình được. Khi một lệnh như vậy được nạp vào, bộ xử lý không
thể giải mã lệnh đó, tiếp theo nó đưa ra một tín hiệu “bẫy” trap bằng việc gửi một
Tiếp cận lập trình cho FPGA từ Spartan -3 36

tín hiệu đến khối logic lập trình được. Nếu khối logic lập trình được có thể giải mã
lệnh đó, thì sau đó lệnh đó được kích hoạt. Nếu lệnh đó chưa được khối logic lập
trình được nhận dạng, thì nó kích hoạt một tín hiệu “bẫy” trap’ để hướng dẫn cho
khối giải mã lệnh lôgic cố định khởi động quá trình nhận dạng lệnh mới.

Khối điều khiển Khối đường dẫn dữ


liu
Khối giải mã lệnh lôgic
cố định

Bộ chọn/Kết hợp

trap trap'
Bus
done điều
khiển
Khối logic lập trình được Bus 1 Bus N

Các tín hiệu điều kiện và dữ liệu


Lôgic cố định Lôgic lập trình
c
Hình 2.7. Kiến trúc giải mã động (Dynamic-Decode Architecture)
2.2.2.4. Cập nhật phần mềm cơ sở
Mỗi một lần một lệnh mới được thực hiện trong bộ xử lý, thì phần mềm nhúng
cơ sở lại cần cập nhật sửa đổi để sử dụng lệnh mới này. Tuỳ thuộc vào phương pháp
trên phần mềm được sử dụng, có ba cách thực hiện việc cập nhật này.
Thứ nhất, nếu phần mềm cơ sở là ngôn ngữ dạng đóng gói hoàn chỉnh hoặc
các lệnh mới chỉ liên quan đến một phần của phần mềm cơ sở thì chỉ có phần mã
lệnh đóng gói được thay đổi.
Nếu phần mềm sử dụng một chương trình biên dịch ngôn ngữ bậc cao như C
và nếu các lệnh mới có thể áp dụng được vào các ứng dụng khác, thì sau đó các lệnh
mới có thể được bổ sung vào chương trình biên dịch đó.
Tiếp cận lập trình cho FPGA từ Spartan -3 37

Khi các chương trình biên dịch được cập nhật lại trở thành các công cụ dùng
chung, thì một bộ tham số về kiến trúc mới của bộ xử lý có thể được thêm vào
chương trình biên dịch để trở thành khả dụng đối với các lệnh mới.
2.2.3. Hướng phát triển của ASIP
Trên đây chỉ là một trong số nhiều phương pháp luận về đồng thiết kế và kiến
trúc cho các ASIP ứng dụng trong các hệ thống nhúng. Hiện nay còn có nhiều
phương pháp khác nghiên cứu và thiết kế ASIP. Việc tuỳ biến hoá tập lệnh của bộ
vi xử lý theo yêu cầu của từng ứng dụng phụ thuộc vào người thiết kế và do đơn đặt
hàng.
2.3. Đồng thiết kế phần cứng/phần mềm (Hardware/Software Co-Design)
Như chúng ta đã biết hầu hết các hệ thống điện tử ngày nay (cả hệ thống
nhúng hoặc kết hợp một phần) đều có chứa một phần lớn các thành phần đã được số
hoá, các phần cứng đó hoạt động được chính là nhờ vào các phần mềm ứng dụng đã
được cài đặt sẵn. Đồng thiết kế phần cứng/phần mềm chính là “sự gặp gỡ” mức hệ
thống của phần cứng và phần mềm thông qua quá trình thiết kế.
Hiện nay việc thiết kế phần cứng số đã có sự phát triển gần như tương đương
với thiết kế phần mềm. Các mạch phần cứng được mô tả bằng các ngôn ngữ lập
trình hoặc ở dạng mô hình hoá bằng phần mềm, và như vậy chúng hoàn toàn phù
hợp và hoạt động theo sự điều khiển của phần mềm. Đôi khi người ta gọi là thiết kế
phần cứng chuyên biệt. Do vậy, việc thiết kế các hệ thống số đòi hỏi người thiết kế
phải nắm rất vững cả phần cứng và phần mềm.
Mục đích của mục này chỉ giới thiệu trong phạm vi hẹp về đồng thiết kế các
hệ thống phần cứng/phần mềm.
Chúng ta chỉ đề cập đến các bước đồng thiết kế các hệ thống phần cứng/phần
mềm mức cao (tức là chỉ nói đến thuần tuý về mặt công nghệ). Ở đây ta chỉ đề cập
đến các vấn đề chung chứ không đi sâu về một vấn đề nào, nhằm làm sáng tỏ sự
giống và khác nhau giữa các hệ thống số thiết kế kết hợp với các hệ thống tự nhiên
khác. Ta cũng đề cập đến các kỹ thuật tương tự nhau được áp dụng trong đồng thiết
kế các hệ thống phần cứng/phần mềm.
Tiếp cận lập trình cho FPGA từ Spartan -3 38

Đồng thiết kế các hệ thống phần cứng/phần mềm là bao gồm mô hình
(modeling), ứng dụng (validation) và cách tiến hành (implementation). Mô hình xử
lý là các tham số kỹ thuật mang tính khái niệm và định nghĩa lại và sản phẩm là các
mô hình phần cứng và phần mềm. ứng dụng xử lý là mức ứng dụng có thể được của
riêng hệ thống đó sẽ làm việc như đã được thiết kế và cách tiến hành là độ tin cậy
về mặt vật lý của phần cứng (trong quá trình tổng hợp) và khả năng có thể làm việc
tương ứng của phần mềm (trong quá trình biên dịch).
Khi đề cập đến các hệ thống nhúng các mẫu mô hình khác nhau và các chiến
lược thì ta chỉ đề cập đến hoàn toàn phần cứng (ví dụ như ASIC) và/hoặc hoàn toàn
phần mềm (phần mềm nhúng chạy trên nền card ISA) vì chúng là các hệ thống đồng
thiết kế. Do vậy tất cả các mô hình hệ thống nhúng đều có kiểu là thuần nhất
(homogeneous) hoặc không thuần nhất (heterogeneous). Ngoài ra còn có mô hình
ngôn ngữ (ví dụ như mô hình lập trình C chẳng hạn) hoặc các hình thức đồ hoạ
được dùng để trình diễn cả phần cứng và phần mềm. Vấn đề phân chia phần
cứng/phần mềm có thể dựa vào các tìm từng phần của mô hình thực hiện tốt nhất
trong phần cứng và tốt nhất trong phần mềm. Việc phân chia này do người thiết kế
thực hiện dựa trên mô hình ban đầu hoặc bằng các công cụ CAD.
Khi sử dụng mô hình không thuần nhất, việc phân chia phần cứng và mềm
thừng dựa vào mô hình của bản thân hệ thống, vì các thành phần phần cứng và phần
mềm có thể được biểu diễn theo các ngôn ngữ tương ứng. Vì dụ, phiên bản thứ nhất
của một sản phẩm có thể có thêm thành phần phần mềm vì lý do thời gian để
thương mại hoá và tính linh hoạt nhưng đến thế hệ tiếp theo của sản phẩm đó thì
thành phần phần mềm đó có thể lại được thực hiện vì lý do giá thành sản phẩm.
ISA được mô hình hoá theo các mức khác nhau. Tập lệnh cung cấp các thông
tin cần thiết về kiến trúc, hỗ trợ cho việc phát triển cả phần cứng và phần mềm.
Việc tổ chức xử lý thường được mô tả trong ngôn ngữ đặc tả phần cứng HDL
(Hardware Description Language) đối với mục đích tổng hợp thiết kế phần cứng,
trong khi đó các mô hình bộ vi xử lý (ví dụ như mô hình các bus chức năng) lại
thường sử dụng các phương pháp giả lập kết hợp.
Tiếp cận lập trình cho FPGA từ Spartan -3 39

Trong trường hợp cấu hình lại hệ thống, chúng ta cần phải chú ý phân chia
giữa mô hình ứng dụng đích và mô hình chủ. Nhiệm vụ đầu tiên thường phù hợp
với hệ thống của người dùng, trong khi đó nhiệm vụ thứ hai tương ứng với mức
phát triển hệ thống. Do vậy, hai nhiệm vụ này thường khác nhau về yêu cầu kết hợp
thiết kế.
Do các hệ thống ngày càng trở nên phức tạp, việc ứng dụng là rất cần thiết để
bảo đảm các chức năng làm việc chính xác và yêu cầu các mức hoạt động đúng theo
mô hình hoạt động của hệ thống. Ngoài ra việc ứng dụng cũng ảnh hưởng đến sự
hoạt động chính của hệ thống. Sự hoạt động của các ứng dụng dựa trên sự phối hợp
hoạt động giữa phần cứng và phần mềm. Mặt khác các hệ thống điều khiển nhúng
yêu cầu phải được phân chia các ứng dụng nhưng sự hoạt động kém hiệu quả cần
phải được kiểm tra trong tất cả các điều kiện để bảo đảm độ an toàn cho hệ thống.
Sự hoạt động của hệ thống phần cứng/phần mềm có thể giải quyết nhiều tiến
trình con.
2.3.1. Phân chia phần cứng/phần mềm
Việc phân chia hệ thống thành phần cứng và phần mềm là yêu cầu rất quan
trọng vì nó là yêu tố đầu tiên ảnh hưởng đến đặc trưng giá thành/tính năng của việc
thiết kế. Do vậy, bất kỳ sự phân chia nào cũng phải tính đến các chi tiết của việc
phân chia thành các khối phần cứng và phần mềm.
Công thức của việc phân chia phần cứng và phần mềm tuan thủ theo nguyên
tắc kết hợp thiết kế. Trong trường hợp các hệ thống nhúng, việc phân chia phần
cứng và phần mềm chính là việc phân chia về mặt vật lý các chức năng của hệ
thống thành các ứng dụng đặc biệt dành cho phần cứng và phần mềm trên một hay
nhiều bộ xử lý. Có nhiều quan điểm phân chia cứng và mềm dựa trên các tiêu chí
như kiến trúc hoặc mục đích sử dụng...
Khi đề cập đến mục đích chung của hệ thống máy tính, thì việc phân chia hệ
thống được thực hiện theo các chức năng logic, trong đó phần cứng được thiết kế hỗ
trợ cho sự hoạt động của phần mềm. Việc phân chia này thường tuân theo tập lệnh.
Do vậy, việc lựa chọn lệnh ảnh hưởng đến việc tổ chức phần cứng và phần mềm.
Tiếp cận lập trình cho FPGA từ Spartan -3 40

Tuy nhiên khi cấu hình lại hệ thống thì việc phân chia phụ thuộc vào mức độ
ưu tiên. Đối với các hệ thống chỉ có các chip kiểu FPGA, thì việc phân chia các
chức năng hệ thống lại thành các thành phần tương ứng với công nghệ chế tạo [16].
1. Quan điểm phân chia theo kiến trúc: quan điểm này chủ yếu áp dụng trong
các hệ thống nhúng với các bộ xử lý hoạt động và các ứng dụng phần cứng, kiến
trúc chung trong các hệ thống này là có thể tham số hoá như là một hệ đồng xử lý...
đó là các bộ xử lý làm việc kết hợp với các phần cứng trong các ứng dụng đặc biệt.
Điển hình là các kiến trúc xử lý đồ hoạ 3-D với cết cấu phần cứng hỗ trợ riêng cho
kỹ thuật đồ hoạ.
Giao diện phần cứng/phần mềm được định nghĩa kiểu kiến trúc thay đổi mà
ảnh hưởng lớn đến việc phân chia này. Chủ yếu nó sử dụng việc ánh xạ kiểu bộ nhớ
thông qua bộ xử lý hoặc theo kiểu truyền dữ liệu.
2. Phân chia theo mục đích: Các kiến trúc đồng xử lý thường được chọn để
cải thiện hiệu quả làm việc của hệ thống theo các thuật toán đặc biệt [5, 34]. Do
vậy, trong một vài quan điểm phân chia đưa ra cách phân chia ứng dụng theo tốc
độ. Do sự độc lập của dữ liệu, trong một vài phạm vi ứng dụng thì tốc độ không
phải là cách phân chia tốt. Nên trong các ứng dụng thời gian thực có yêu cầu ngặt
nghèo về mặt thời gian, việc phân chia này tỏ ra không hiệu quả.
3. Các chiến lược phân chia: Một số quan niệm sai về cách thức phân chia đó
là theo cách thức sử dụng các công cụ CAD. Thường thường việc xác định phần
cứng đối lại với sự hoạt động của phần mềm theo các chức năng thi hành ở các mức
độ trừu tượng mà không phải là được mô hình hoá theo tham số hệ thống. Do vậy
có hai quan điểm phân chia: đó là phân chia mờ và phân chia theo quan niệm kiến
trúc.
Dựa vào kiến trúc đã được biết rõ, người ta phân chia mức hệ thống theo các
chức năng mô tả nhờ việc gán nhãn cho các chức năng của nó theo sự hoạt động của
phần cứng hay phần mềm. Giải pháp chính xác nhất cho vấn đề phân chia, thậm chí
trong trường hợp đơn giản nhất, cũng đòi hỏi cách tính toán cực kỳ phức tạp. Trong
một nỗ lực để đưa ra mô hình toán học về vấn đề phân chia, thì công thức của mô
Tiếp cận lập trình cho FPGA từ Spartan -3 41

hình lập trình số nguyên IP (programming integer) và mô hình lập trình tuyến tính
số nguyên ILP (programming linear integer) thường được sử dụng hơn cả. Việc so
sánh các phương pháp mô hình toán học về phân chia phần cứng và phần mềm rất
khó khăn, bởi vì chất lượng của việc phân chia đôi khi còn chịu sự ảnh hưởng của
giá thành/tính năng hoạt động.
Các phương pháp mờ để phân chia có hai ý tưởng chính đó là: các phương
pháp xây dựng chẳng hạn như các công nghệ hợp nhóm và các phương pháp lặp
như là các luồng thông tin trên mạng, kỹ thuật tìm kiếm nhị phân cưỡng chế và mô
hình lập trình động. Ngoài ra, hầu hết các phương pháp được sử dụng đều dựa trên
các phương pháp tìm kiếm theo độ sâu, chẳng hạn như sự thay đổi của phương pháp
dự đoán mờ KL (Kernighan-Lin), hoặc các phương pháp khác...
Còn các phương pháp theo quan niệm kiến trúc thì chủ yếu là theo nhiệm vụ,
chức năng, tính liên kết....
Ngoài những mối quan hệ rất mật thiết giữa các chương trình con với nhau, thì
việc phân chia còn đối mặt với các vấn đề như lựa chọn giữa các yếu tố tính
năng/giá thành cũng là một bài toán khó. Trong khi các phương pháp ước lượng tốt
đối với sự hoạt động của phần cứng thì các tham số phần mềm nói chung phụ thuộc
vào rất nhiều yếu tố...
2.3.2. Lập chương trình thực hiện (Scheduling)
Lập chương trình thực hiện có rất nhiều vấn đề cần đề cập đến. Các giải thuật
lập chương trình do các nhà nghiên cứu lẫn giới khoa học máy tính đưa ra theo
nhiều mô hình và quan điểm khác nhau để áp dụng vào kỹ thuật thiết kế các hệ
thống phần cứng và phần mềm.
Thực tế, một số giải thuật lập chương trình cho phần cứng dựa trên các kỹ
thuật được sử dụng trong phần mềm và một số phương pháp lập chương trình mức
hệ thống - ngược lại dựa trên các ý tưởng xây dựng phần cứng.
Lập chương trình có thể hiểu một cách gần đúng như là việc gán một kích hoạt
start time cho một một sự kiện trong một tập, trong đó các sự kiện đã được liên kết
thông qua một số mối liên hệ (như là tính độc lập, mức độ ưu tiên,…).
Tiếp cận lập trình cho FPGA từ Spartan -3 42

Một số giải thuật lập chương trình được áp dụng vào việc thiết kế phần cứng,
trình biên dịch và hệ điều hành như sau:
- Lập chương trình hoạt động trong phần cứng: tương tự như việc thiết kế
ASIC bằng các ngôn ngữ HDL.
- Lập chương trình lệnh trong các trình biên dịch: các trình biên dịch là các
công cụ phần mềm phức tạp, gồm có các thiết bị ngoại vi, cơ chế định tuyến làm
việc tối ưu trên các khuôn dạng tức thời và các chương trình phụ trợ.
- Lập chương trình xử lý trong các hệ điều hành khác nhau: đó chính là vấn đề
xác định khi các các xử lý kích hoạt và gồm có cả đồng bộ và ngăn chặn các sự cố.
Các giải thuật đối với lập chương trình xử lý rất quan trọng đối với các hệ điều hành
và các chương trình thực hiện dưới thời gian thực.
2.3.3. Nhận xét
Đồng thiết kế phần cứng/phần mềm là một vấn đề rất khó hiện nay và đó
chính là các cơ hội thử thách đối với các nhà thiết kế hệ thống. Việc sử dụng và sử
dụng lại các khối lệnh phần cứng và phần mềm có thể tạo ra các sản phẩm với chất
hàng đầu (như là về giá cả, tính năng, độ linh hoạt, …) với thời gian thiết kế và phát
triển ngắn hơn giống như chúng việc chúng ta so sánh các thế hệ công nghệ chế tạo
mật độ mạch tích hợp (IC). Chủ yếu hiện nay chúng ta vẫn đang sử dụng các công
cụ CAD có sẵn để thiết kế và chế tạo các hệ thống phần cứng/phần mềm.
Các nhà khoa học và các nhà thương mại vẫn nuôi hy vọng vào các phương
pháp và các công cụ đồng thiết kế phần cứng/phần mềm tăng trưởng và phát triển
mạnh trong những năm tới đây.
Nhìn chung, đồng thiết kế phần cứng/phần mềm là một lĩnh vực nghiên cứu
rất rộng, do tính đa dạng của các ứng dụng, các cách thức thiết kế và công nghệ chế
tạo cũng như hoạt động.
Tiếp cận lập trình cho FPGA từ Spartan -3 43

Phần III. Giới thiệu bo mạch Spartan -3 starter kid


board và môi trường lập trình ISE 7.1

I. Tổng quát.
Đây là họ FPGA mới nhất của Xilinx với nhiều ưu điểm nổi bật. Đầu
tiên phải kể đến là khả năng tích hợp của Spartan-3 từ 50,000K-gate đến 5 triệu K-
gate. Một số đặc điểm chính của Spartan-3 là:
- Giá thành thấp, tiêu thụ điện năng ít.
- Mật độ tích hợp lên đến 74K trên một phần tử logic
- Tốc độ xung nhịp hệ thống lên đến 325MHz
- 3 mức tiêu thụ điện năng (1.2V; 3.3V; 2.5V)
- Có 784 chân
- Tốc độ truyền dữ liệu lên đến 622Mbps
Bảng 3.2. Một số sản phẩm của dòng Spartan-3

Tên sản Số cổng của Các phần Số hàng Số cột Khối Số chân
phẩm hệ thống tử logic RAM

XC3S200 200K 4320 24 20 216K 173

XC3S400 400K 8064 32 28 288K 264

XC3S1000 1M 17280 48 40 432K 391

XC3S2000 2M 46080 80 64 720K 565

XC3S4000 4M 62208 96 72 1728K 712

XC3S5000 5M 74880 104 80 1872K 784


Tiếp cận lập trình cho FPGA từ Spartan -3 44

Hiện nay với dòng sản phẩm Spartan-3 Platform FPGA Xilinx trở thành hãng
đầu tiên trên thế giới tiếp cận công nghệ 90nm.
Spartan -3 starter kid board là một công cụ hữu hiệu cho bất kì ai đang có ý
định thiết kế các sản phẩm dựa trên công nghệ FPGA (Field-Programmable Gate
Array) với một giải pháp cơ bản cho vấn đề tranh thủ thời gian và chi phí ban đầu
thấp. Nó cho phép chế tạo ngay và giá thành sản phẩm thấp và là một thiết bị cấu
trúc logic có thể được người sử dụng lập trình trực tiếp mà không phải sử dụng bất
kỳ một công cụ chế tạo mạch tích hợp nào.
Trong phần này ta chỉ giới thiệu sơ qua các chi tiết có thể nhìn thấy từ giao
diện bề ngoài của bo mạch. Các chi tiết cụ thể, các đặc điểm cũng như các vấn đề
cần chú ý đối với mỗi thành phần trên bo mạch sẽ được trình bày cụ thể trong mỗi
ứng dụng sau này.
Hình 3.1 là hình ảnh của Spartan-3 Starter Kit Board nhìn từ mặt trên.
Hình 3.2 là hình ảnh của Spartan-3 Starter Kit Board nhìn từ mặt dưới.
Hình vẽ 3.2 là hình ảnh của Spartan-3 Starter Kit Board khi ta trải trên mặt
phẳng.

Hình 3.1 . Spartan-3 Starter Kit Board nhìn từ mặt trên


Tiếp cận lập trình cho FPGA từ Spartan -3 45

Hình vẽ 3.2 : Spartan-3 Starter Kit Board nhìn từ mặt dưới


Spartan-3 Starter Kit Board: bao gồm các thành phần với các đặc trưng sau ( ở
đây ta chỉ giới thiệu những thành phần có liên quan đến ứng dụng sau này, chi tiết
chúng ta tìm hiểu ở phần Gui của SP3 trên http://xilinx.com).
1. Khối số 1: chính là chíp điều khiển chung Xc3s200ft256. Tên gọi của
nó rất quan trọng vì chúng ta sẽ còn phải sử dụng sau này khi thực hiện
việc gán chân.
2. Khối số 2 và khối 3 : Đó là Prom loại XCF02S, nó có chức năng lưu trữ
các cấu hình cũng như chương trình nạp từ trình dịch vào.
3. Khối số 5 : đây là cổng kết nối VGA. Spartan – 3 có khả năng kết nối
với màn hình và hiển thị các dữ liệu bằng việc thực hiện quét dòng quét
mành theo phương thức quét lần lượt. Chi tiết về việc tạo ảnh trên màn
hình hiển thị sẽ được phân tích cụ thể trong phần ứng dụng sẽ được đề
cập ở phần sau.
4. Khối số 6,7: Cổng kết nối Rs232.
5. Khối 9 : Kết nối với bàn phím hoặc là chuột.
6. Khối số 10 : Các led 7 đoạn. Cụ thể là có 4 led 7 đoạn anot chung.
7. Khối 11: 8 chuyển mạch. Có thể dùng để chọn chế độ làm việc, hoặc
cho những ứng dụng kiểm tra khi thiết kế thực hiện chức năng 8 bít đầu
vào số.
8. Khối 12: 8 led, dùng để kiểm tra quá trình thiết kế. Là một trong những
phương tiện kiểm tra đầu ra hiệu quả các chương trình mà chúng ta thiết
kế.
Tiếp cận lập trình cho FPGA từ Spartan -3 46

9. Khối 13: 4 nút ấn. Cũng tương tự như 8 chuyển mạch, chúng ta có thể
dùng nó để chọn chế độ làm việc cho bo mạch.
10. Khối 15 : Vị trí cắm của bộ tạo dao động thạch anh. Bộ tạo dao động
thạch anh tạo ra dao động chuẩn 50mhz. Chính vì vậy, trong quá trình
thiết kế, với các ứng dụng cụ thể cần thực hiện làm việc ở tần số đồng
bộ nào đó chúng ta phải thực hiện chia tần số trung tâm này ra để đạt
được tần số mong muốn. Thực tế, chúng ta hoàn toàn có thể kết nối với
một bộ tạo dao động đưa vào từ ngoài thông qua các thành phần kết nối
A1,A2, B1 như chỉ ra trên hình vẽ.
11. Khối 17 và 18: Khởi động chương trình thường trực trong Rom. Chi tiết
này sẽ được phân tích kỹ khi thực hiện một chương trình cụ thể.
12. Ở đây chúng ta cũng cần quan tâm đến các khối 19, 20 và 21: đây là các
thành phần dùng để kết nối với ngoại vi hoặc dẫn tín hiệu ra sau khi đã
thực hiện xử lý. Các khối này tương ứng với các thành phần được kí
hiệu trên bo mạch là A1, A2, B1. Đây là một chi tiết quan trọng và cần
phải đặc biệt quan tâm về cách thức thực hiện đầu cuối số liệu.Chính vì
vậy khi tiến hành thực hiện kết nối với thành phần ngoài phải quan tâm
đến các mức điện áp cũng như các chân tương ứng của nó. Để tiện hình
dung chúng ta xét một cổng A1 như sau

Hình 3.2 Cổng kết nối ngoại vi A1

Thứ tự của các chân từ phải qua trái, với chân số một là chân GND, chân số
3 là chân tương ứng với điện áp +3.3V, chân số 2 tương ứng mức +5V.
Như vậy các chân lẻ trừ chân 1 nối đất, các chân lẻ còn lại sẽ được kết nối
ở mức điện áp +3.3V, các chân chẵn sẽ được kết nối ở mức điện áp +5V.
13. Ngoài ra nó còn một số thành phần khác nữa. Để dễ hình dung hơn
chúng ta có thể quan sát hình vẽ dưới đây mô tả các thành phần được
kết nối với XC2s300.
Tiếp cận lập trình cho FPGA từ Spartan -3 47

Hình 3.3: Sơ đồ khối các thành phần trên Spartan-3 Starter Kit
Board
Tiếp cận lập trình cho FPGA từ Spartan -3 48

II. Giới thiệu môi trường lập trình ISE.


Khi kích thước và độ phức tạp của các hệ thống số gia tăng, nhiều công
cụ thiết kế được trợ giúp bởi máy tính CAD (Computer Aided Design) được đưa
vào quá trình thiết kế phần cứng. Phương pháp thiết kế trên giấy đã được thay bằng
cách thiết kế trên máy tính, từ đó các nhà thiết kế có thể kiểm tra và có các công cụ
tạo ra phần cứng tự động từ các bản thiết kế đó. Hỗ trợ mạnh mẽ nhất cho các công
cụ thiết kế này là các ngôn ngữ mô tả phần cứng HDL (Hardware Description
Languages). Hiện nay, các nhà nghiên cứu đã tìm ra nhiều cách cho phép HDL có
thể cải tiến quá trình thiết kế hệ thống số.
Quá trình thiết kế bắt đầu từ ý tưởng thiết kế của người thiết kế phần cứng.
Lúc này người thiết kế cần phải tạo ra các định nghĩa cho hành vi của hệ thống dưới
ý đồ thiết kế. Sản phẩm này có thể là ở dạng sơ đồ khối, lưu đồ hoặc chỉ là dạng
ngôn ngữ tự nhiên. Giai đoạn này ý tưởng thiết kế mới chỉ có đầu vào và đầu ra,
chứ hoàn toàn chưa có một chi tiết nào về phần cứng cũng như kiến trúc của hệ
thống.
Giai đoạn thứ hai của quá trình thiết kế là việc thiết kế đường dẫn dữ liệu hệ
thống. Trong giai đoạn này, người thiết kế chỉ rõ các thanh ghi và các phần tử logic
cần thiết cho quá trình cài đặt. Đây chính là giai đoạn thiết kế thư viện các phần tử
cho hệ thống. Các thành phần này có thể được kết nối thông qua bus 2 chiều hoặc 1
chiều. Dựa trên chức năng hoặc hành vi của hệ thống, tiến trình điều khiển hoạt
động của dữ liệu giữa các thanh ghi và các phần tử logic thông qua các bus được
phát triển. Giai đoạn này không cung cấp các đặc điểm về sự hoạt động của các bộ
điều khiển, cách đi dây, kỹ thuật mã hoá…
Giai đoạn thứ 3 là là giai đoạn thiết kế logic, giai đoạn này liên quan đến ứng
dụng của các cổng và các mạch cơ bản cho việc cài đặt các thanh ghi dữ liệu, các
bus hệ thống, các phần tử logic và phần cứng điều khiển chúng. Kết quả của giai
đoạn này chính là một danh sách kết nối (netlist).
Giai đoạn thiết kế tiếp là chuyển netlist của giai đoạn trước thành sơ đồ hay là
danh sách các tranzitor. Giai đoạn này xét đến cả chế độ tải và thời gian trong quá
Tiếp cận lập trình cho FPGA từ Spartan -3 49

trình thực hiện hành vi của hệ thống cũng như việc chọn tranzitor hoặc các phần tử
của nó. Giai đoạn này bao gồm: tổng hợp logic, ánh xạ công nghệ, flooplanning,
placement, routing. Giai đoạn này sử dụng công cụ CAD là chủ yếu.
Giai đoạn cuối cùng là nạp vào kid và kiểm tra kết quả, hoản chỉnh sản phẩm.
2.1. Giới thiệu gói phần mềm ISE:
Phần mềm ISE (Integrated Software Environment) này là một môi trường thiết
kế hoàn hảo của Xilinx, nó trợ giúp cho người thiết kế hầu hết các công cụ cần thiết
nhất để có thể hoàn thành một đề án thiết kế nhanh nhất và hiệu quả nhất. ISE tích
hợp các công nghệ tiên tiến nhất mạng lại tính linh hoạt, giao diện GUI thân thiện
với người sử dụng.
Một số ưu điểm của ISE là:
- Tận dụng tối đa tất cả các công nghệ tiên tiến nhất của PLD.
- Tiết kiệm thời gian thiết kế, hỗ trợ tất cả các dòng sản phẩm của Xilinx.
- Tăng hiệu quả và giảm giá thành.
- Hỗ trợ tối đa cho việc thiết kế các hệ thống nhúng.
Bộ sản phẩm ISE bao gồm các gói phần mềm:
a. ISE WebPACK: đây là gói sản phẩm dùng để phát triển hệ thống một cách
dễ dàng nhất vì nó là môi trường thiết kế on-line (trực tuyến) trên Web và được hỗ
trợ trực tiếp từ Xilinx. ISE WebPACK cho phép người dùng hoàn thành bản thiết kế
nhanh chóng nhờ sự kết hợp của các thiết kế đầu vào HDL, các công vụ tổng hợp
tiên tiến và khả năng kiểm tra đối với cả CPLD và FPGA trực tuyến.
b. ISE BaseX: đây là gói phần mềm hiệu quả về kinh tế nhất, là môi trường
thiết kế PLD trên máy tính cá nhân linh hoạt và ổn định. Nó cung cấp tất cả các khả
năng như ISE WebPACK, ngoài ra nó còn được bổ sung nhiều công cụ khác hỗ trợ
cho người dùng.
c. ISE Alliance: gói phần mềm này được thiết kế phù hợp với môi trường thiết
kế có sẵn của người dùng. Nó kết hợp các công cụ hay nhất của Xilinx để tạo môi
trường thiết kế hoàn chỉnh với các tính năng cao hơn ISE BaseX.
Tiếp cận lập trình cho FPGA từ Spartan -3 50

d. ISE Foundation: đây là gói phần mềm hoàn chỉnh nhất, dễ sử dụng, tính
năng nhiều nhất đồng thời tích hợp các công cụ phân tích, tổng hợp và công nghệ
kiểm tra sản phẩm với các giải pháp hữu hiệu.
2.2. Hướng dẫn sử dụng phần mềm ISE Foundation 7.1
Giao diện chương trình

Hình 3.4: Giao diện chính của môi trường lập trình ISE 7.1

Hướng dẫn các bước tạo một đề án mới ( ở đây lựa chọn kid là Spartan 3)
Ø Bước 1: Từ mênu file à new project à Điền tên vào Poject name
àchọn thư mục lưu ở project location à chọn ngôn ngữ để viết à
next:

Hình 3.5: Tạo một đề án mới


Tiếp cận lập trình cho FPGA từ Spartan -3 51

Ø Bước 2: Lựa chọn kid là spartan-3, loại Xc3s200, speed grade là -4,
ngôn ngơi để soạn thảo là VHDL, mô phỏng dùng ModenSim.

Hình3.6: Các lựa chọn cụ thể cho một đề án.

Ø Bước 3.Sau khi là xong bước trên chúng ta next, sẽ có một cửa sổ để ta
thêm nguồn mới vào đề án. Chọn New source à next àchọn Vhdl
module à chọn tên mô đunà next:

Hình 3.7: Thêm một module vào đề án thiết kế.


Tiếp cận lập trình cho FPGA từ Spartan -3 52

Ø Bước 4: Chọn các cổng vào ra cho đề án.

Bao gồm tên cổng, mô tả vào (in), ra(out) hay vào ra (in- out). S ố bít vào ra
tương ứng với các cổng. Các bước tiếp theo cứ next đến khi kế thúc (finish). Sau
khi định nghĩa và mô tả xong ở bước này lúc đó trình dịch sẽ tự động tạo ra thực thể
với các cổng được mô tả bằng lệnh ( VHDL) như sau:
entity chiatansodauvao is
Port ( clock : in std_logic;
1hz : out std_logic;
led : out std_logic_vector(7 downto 0));
end chiatansodauvao;
architecture Behavioral of chiatansodauvao is
begin
end Behavioral;
Như vậy ta đã tạo ra một đề án. Tiếp theo là ta viết chương trình ở cửa sổ soạn thảo.
Vấn đề đặt ra là với chương trình lớn có nhiều hơn một mô đule ta sẽ làm thế nào?
Ở chương này ta chỉ giới thiệu cách thức để chúng ta tạo ra một đề án mới. Các
phần còn lại bao gồm kiểm tra cấu trúc lệnh, kiểm tra mức logic, gán chân, nạp cấu
hình chạy thử sẽ được hướng dẫn chi tiết cùng với bài tập cụ thể.
Tiếp cận lập trình cho FPGA từ Spartan -3 53

CHƯƠNG IV: NGÔN NGỮ VHDL

4.1. Giới thiệu chung về ngôn ngữ VHDL


VHDL (VHSIC Hardware Description Laguage) là một ngôn ngữ được dùng
để mô tả các hệ thống điện tử số. Nó được chương trình quốc gia về Các mạch tích
hợp Tốc độ rất cao - VHSIC (Very High Speed Integrated Circuits) do chính phủ
Mỹ khởi xướng vào đầu những năm 1980. Các công ty tham gia chương trình
VHSIC nhận thấy rằng họ cần phải có một công cụ nào đó để thiết kế các giản đồ
đầu vào cho các IC chuyên dụng cỡ lớn, và họ đã đề xuất việc lập ra một ngôn ngữ
mô tả phần cứng dùng để mô tả cấu trúc và chức năng của các mạch tích hợp (còn
được gọi là IC - Integrated Circuits).
Kể từ đó, VHDL ra đời và được phát triển, rồi sau đó được Hiệp hội các kỹ sư
Điện và Điện tử - IEEE (Institude of Electrical and Electronic Engineers) chấp
nhận coi như là tiêu chuẩn tại Mỹ. Phiên bản đầu tiên là Tiêu chuẩn IEEE 1076-
1987 (còn được gọi là VHDL-87). Phiên bản này được bổ sung sửa đổi năm 1993
thành IEEE 1076-1993 (còn được gọi là VHDL-93).
VHDL được thiết kế nhằm thay thế cho một số khâu cần thiết trong quá trình
thiết kế. Đầu tiên, nó cho phép mô tả cấu trúc của một bản thiết kế, tức là làm thế
nào để có thể phân tách bản thiết kế thành các bản thiết kế con, và làm thế nào để
kết nối các bản thiết kế con đó lại với nhau. Thứ hai là nó cho phép mô tả đặc điểm
chức năng của các bản thiết kế tương tự như trong ngôn ngữ lập trình. Thứ ba là dựa
vào kết quả đạt được, nó cho phép một bản thiết kế có thể mô phỏng được trước khi
đưa vào sản xuất, vì vậy các nhà thiết kế có thể so sánh một cách nhanh chóng việc
thay thế và kiểm tra để điều chỉnh chính xác mà không mất thời gian và tiền bạc vào
việc chế tạo mẫu thử đầu tiên.
Tiếp cận lập trình cho FPGA từ Spartan -3 54

4.1.1. Mô tả cấu trúc (Describing Structure)


Một hệ thống điện tử số có thể được mô tả thành các khối - còn gọi là modul
(module) với các đầu vào và/hoặc đầu ra. Các giá trị điện ở 0 đầu ra có mối quan hệ
với các giá trị trên các đầu vào. Hình 4.1.a biểu diễn một ví dụ như vậy. Khối F có
hai đầu vào A và B, và có một đầu ra Y.

Hình 4.1. (a) Khối F có hai đầu vào và một đầu ra;
(b) Khối F gồm có 3 thực thể G, H và I
Sử dụng ngôn ngữ VHDL để mô tả khối F, thì ta gọi khối F là một thực thể
(entity) thiết kế, và các đầu vào và đầu ra là các cổng (port).
Có một cách để mô tả chức năng của khối F, đó là chúng ta mô tả các khối con
(sub-module) thành phần của nó. Mỗi một khối con được gọi là một tập hợp
(instance) của một vài thực thể, và các cổng của các tập hợp đó được nối lại bằng
các đường tín hiệu (signal).
Hình 4.1.b mô tả khối F là một tập hợp gồm các thực thể G, H và I. Kiểu mô
tả này được gọi là mô tả cấu trúc (structural). Các thực thể G, H và I cũng được mô
tả theo cấu trúc tương tự như vậy.
4.1.2. Mô tả hoạt động (Describing Behaviour)
Trong nhiều trường hợp, việc mô tả cấu trúc không tương ứng với việc mô tả
hoạt động. Người ta thường dùng cách mô tả hoạt động theo kiểu từ dưới lên dựa
vào mô tả cấu trúc. Ví dụ, khi chúng ta thiết kế hệ thống điện tử thì không cần phải
mô tả cụ thể cấu trúc bên trong của từng con IC mà chỉ cần mô tả theo chức năng
Tiếp cận lập trình cho FPGA từ Spartan -3 55

của các khối của hệ thống mà thôi. Trường hợp này được gọi là mô tả chức năng
(fuctional) hoặc mô tả hoạt động (behavioural).
Để minh hoạ cho điều này, chúng ta giả sử rằng chức năng của thực thể F
trong hình 4.1(a) là một mạch OR đảo. Khi mô tả hoạt động của F ta có thể biến đổi
theo đại số Boolean như sau:
Y = A.B + A.B
Đối với các mạch có chức năng hoạt động phức tạp hơn, thì không thể biển
diễn theo các chức năng đầu vào được. Trong các hệ thống có phản hồi ngược, đầu
ra thường là các hàm chức năng theo thời gian. Ngôn ngữ VHDL cho phép giải
quyết vấn đề này bằng cách mô tả hoạt động theo khuôn dạng chương trình lập
trình.
4.1.3. Mô hình thời gian theo các sự kiện rời rạc
Khi chức năng hoạt động và cấu trúc của khối đã được chỉ định rõ, thì người ta
có thể mô phỏng khối bằng cách kích hoạt theo mô tả hoạt động của nó. Điều này
có thể thực hiện được bằng cách mô phỏng quá trình hoạt động đã được rời rạc
thành các bước theo thời gian. Tại một vài thời điểm mô phỏng, khối đầu vào được
kích hoạt bằng cách theo đổi giá trị trên cổng đầu vào. Khối này phản ứng lại bằng
cách thực hiện mã lệnh theo mô tả hoạt động của nó đã được gán và tạo ra các giá
trị mới đưa đến đường tín hiệu để đưa đến các cổng đầu ra của nó tại các thời điểm
mô phỏng tiếp theo sau. Việc này được gọi là kế hoạch giao tác (scheluding a
transaction) trên tín hiệu đó. Nếu giá trị mới khác giá trị trước đó đã có trên đường
tín hiệu, thì sẽ có một sự kiện (event) xảy ra, và các khối khác với các đầu vào đã
được kết nối với đường tín hiệu đó có thể sẽ được kích hoạt.
Quá trình mô phỏng bắt đầu với một pha được gọi là pha khởi động (initilation
phase), và sau đó các quá trình được thực hiện lặp lại hai giai đoạn trong một chu
kỳ mô phỏng (simulation cycle). Trong pha khởi động, tất cả các tín hiệu được cung
cấp sẵn các giá trị khởi động, thời gian mô phỏng được đưa về 0, và mỗi một
chương trình hoạt động của một khối được kích hoạt.
Tiếp cận lập trình cho FPGA từ Spartan -3 56

Trong giai đoạn đầu tiên của chu kỳ mô phỏng, thời gian mô phỏng được nâng
lên thành thời gian sớm nhất tại thời điểm mà giao tác đã được thực hiện. Tất cả các
giao tác được đưa vào tại thời điểm này đều được kích hoạt, và điều này có thể gây
ra một số sự kiện nào đó.
Trong gian đoạn thứ hai của chu kỳ mô phỏng, tất cả các khối mà phản ứng lại
đối với các sự kiện vừa xảy ra trong giai đoạn một sẽ kích hoạt chương trình hoạt
động của chúng. Các chương trình đó thường là kế hoạch giao tác trên các tín hiệu
đầu ra của chúng. Khi tất cả các chương trình kết thúc hoạt động, chu kỳ mô phỏng
được lặp lại. Nếu không có thêm giao tác nào thì quá trình mô ph ỏng đã hoàn thành.
Mục đích của việc mô phỏng là để biết thêm thông tin về sự thay đổi trong hệ
thống tại từng thời điểm. Việc này có thể thực hiện được giám sát bởi chương trình
kiểm soát mô phỏng (simulation monitor). Chương trình này nhằm mục đích ghi lại
quá trình hoạt động theo từng thời điểm tại các điểm để dùng vào việc phân tích về
sau.

4.1.4. Ví dụ
Chúng ta có một bộ đếm hai bit COUNT như trong hình vẽ 4.2, gồm có 3
khối: 2 khối T_FLIPFLOP và một khối INVERTER. Bộ đếm này có 2 đầu ra là q1
và q0, một đầu vào là clock.

Hình 4.2. Sơ đồ bộ đếm COUNT


Dùng ngôn ngữ VHDL để định nghĩa bộ đếm này như sau:
Tiếp cận lập trình cho FPGA từ Spartan -3 57

entity count2 is
generic (prop_delay : Time := 10 ns);
port (clock : in bit; q1, q0 : out bit);
end count2;
Nhìn vào đoạn chương trình trên chúng ta thấy bộ đếm COUNT đã được định
nghĩa với một cổng vào clock và hai cổng ra q1, q0 đều thiết lập theo các giá trị bit.
Hằng số prop_delay được sử dụng đề điều khiển hoạt động của COUNT với thời
gian đếm sẽ là 10 ns một lần. Trên đây mới chỉ là định nghĩa bộ đếm, còn hoạt động
của bộ đếm này sẽ được viết theo hai cách.
Cách thứ nhất là viết theo hoạt động của bộ đếm:
architecture behaviour of count2 is
begin
count_up: process (clock)
variable count_value : natural := 0;
begin
if clock = '1' then
count_value := (count_value + 1) mod 4;
q0 <= bit'val(count_value mod 2) after prop_delay;
q1 <= bit'val(count_value / 2) after prop_delay;
end if;
end process count_up;
end behaviour;
Có thể viết lại hoạt động dựa theo thành phần các khối trong bộ đếm và đường
tín hiệu như sau:
architecture structure of count2 is
component t_flipflop
port (ck : in bit; q : out bit);
end component;
component inverter
Tiếp cận lập trình cho FPGA từ Spartan -3 58

port (a : in bit; y : out bit);


end component;
signal ff0, ff1, inv_ff0 : bit;
begin
bit_0 : t_flipflop port map (ck => clock, q => ff0);
inv : inverter port map (a => ff0, y => inv_ff0);
bit_1 : t_flipflop port map (ck => inv_ff0, q => ff1);
q0 <= ff0;
q1 <= ff1;
end structure;
4.2. Cú pháp và ngữ nghĩa ()
Theo như vấn đề đã đề cập trong mục 4.1, hoạt động của một khối (module) có
thể được mô tả theo dạng ngôn ngữ lập trình. Trong mục 4.2 này sẽ giới thiệu về cú
pháp và ngữ nghĩa của các khái báo trong ngôn ngữ BHDL.
4.2.1. Các thành phần từ vựng
4.2.1.1. Chú thích trong chương trình
Phần chú thích trong chương trình viết bằng VHDL được phân biệt bằng dấu
gạch ngang (‘--’) và có tác dụng đến hết dòng đó.
4.2.1.2. Các kiểu định danh
Các định danh trong VHDL sử dụng bằng các từ riêng và do người lập trình
định nghĩa tên. Chúng phải tuân theo các qui tắc sau:
Định danh ::= chữ cái {[dấu gạch dưới] chữ cái hoặc số}
Ví dụ:
signal ff0:bit; -- đường tín hiệu ff0 thuộc signal
-- được định nghĩa là đơn vị là bit
Chú ý trường hợp các chữ cái không được trùng với ký tự đặc biệt như các
đơn vị, các ký tự đặc biệt, các tên chức năng như ns, ms, FF, read, write...
Tiếp cận lập trình cho FPGA từ Spartan -3 59

4.2.1.3. Các kiểu số


Các số bằng chữ có thể biểu diễn theo hệ thập phân hoặc hệ cơ sở khác như hệ
cơ số 2 (hệ nhị phân), hệ cơ số 16 (hệ hexa). Nếu các chữ bằng số có dấu phẩy, nó
biểu diễn số thực, ngược lại là biểu diễn số nguyên. Số hệ thập phân được định
nghĩa như sau:
số thập phân bằng chữ::=số nguyên[.số nguyên][số mũ]
số nguyên::=bằng số{[dấu gạch dưới]bằng số}
số mũ::=E[+]số nguyên|E-số nguyên
Ví dụ:
0 1 123_456_789 987E6 -- các s ố nguyên biểu diễn bằng chữ
0.0 0.5 2.718_28 12.4E-9 -- các số thực biểu diễn bằng chữ
Các số bằng chữ hệ cơ sở khác được định nghĩa như sau:
số hệ cơ sở bằng chữ::=ký hiệu cơ sở#số nguyên hệ cơ sở[.số nguyên hệ cơ
sở]#[số mũ]
số hệ sơ cở::=số nguyên
số nguyên hệ cơ sở::=số mũ mở rộng{[dấu gạch dưới]bằng số mở rộng}
số mũ mở rộng::=bằng số|chữ cái
Các thành phần cơ sở và số mũ được biểu diễn theo hệ thập phân. Các chữ cái
từ A đến F (cả chữ thường và in hoa) được sử dụng như phần số mở rộng dùng để
biểu diễn các số thập phân từ 10 đến 15 tương ứng.
Ví dụ:
2#1100_0100# 16#C4# 4#301#E1 -- có ngh ĩa là bằng số nguyên 196
2#1.1111_1111_111#E+11 16#F.FF#E2 -- có nghĩa là bằng số thực 4095.0
4.2.1.4. Kiểu ký tự
Các ký tự bằng chữ có khuôn dạng tuân theo chuẩn ASCII nằm dấu nháy đơn.
Ví dụ:
‘A’ ‘*’ ‘`’ ‘’
4.2.1.5. Kiểu xâu
Các xâu bằng chữ dạng các ký tự có khuôn dạng nằm trong dấu nháy kép.
Tiếp cận lập trình cho FPGA từ Spartan -3 60

Ví dụ:
“A string”
“” -- xâu trống
“A string in a string: “ “A string”.” -- xâu ch ứa dấu nháy kép
4.2.1.6. Kiểu xâu dạng bit
VHDL cung cấp kiểu biến dạng bit (‘0’ và ‘1’). Cú pháp như sau:
xâu bit bằng chữ::=ký hiệu hệ cơ sở “giá trị bằng bit”
ký hiệu hệ cơ sở::=B|O|X
giá trị bằng bit::=số mũ mở rộng{[dấu gạch dưới]số mũ mở rộng}
Ký hiệu hệ cơ sở là B - tức là hệ nhị phân, O - là hệ 8 (octal) và X là hệ 16
(hexa).
Ví dụ:
B “1010110” -- độ dài là 7
O “126” -- độ dài là 9, tương đương với B “001_010_110”
X “56” -- độ dài là 8, tương đương với B “0101_0110”
4.2.2. Các kiểu dữ liệu và đối tượng
VHDL cung cấp các kiểu dữ liệu số cơ bản, các kiểu dữ liệu vô hướng, và các
dạng đối tượng tổng hợp gồm nhiều thành phần. Các kiểu dữ liệu vô hướng gồm có
các kiểu số, đơn vị vật lý, và các số đếm (gồm có cả các số đếm kiểu ký tự), và
ngoài ra còn có các kiểu đã được định nghĩa sẵn. Kiểu đối tượng tổng hợp gồm có
kiểu bản ghi và mảng. VHDL cũng cung cấp cách thức truy xuất kiểu con trỏ
(pointer) và kiểu tệp (file).
Định nghĩa một kiểu dữ liệu trong chương trình được mô tả như sau:
Mô tả tên đầy đủ của kiểu dữ liệu ::= type tên định danh is định nghĩa kiểu
định nghĩa kiểu ::=
định nghĩa kiểu vô hướng
|định nghĩa kiểu tổng hợp
|định nghĩa kiểu truy xuất con trỏ
|định nghĩa kiểu tệp
Tiếp cận lập trình cho FPGA từ Spartan -3 61

định nghĩa kiểu vô hướng ::=


định nghĩa kiểu số đếm
| định nghĩa kiểu số nguyên
|định nghĩa kiểu số dấu phẩy động|
|định nghĩa kiểu đơn vị vật lý
định nghĩa kiểu tổng hợp ::=
định nghĩa kiểu mảng
|định nghĩa kiểu bản ghi
4.2.2.1. Kiểu số nguyên
Kiểu số nguyên có phạm vi làm việc trong các giá trị của số nguyên đã đươc
chỉ định sẵn. Cú pháp của kiểu số nguyên như sau:
định nghĩa kiểu số nguyên ::= phạm vi cho phép
phạm vi cho phép ::= range phạm vi
phạm vi ::= biểu thức đơn điều khiển biểu thức đơn
điều khiển ::= to | downto – tăng | giảm
Biểu thức chỉ định trong phạm vi phải được biểu diễn bằng số nguyên. Các
kiểu có kèm thêm từ khoá to được gọi là có phạm vi tăng và cũng như vậy với từ
khoá downto thì được gọi là có phạm vi giảm. VHDL chuẩn cũng cho phép hoạt
động trong phạm vi cấm, nhưng yêu cầu tối đa là trong phạm vi từ -2147483647
đến + 2147483647.
Ví dụ về kiểu số nguyên:
type byte_int is range 0 to 255;
-- kiểu byte_int là kiểu số nguyên có phạm vi từ 0 đến 255
type signed_word_int is range –32768 to 32767;
-- kiểu signed_word_int là kiểu số nguyên có phạm vi từ -32768
-- đến 32767
type bit_index is range 31 downto 0;
-- kiểu bit_index là kiểu số nguyên có phạm vi giảm từ 31
-- xuống 0
Tiếp cận lập trình cho FPGA từ Spartan -3 62

4.2.2.2. Kiểu đơn vị vật lý


Kiểu đơn vị vật lý là kiểu số dùng để biểu diễn một vài đơn vị vật lý, chẳng
hạn như khối lượng, chiều dài, thời gian hoặc điện áp. Việc biểu diễn kiểu đơn vị
vật lý bao gồm tham số của đơn vị cơ sở, có thể có thêm một số đơn vị thứ cấp,
được dẫn xuất bằng cách nhân với đơn vị cơ sở.
Cú pháp của kiểu đơn vị vật lý như sau:
định nghĩa kiểu vật lý ::=
phạm vi cho phép
units
mô tả đơn vị cơ sở
{mô tả đơn vị thứ cấp}
end units
mô tả đơn vị cơ sở ::= định danh;
mô tả đơn vị thứ cấp ::= định danh = kiểu biểu diễn vật lý bằng chữ
kiểu biểu diễn vật lý bằng chữ ::= [kiểu số ảo bằng chữ] tên đơn vị
Ví dụ về kiểu đơn vị vật lý:
type length is range 0 to 1E9
units
um;
mm = 1000 um;
cm = 10 mm;
m = 1000 mm;
in = 25.4 mm;
ft = 12 in;
yd = 3 ft;
rod = 198 in;
chain = 22 yd;
furlong = 10 chain;
end units;
Tiếp cận lập trình cho FPGA từ Spartan -3 63

type resistance is range 0 to 1E8


units
ohms;
kohms = 1000 ohms;
Mohms = 1E6 ohms;
end units;
Riêng kiểu đơn vị vật lý thời gian (time) rất quan trọng trong VHDL, vì nó
được sử dụng rất nhiều trong các thiết bị mô phỏng trễ thời gian. Nó được định
nghĩa như sau:
type time is range implementation_defined
units
fs;
ps = 1000 fs;
ns = 1000 ps;
us = 1000 ns;
ms = 1000 us;
sec = 1000 ms;
min = 60 sec;
hr = 60 min;
end units;
Để ghi giá trị của một kiểu số vật lý nào đó đã được định nghĩa thì viết số
trước và tiếp theo là đơn vị đo của nó. Ví dụ: 10 mm; 10 rod; 1200 ohms; 23 ns.
4.2.2.3. Kiểu số dấu phẩy động
Kiểu số dấu phẩy động là một tập của kiểu số thực trong một phạm vi chỉ định
trước. Phạm vi cho phép của kiểu số dấu phẩy động từ -1E38 đến +1E38. Cú pháp
của kiểu số dấu phẩy động như sau:
định nghĩa kiểu số dấu phẩy động := phạm vi cho phép
Ví dụ:
Type signal_level is range –10.00 to +10.00;
Tiếp cận lập trình cho FPGA từ Spartan -3 64

Type probability is range 0.0 to 1.0;


4.2.2.4. Các kiểu liệt kê
Kiểu liệt kê là một tập các định danh hoặc các ký tự đã được sắp xếp. Các định
danh và các ký tự trong kiểu liệt kê phải riêng biệt, rõ ràng, tuy nhiên chúng có thể
được sử dụng lại trong các kiểu liệt kê khác nhau.
Cú pháp của kiểu liệt kê như sau:
định nghĩa kiểu liệt kê ::= (kiểu biểu diễn liệt kê bằng chữ {, biểu diễn kiểu
liệt kê bằng chữ})
Kiểu biểu diễn kiểu liệt kê bằng chữ ::= định danh | kiểu biểu diễn bằng ký tự
Ví dụ:
Type logic_level is (unknown, low, undriven, high);
Type alu_function is (disable, pass, add, subtract, multiply, divide);
Type octal_digit is (‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’);
Có một số kiểu liệt kê đã được định nghĩa sẵn:
Type severity_level is (note, warning, error, failure);
Type boolean is (false, true);
Type bit is (‘0’, ‘1’);
Ngoài ra còn một kiểu ký tự được định nghĩa sẵn là
type character is (NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, HT,
LF, VT, FF, CR, SO, SI, DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, CAN,
EM, SUB, ESC, FSP, GSP, RSP, USP, ' ', '!', '"', '#', '$', '%', '&', ''', '(', ')', '*', '+', ',', '-',
'.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[',
'\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', DEL);
Kiểu ký tự này bao gồm cả các ký tự định danh, số và các chữ cái.
4.2.2.5. Kiểu mảng
Kiểu mảng trong VHDL là một tập hợp được đánh chỉ mục các phần tử có
cùng kiểu. Mảng có thể là một chiều hoặc nhiều chiều. Ngoài ra, kiểu mảng có thể
Tiếp cận lập trình cho FPGA từ Spartan -3 65

tự tạo - tức là phạm vi của một chỉ mục được thiết lập khi kiểu được định nghĩa.
Hoặc có thể là mảng tự nhiên - tức là phạm vi của chỉ mục được thiết lập theo thứ tự
tuần tự.
Cú pháp của mảng như sau:
định nghĩa kiểu mảng ::= định nghĩa mảng tự tạo | định nghĩa mảng tự nhiên
định nghĩa mảng tự tạo :: = array (định nghĩa kiểu chỉ mục con {, định nghĩa
kiểu chỉ mục}) of biểu diễn của các phần tử trong mảng
định nghĩa mảng tự nhiên :: = array kiểu chỉ mục tự nhiên of biểu diễn của
các phần tử trong mảng
định nghĩa kiểu chỉ mục con ::= kiểu đánh dấu range <>
kiểu chỉ mục tự nhiên ::= (phạm vi rời rạc {, phạm vi rời rạc})
phạm vi rời rạc ::= biểu diễn kiểu rời rạc con | phạm vi
Định nghĩa kiểu chỉ mục con được trình bầy trong mục 3.2.2.7.
Ví dụ về mảng tự nhiên:
Type word is array (31 downto 0) of bit;
Type memory is array (address) of word;
Type transform is array (1 to 4, 1 to 4) of real;
Type register_bank is array (byte range 0 to 132) of integer;
Ví dụ về mảng tự tạo:
Type vector is array (integer range <>) of real;
Ký hiệu ‘<>’ (được gọi là hộp (box)) có thể là kiểu trống để dành sử dụng
trong chương trình. Trong ví dụ trên, khi một đối tượng có thể biểu diễn theo kiểu
vector có 20 phần tử bằng cách cung cấp cho kiểu vector của nó như sau:
Vector(1 to 20);
Có hai kiểu mảng được định nghĩa sẵn và cả hai đều là là kiểu tự tạo:
Type string is array (positive range <>) of character;
Type bit_vector is array (natural range <>) of bit;
Kiểu positive và natural sẽ được trình bầy trong mục 3.2.2.7.
Tiếp cận lập trình cho FPGA từ Spartan -3 66

4.2.2.6. Kiểu bản ghi


VHDL cung cấp các kiểu bản ghi cơ bản một cách linh hoạt, bao gồm tập hợp
tên các phần tử và kiểu định nghĩa của chúng.
Cú pháp của kiểu bản ghi như sau:
định nghĩa kiểu bản ghi ::=
record
mô tả các phần tử
{mô tả các phần tử}
end record
mô tả các phần tử ::= liệt kê các định danh : định nghĩa kiểu phần tử con;
liệt kê các định danh ::= định danh {, định danh}
định nghĩa kiểu phần tử con ::= biểu diễn kiểu phần tử con
Ví dụ:
Type instruction is
Record
Op_code : processor_op;
Address_mode : mode;
Operand1, operand2 : integer range 0 to 15;
End record;
4.2.2.7. Các kiểu con
Việc sử dụng kiểu con cho phép các giá trị nhận được qua một đối tượng bị
hạn chế và thu hẹp trong một vài kiểu. Cú pháp của kiểu con là:
mô tả kiểu con ::= subtype tên định danh is biểu diễn kiểu con;
biểu diễn kiểu con ::= [tên hàm phân giải] kiểu đánh dấu [phạm vi]
kiểu đánh dấu ::= tên kiểu | tên kiểu con
phạm vi ::= dải phạm vi | chỉ số phạm vi
Có hai kiểu con, thứ nhất là kiểu con có thể có hạn chế các giá trị từ một kiểu
vô hướng cùng với phạm vi đã được chỉ định. Ví dụ như:
subtype pin_count is integer range 0 to 400;
Tiếp cận lập trình cho FPGA từ Spartan -3 67

subtype digits is character range ‘0’ to ‘9’;


Thứ hai là kiểu con có thể có hạn chế theo dạng mảng. Ví dụ như:
subtype id is string(1 to 20);
subtype word is bit_vector(31 downto 0);
Ngoài ra còn có hai kiểu con dạng số định nghĩa trước, ví dụ như:
subtype natural is integer range 0 to highest_integer
subtype positive is integer range 1 to highest_integer
4.2.2.8. Các mô tả về đối tượng
Một đối tượng là một thành phần được đặt tên trong một mô tả VHDL mà có
giá trị theo kiểu đã được chỉ định. Có 3 lớp đối tượng: hằng số, biến số và tín hiệu.
hai kiểu đầu được trình bầy sau đây, còn kiểu thứ ba sẽ được trình bầy trong phần
3.3.2.1. Việc mô tả và sử dụng các hằng số và biến số hoàn toàn giống như trong
các ngôn ngữ lập trình khác.
Một hằng số là một đối tượng được khởi động theo một giá trị chỉ định trước
khi nó được khởi tạo, và không thể thay đổi. Cú pháp của nó là:
mô tả hằng số ::=
constant liệt kê tên định danh : biểu diễn kiểu con [ := biểu thức ]
Các mô tả hằng số thiếu biểu thức khởi động được gọi là các hằng số hoãn, và
chỉ có thể xuất hiện trong các mô tả đóng gói (xem mục 3.2.5.3). Giá trị khởi động
phải được cung cấp tương ứng trong thân đóng gói.
Ví dụ:
constant e : real := 2.71828;
constant delay : Time := 5 ns;
constant max_size : natural;
Một biến là một đối tượng mà giá trị có thể thay đổi được sau khi khởi động.
Cú pháp của nó là:
mô tả biến ::=
variable liệt kê tên định danh : biểu diễn kiểu con [ := biểu thức ]
Tiếp cận lập trình cho FPGA từ Spartan -3 68

Nếu vắng thành phần biểu thức, thì một giá trị mặc định sẽ được gán khi biến
được khởi tạo. Nếu giá trị mặc định đối với các kiểu vô hướng là giá trị bên trái
nhất của kiểu đó, thì đó là kiểu số đầu tiên trong danh sách, và là giá trị thấp nhất
theo thứ tự sắp xếp từ dưới lên, hoặc cao nhất từ trên xuống. Nếu biến là một kiểu
tổ hợp, thì giá trị mặc định là tổ hợp của các giá trị mặc định đối với từng phần tử,
dựa trên kiểu của phần tử.
Ví dụ:
variable count : natural := 0;
variable trace : trace_array;
Dựa vào một đối tượng có sẵn, nó có thể cung cấp tên thay thế cho một phần
hoặc cả đối tượng đó. Để thực hiện, người ta sử dụng kiểu mô tả tên phụ (alias). Cú
pháp như sau:
mô tả tên phụ ::= alias tên dịnh danh : biểu diễn kiểu con is tên;
Ví dụ như sau:
variable instr : bit_vector(31 downto 0);
alias op_code : bit_vector(7 downto 0) is instr(31 downto 24);
4.2.2.9. Các thuộc tính
Các kiểu và các đối tượng được mô tả trong một mô tả VHDL có thể có thêm
thông tin phụ, và được gọi là các thuộc tính, tương ứng với chúng. Đây là một số
thuộc tính chuẩn đã định nghĩa sẵn. Một thuộc tính được tham chiếu bằng cách sử
dụng dấu “`”. Ví dụ:
thing`attr
Đầu tiên, đối với bất kỳ kiểu vô hướng hoặc kiểu con T nào, có thể sử dụng
các thuộc tính sau:
Thuộc tính Kết quả trả về
T'left Left bound of T --
T'right Right bound of T
T'low Lower bound of T
T'high Upper bound of T
Tiếp cận lập trình cho FPGA từ Spartan -3 69

Đối với phạm vi sắp xếp tăng dần, T`left = T`low và T`right = T`high. Đối với
phạm vi sắp xếp giảm dần, T`left = T`high và T`right = T`low.
Thứ hai, đối với bất kỳ kiểu vật lý hoặc kiểu con T, kiểu con X là một phần
của kiểu con T và kiểu số nguyên N, thì có thể sử dụng các thuộc tính sau:
Thuộc tính Kết quả trả về
T'pos(X) Position number of X in T
T'val(N) Value at position N in T
T'leftof(X) Value in T which is one position left from X
T'rightof(X) Value in T which is one position right from X
T'pred(X) Value in T which is one position lower than X
T'succ(X) Value in T which is one position higher than X
Đối với phạm vi sắp xếp tăng dần, T'leftof(X) = T'pred(X) và T'rightof(X) =
T'succ(X). Đối với phạm vi sắp xếp giảm dần T'leftof(X) = T'succ(X) và
T'rightof(X) = T'pred(X).
Thứ ba, đối với bất kỳ kiểu mảng hoặc đối tượng kiểu A, thì có thể sử dụng
các thuộc tính sau:
Thuộc tính Kết quả trả về
A'left(N) Left bound of index range of dim’n N of A
A'right(N) Right bound of index range of dim’n N of A
A'low(N) Lower bound of index range of dim’n N of A
A'high(N) Upper bound of index range of dim’n N of A
A'range(N) Index range of dim’n N of A
A'reverse_range(N) Reverse of index range of dim’n N of A
A'length(N) Length of index range of dim’n N of A
4.2.3. Các biểu thức và toán tử
Các biểu thức trong VHDL hoàn toàn tương tự như các biểu thức trong các
ngôn ngữ lập trình khác. Một biểu thức là một công thức kết hợp các đa thức với
các toán tử. Các đa thức bao gồm tên các đối số, ký hiệu bằng chữ, các hàm gọi và
Tiếp cận lập trình cho FPGA từ Spartan -3 70

các dấu ngoặc của biểu thức. Các toán tử được liệt kê trong bảng 4.1 theo thứ tự ưu
tiên.
Bảng 4.1. Các toán tử và thứ tự ưu tiên

Ưu tiên cao nhất: **; abs; not;

*; /; mod; rem;

+ (sign); – (sign)

+; –; &;

=; /=; <; <=; >; >=;

Ưu tiên thấp nhất: And; or; nand; nor; xor;

Các toán tử logic and, or, nand, nor, xor và not ho ạt động trên các giá trị kiểu
bit hoặc là boolean, và trên các mảng 1 chiều của các kiểu đó. Đối với các toán hạng
là mảng (array), sự hoạt động tuân theo các phần tử tương ứng của từng mảng,
thường thì độ lớn của mảng có cùng độ lớn với kết quả. Đối với các toán hạng bit và
boolean, các toán tử and, or, nand và nor là các toán t ử “ngắn mạch” (short-circuit),
do chúng chỉ ưu tiên hơn so với toán hạng bên phải của chúng nếu toán hạng bên
trái không xác định được kết quả. Do and và nand chỉ ưu tiên hơn so với toán hạng
bên phải nếu toán hạng bên trái là “true” hoặc “1”, còn or và nor chỉ ưu tiên hơn
toán hạng bên phải nếu toán hạng bên trái là “false” hoặc “0”.
Các toán tử quan hệ =, /=, <, <=, > và >= phải có các toán hạng ở hai đầu cùng
kiểu, và thường là cho các kết quả theo kiểu boolean. Các toán tử bằng (= và /=) có
thể có các toán hạng theo bất kỳ kiểu nào. Đối với các toán tử so sánh, hai giá trị
bằng nhau nếu tất cả các phần tử tương ứng của chúng bằng nhau. Các toán tử còn
lại phải có các toán hạng có kiểu vô hướng hoặc mạng một chiều của các kiểu riêng
biệt.
Các toán tử dấu (+ và -), các toán tử cộng (+) và trừ (-) có cách sử dụng của
chúng trên các toán hạng dạng số. Toán tử móc xích (&) làm việc trên các mảng
Tiếp cận lập trình cho FPGA từ Spartan -3 71

một chiều thành dạng một mảng mới với nội dung của toán hạng bên phải kế tiếp
toán hạng trái. Nó có thể móc nối một phần tử đơn vào một mảng hoặc hai phần tử
độc lập để thành một mảng mới. Toán tử móc xích sử dụng cho hầu hết các xâu.
Các toán tử nhân (*) và chia (/) làm việc với các số nguyên, số dấu phẩy động
và các kiểu đơn vị vật lý. Các toán tử chia lấy phần nguyên (mod- modulus) và chia
lấy phần dư (rem - remainder) chỉ làm việc với kiểu số nguyên. Toán tử giá trị tuyệt
đối (abs) chỉ làm việc với bất kỳ kiểu số nào. Cuối cùng toán tử mũ (**) có thể làm
việc với kiểu số nguyên và toán hạng trái của kiểu số dấu phẩy động, nhưng phải có
một số nguyên ở toán hạng phải, còn toán hạng phải mà âm chỉ được phép nếu toán
hạng trái là một số dấu phẩy động dạng số.
4.2.4. Các khai báo tuần tự
VHDL cung cấp một cong cụ hiệu quả cho việc kiểm tra trạng thái của các đối
tượng và điều khiển luồng hoạt động của các mô hình.
4.2.4.1. Phép gán biến (Variable Assignment)
Như các ngôn ngữ lập trình khác, một biến được gán một giá trị mới bằng
cách sử dụng một khai báo gán. Cú pháp là:
khai báo gán biến ::= đích := biểu thức;
đích ::= tên | tập hợp
Trong trường hợp đơn giản nhất, đích của phép gán là một tên đối tượng, và
giá trị của biểu thức được gán theo tên đối tượng. Đối tượng và giá trị phải có cùng
một kiểu.
Nếu đích của phép gán là một tập hợp, thì các phần tử được liệt kê phải là các
tên của đói tượng và giá trị của biểu thức phải là một giá trị đa thức có cùng kiểu
như tập hợp. Đầu tiên, tất cả các tên trong tập hợp được đánh giá, sau đó biểu thức
được đánh giá, và cuối cùng là các thành phần của giá trị biểu thức được gán thành
tên các biến. Việc này được gọi là hiệu ứng của phép gán song song. Ví dụ, nếu một
biến r làm một bản ghi có hai trường a và b, thì chúng có thể trao đổi bằng cách
viết:
(a => r.b, b => r.a) := r
Tiếp cận lập trình cho FPGA từ Spartan -3 72

4.2.4.2. Khai báo If (nếu)


Khai báo If cho phép lựa chọn các khai báo để kích hoạt phụ thuộc vào một
hoặc nhiều điều kiện. Cú pháp là:
khai báo if ::=
if điều kiện then
chuỗi các khai báo
{ elsif điều kiện then
chuỗi các khai báo
[ else
chuỗi các khai báo }
end if;
Các điều kiện là các biểu thức trả về giá trị boolean. Các điều kiện được đánh
giá là đúng khi có một giá trị được trả về “true”. Ngược lại thì mệnh đề else được
thực hiện, và khai báo của nó được kích hoạt.
4.2.4.3. Khai báo Case (cây)
Khai báo case cho phép lựa chọn các khai báo để kích hoạt phụ thuộc vào giá
trị của một biểu thức chọn. Cú pháp là:
khai báo case ::=
case biểu thức is
khai báo case có thể chọn
{ khai báo case có thể chọn }
end case;
khai báo case có thể chọn ::=
when các lựa chọn =>
chuỗi các khai báo
các lựa chọn ::= lựa chọn { | lựa chọn}
lựa chọn ::=
biểu thức đơn
| phạm vi đơn
Tiếp cận lập trình cho FPGA từ Spartan -3 73

| tên phần tử đơn


| others
Biểu thức chọn phải đưa đến hoặc là kiểu rời rạc hoặc là mảng một chiều các
ký tự. Khi một lựa chọn trong danh sách chứa giá trị của biểu thức được chọn thì
khai báo của lựa chọn đó được kích hoạt. Các lựa chọn phải độc lập, không có giá
trị trùng nhau. Ngoài ra, tất cả các giá trị phải được biểu diễn trong bảng danh sách
các lựa chọn, hoặc others đặc biệt phải nằm trong khai báo cuối cùng. Nếu không có
lựa chọn nào chứa giá trị của biểu thức, thì others sẽ được chọn. Nếu biểu thức kết
quả là một mảng, thì các lựa chọn có thể là một xâu hoặc một xâu bít.
Ví dụ thứ nhất:
case element_colour of
when red =>
statements for red;
when green | blue =>
statements for green or blue;
when orange to turquoise =>
statements for these colours;
end case;
Ví dụ thứ 2:
case opcode of
when X"00" => perform_add;
when X"01" => perform_subtract;
when others => signal_illegal_opcode;
end case;
4.2.4.4. Các khai báo vòng lặp
VHDL có một khai báo vòng lặp cơ bản có thể sử dụng các vòng lặp while và
for giống như trong các ngôn ngữ lập trình khác. Cú pháp của một khai báo vòng
lặp là:
khai báo vòng lặp ::=
Tiếp cận lập trình cho FPGA từ Spartan -3 74

[ nhãn vòng lặp : ]


[ lược đồ lặp ] loop
chuỗi các khai báo
end loop [ nhãn vòng lặp ];
lược đồ lặp ::=
while điều kiện
| for đặc điểm tham số lặp
đặc điểm tham số lặp
định danh in phạm vi đơn
Nếu lược đồ lặp bị bỏ qua, chúng ta sẽ bị rơi vào vòng lặp vô tận. Ví dụ về
một vòng lặp vô tận:
loop
do_something;
end loop;
Lược đồ lặp while cho phép kiểm tra một điều kiện để đánh giá trước mỗi một
vòng lặp. Vòng lặp chỉ được thực hiện nếu việc kiểm tra được đánh giá là đúng
(true). Nếu việc kiểm tra là sai (false), thì khai báo vòng lặp sẽ kết thúc. Ví dụ như
sau:
while index < length and str(index) /= ‘’ loop
index := index + 1;
end loop;
Lược đồ lặp for cho phép chỉ định một số cố định vòng lặp. Đặc điểm tham số
lặp được mô tả là một đối tượng sẽ thực hiện với giá trị đúng trong phạm vi đã cho
đối với mỗi vòng lặp. Cùng với các khai báo đóng trong vòng lặp, đối tượng được
coi như là hằng số, và do vậy không thể gán được. Ví dụ như:
for item in 1 to last_item loop
table(item) := 0;
end loop;
Tiếp cận lập trình cho FPGA từ Spartan -3 75

Có hai khai báo bổ sung có thể sử dụng bên trong vòng lặp để kiểm tra mẫu cơ
sở của vòng lặp. Khai báo “next” kết thúc việc kích hoạt vòng lặp hiện thời và bắt
đầu vòng lặp con. Khai báo “exit” kết thúc việc kích hoạt vòng lặp hiện thời và kết
thúc luôn vòng lặp. Cú pháp của các khai báo đó là:
khai báo next ::= next [ nhãn vòng lặp ] [ when điều kiện ];
khai báo exit ::= exit [ nhãn vòng lặp ] [ when điều kiện ];
Nếu nhãn vòng lặp bị bỏ qua, thì khai báo được áp dụng cho vòng lặp gần nó
nhất, ngược lại nó được gán cho tên của vòng lặp. Nếu mệnh đề when tồn tại nhưng
điều kiện lại sai (false), thì vòng lặp sẽ tiếp tục bình thường.
Ví dụ:
for i in 1 to max_str_len loop
a(i) := buf(i);
exit when buf(i) = NUL;
end loop;
Ví dụ:
outer_loop : loop
inner_loop : loop
do_something;
next outer_loop when temp = 0;
do_something_else;
end loop inner_loop;
end loop outer_loop;
4.2.4.5. Khai báo Null (rỗng)
Khai báo null không có hiệu quả. Nó có thể được sử dụng để biểu diễn trạng
thái rõ ràng là không có hành động nào được yêu cầu trong trường hợp đó. Người ta
thường sử dụng nó trong các khai báo case, trong đó tất cả các giá trị có thể của
biểu thức lựa chọn phải được liệt kê để chọn, nhưng đối với một vài lựa chọn thì
không yêu cầu hành động nào.
Ví dụ:
Tiếp cận lập trình cho FPGA từ Spartan -3 76

case controller_command is
when forward => engage_motor_forward;
when reverse => engage_motor_reverse;
when idle => null;
end case;
4.2.4.6. Các xác nhận
Một khai báo xác nhận được sử dụng để kiểm tra một điều kiện đã được chỉ
định và lập báo cáo nếu điều kiện đó bị vi phạm. Cú pháp là:
khai báo xác nhận ::=
assert điều kiện
[ report biểu thức ]; -- báo cáo bằng biểu thức
[ severity biểu thức ]; -- mức độ vi phạm bằng biểu thức
Nếu mệnh đề report tồn tại, kết quả của biểu thức phải trả về một xâu. Đây là
thông điệp sẽ được báo cáo nếu điều kiện là sai (false). Nếu nó bị bỏ qua, thông
điệp mặc định là “Assertion violation”. Nếu mệnh đề severity tồn tại, biểu thức phải
trả về mức độ vi phạm severity_level. Nếu nó bị bỏ qua, giá trị ngầm định là error.
4.2.5. Các chương trình con và các dạng đóng gói
Giống như các ngôn ngữ lập trình khác, VHDL cung cấp công cụ thực hiện
chương trình con linh hoạt dưới dạng các thủ tục và các hàm. VHDL cũng cung cấp
một kiểu đóng gói mạnh đối với tập các mô tả và các đối tượng đưa vào các đơn vị
dạng modul. Các đóng gói cũng cung cấp một tiêu chuẩn về tính trừu tượng dữ liệu
và thông tin ẩn.
4.2.5.1. Các thủ tục và hàm
Các chương trình con dạng thủ tục và hàm được mô tả theo cú pháp sau:
mô tả chương trình con ::= đặc điểm chương trình con ;
đặc điểm chương trình con ::=
procedure tên chỉ định [ ( danh sách tham biến chính ) ]
| function tên chỉ định [ ( danh sách tham biến chính ) ] return đánh dấu
kiểu trả về
Tiếp cận lập trình cho FPGA từ Spartan -3 77

Một chương trình con mô tả theo cú pháp trên có các tên đơn giản của chương
trình con và chỉ định tham số theo yêu cầu. Thân của các khai báo định nghĩa hoạt
động của chương trình con phải thực hiện theo. Đối với các chương trình con dạng
hàm (function), việc mô tả cũng chỉ ra kiểu của kết quả trả về khi hàm được gọi.
Khuôn dạng chương trình con này mô tả kiểu thông dụng được dùng trong các đặc
điểm đóng gói (xem thêm mục 3.2.5.3), trong đó thân chương trình con được gán
trong thân của đóng gói, hoặc để định nghĩa cho các thủ tục đệ qui.
Cú pháp đối với việc chỉ định các tham biến chính của chương trình con là:
danh sách tham biến chính ::= danh sách giao diện tham số
danh sách giao diện ::= phần tử giao diện { ; phần tử giao diện }
phần tử giao diện ::= mô tả giao diện
mô tả hằng số giao diện
| mô tả tín hiệu giao diện
| mô tả biến giao diện
mô tả hằng số giao diện
[constant] danh sách định danh : [in] ký hiệu kiểu con [ := biểu thức tĩnh ]
mô tả biến giao diện
[variable] danh sách định danh : [mode] ký hiệu kiểu con [ := biểu thức tĩnh
]
Chúng ta chỉ đề cập đến các tham số hằng số và các tham số biến, mặc dù các
tham số tín hiệu cũng có thể được sử dụng, tuy nhiên phần này được đề cập đến
trong mục 3.3. Đầu tiên là ví dụ đơn giản về một thủ tục không có tham số:
procedure reset;
Việc gọi thủ tục reset trên thực hiện đơn giản như sau:
reset;
Tiếp theo là một thủ tục có vài tham số:
procedure increment_reg(variable reg: inout word_32;
constant incr: in integer := 1)
Tiếp cận lập trình cho FPGA từ Spartan -3 78

Thủ tục increment_reg có hai tham số là reg và incr. reg là tham biến, trong
thân chương trình con nó được sử dụng như một đối tượng biến và có thể gán cho
nó được. Còn khi thủ tục increment_reg được gọi thì tham số thực phải tương ứng
với biến reg và bản thân tham số thực đó cũng phải là biến cùng kiểu. Chế độ của
reg là inout, có nghĩa là biến này có thể đọc hoặc gán. Nếu chỉ có chế độ in - tức là
chỉ đọc, còn out - tức là chỉ gán. Khi chế độ là inout hoặc out thì có thể bỏ qua từ
variable và được ngầm định.
Còn tham số thứ hai là incr, là một tham hằng (tham số hằng số), có nghĩa là
nó được gán là một đối tượng hằng số trong thân chương trình con, và không thể
gán cho nó được.
Việc gọi một chương trình con bao gồm một danh sách các tham số thực
tương ứng với tham số chính của chương trình con có thể cả về vị trí và tên gọi hoặc
cả hai. Ví dụ:
increment_reg(index_reg, offset-2); -- bổ sung giá trị vào index_reg
increment_reg(prog_counter); -- bổ sung 1 (mặc định) vào
prog_counter
Việc gọi theo tên gọi tương ứng theo tham số chính như ví dụ sau:
increment_reg(incr => offset-2, reg => index_reg);
increment_reg(reg => prog_counter);
Trong lệnh gọi thứ hai, tham số incr không có, nên nó được gán giá trị mặc
định.
Ví dụ về mô tả một hàm:
function byte_to_int(byte : word_8) return integer;
Hàm trên chỉ có một tham số. Đối với các hàm, chế độ tham số phải là in. Nếu
lớp tham số không được chỉ định thì nó được coi như là constant. Giá trị trả về của
hàm trên phải là một số nguyên (integer).
Khi thân của chương trình con được chỉ định, cú pháp sử dụng như sau:
thân chương trình con ::=
tham số chương trình con is
Tiếp cận lập trình cho FPGA từ Spartan -3 79

thành phần mô tả chương trình con


begin
thành phần khai báo chương trình con
end [tên chỉ định];
thành phần mô tả chương trình con ::= {mục mô tả chương trình con}
thành phần khai báo chương trình con ::= {khai báo tuần tự}
mục mô tả chương trình con ::=
mô tả chương trình con
| thân chương trình con
| mô tả kiểu
| mô tả kiểu con
| mô tả hằng số
| mô tả biến
| mô tả tên phụ
Các mục mô tả chương trình con được liệt kê sau đặc điểm của chương trình
con mô tả tất cả những gì được sử dụng cục bộ trong thân chương trình con. Tên
của các mục này không nhìn thấy được ở ngoài chương trình con. Ngoài ra, các mục
này là bóng của tất cả những gì có cùng tên đã được mô tả ở ngoài chương trình
con.
Khi một chương trình con được gọi, các khai báo trong thân được kích hoạt
đến khi hoặc là gặp dấu kết thúc danh sách khai báo hoặc khi một khai báo quay lại
(return) được kích hoạt. Cú pháp của một khai báo quay lại như sau:
khai báo quay lại ::= return [biểu thức];
Nếu một khai báo quay lại xảy ra trong thân của một thủ tục, thì nó phải không
được chứa một biểu thức. Và cũng phải có ít nhất một khai báo quay lại trong thân
một hàm, nó phải chứa một biểu thức, và hàm được kết thúc bằng việc kích hoạt
một khai báo quay lại. Giá trị của biểu thức là giá trị được trả về khi gọi hàm.
Ví dụ :
function byte_to_int(byte : word_8) return integer is
Tiếp cận lập trình cho FPGA từ Spartan -3 80

variable result : integer := 0;


begin
for index in 0 to 7 loop
result := result*2 + bit'pos(byte(index));
end loop;
return result;
end byte_to_int;
4.2.5.2. Overloading (trùng tên)
VHDL cho phép hai chương trình con cùng tên, nhưng tên và kiểu của các
tham số phải khác nhau. Tên của chương trình con như vậy được gọi là tên
overloaded (trùng tên). Khi thực hiện gọi một chương trình con sử dụng tên
overloaded, thì số lượng tham số thực, thứ tự của chúng, kiểu của chúng và các tên
tham số chính tương ứng được sử dụng để xác định là chương trình con nào được
gọi. Nếu cuộc gọi là một cuộc gọi hàm, thì kiểu kết quả trả về sẽ được sử dụng. Ví
dụ, chúng ta có hai chương trình con sau:
function check_limit(value:integer) return boolean;
function check_limit(value:word_32) return boolean;
Sau đó, khi hàm nào được gọi thì căn cứ vào giá trị của kiểu integer hay là
word_32 mà tham số thực sử dụng để biết. Chẳng hạn:
test:=check_limit(4095)
sẽ gọi hàm thứ nhất, còn:
test:=check_limit(X“0000_0FFF”)
sẽ gọi hàm thứ hai.
Người sử dụng có thể sử dụng cách định nghĩa tên chương trình con theo một
định danh hoặc một xâu biểu diễn bởi các ký hiệu toán tử như đã liệt kê trong phần
2.2.3. Ví dụ như sau:
function “+”(a,b:word_32) return word_32 is
begin
return int_to_word_32(word_32_to_int(a)+word_32_to_int(b));
Tiếp cận lập trình cho FPGA từ Spartan -3 81

end “+”;
Toán tử cộng, khi thực hiện phép tính của mình, chỉ thực hiện với các toán
hạng là số nguyên. Còn khi hàm “+” này được gọi thì nó lại thực hiện biểu thức với
kiểu word_32 theo hai cách như sau:
X“1000_0010” + X“0000_FFD0”
hoặc là:
“+” (X“1000_0010”,X“0000_FFD0”)
4.2.5.3. Đóng gói và mô tả thân các đóng gói
Một đóng gói là một tập bao gồm các kiểu, các hằng số, các chương trình con
và có thể cả các thành phần khác, thường thường nó được dự định cho hoạt động
của một vài dịch vụ thành phần hoặc để tách một nhóm các thành phần có liên quan
với nhau. Đặc biệt, chi tiết các giá trị hằng số và các thân chương trình con có thể
ẩn do người dùng, chúng chỉ nhìn thấy được giao diện mà thôi.
Một đóng gói có thể phân chia thành hai phần: phần mô tả đóng gói dùng để
định nghĩa giao diện của nó và phần thân đóng gói dùng để định nghĩa các chi tiết
khác. Phần thân có thể bỏ qua nếu chúng không có chi tiết nào. Cú pháp của phần
mô tả đóng gói như sau:
mô tả đóng gói ::=
package tên định danh is
phần mô tả đóng gói
end [tên đơn của đóng gói];
phần mô tả đóng gói ::= {thành phần mô tả đóng gói}
thành phần mô tả đóng gói ::=
mô tả chương trình con
| mô tả kiểu
| mô tả kiểu con
| mô tả hằng số
| mô tả tên phụ
| mệnh đề sử dụng
Tiếp cận lập trình cho FPGA từ Spartan -3 82

Các mô tả định nghĩa các thành phần khác mà nhìn thấy được đối với người
dùng đóng gói, và chúng cũng nhìn thấy được bên trong của thân đóng gói.
Ví dụ :
package data_types is
subtype address is bit_vector(24 downto 0);
subtype data is bit_vector(15 downto 0);
constant vector_table_loc : address;
function data_to_int(value : data) return integer;
function int_to_data(value : integer) return data;
end data_types;
Trong ví dụ trên, giá trị của hằng số vector_table_loc và thân của hai chương
trình con khác nhau, do vậy thân một đóng gói cũng cần phải gán.
Cú pháp của thân một đóng gói là:
thân đóng gói ::=
package body tên đơn của thân đóng gói is
phần mô tả thân đóng gói
end [tên đơn của thân đóng gói];
phần mô tả thân đóng gói ::= {thành phần mô tả thân đóng gói}
thành phần mô tả thân đóng gói ::=
mô tả chương trình con
| mô tả kiểu
| mô tả kiểu con
| mô tả hằng số
| mô tả tên phụ
| mệnh đề sử dụng
Thân đối với đóng gói data_types biểu diễn ở trên có thể viết lại như sau:
package body data_types is
constant vector_table_loc : address := X"FFFF00";
function data_to_int(value : data) return integer is
Tiếp cận lập trình cho FPGA từ Spartan -3 83

body of data_to_int
end data_to_int;
function int_to_data(value : integer) return data is
body of int_to_data
end int_to_data;
end data_types;
Trong thân đóng gói này, giá trị đối với hằng số được chỉ định trước, và thân
đóng gói phải được gán trước. Các mô tả kiểu con không được lặp lại, do các mô tả
đóng gói là nhìn thấy được trong thân đóng gói.
4.2.5.4. Thay thế Tên và sử dụng đóng gói
Khi một đóng gói đã được mô tả, thì các thành phần đã mô tả kèm với nó có
thể sử dụng bằng cách lấy tiền tố các tên của chúng với tên đóng gói. Như ví dụ
trong mục 3.2.5.3, các thành phần đã được mô tả có thể sử dụng như sau:
variable PC : data_types.address;
int_vector_loc := data_types.vector_table_loc + 4*int_level;
offset := data_types.data_to_int(offset_reg);
Thường thường để thuận tiện có thể tham chiếu đến các tên từ một đóng gói
mà không cần có điều kiện là mỗi khi sử dụng phải kèm với tên đóng gói. Việc này
được thực hiện bằng mệnh đề Use (sử dụng) trong một mô tả cục bộ. Cú pháp của
nó như sau:
mệnh đề sử dụng ::= use tên được chọn { , tên được chọn};
tên được chọn ::= tiền tố . hậu tố
Tác dụng của mệnh đề use là cho tất cả các tên đã được liệt kê rồi sau đó có
thể sử dụng mà không cần tiền tố của chúng. Nếu tất cả các tên đã được mô tả trong
một đóng gói đều sử dụng cách này, thì ta có thể dùng một hậu tố đặc biệt là all, ví
dụ như:
use data_types.all;
4.3. Mô tả cấu trúc của VHDL
Tiếp cận lập trình cho FPGA từ Spartan -3 84

Trong mục 4.1 tác giả đã giới thiệu về một số thuật ngữ dùng để mô tả cấu
trúc của một hệ thống số. Trong mục này, tác giả sẽ đề cập đến việc mô tả hệ thống
số đó bằng ngôn ngữ VHDL.
4.3.1. Mô tả đầu vào
Một hệ thống số thường được thiết kế dưới dạng kiến trúc kiểu các khối
(module). Mỗi một khối sẽ có một số cổng vào/ra dùng để giao tiếp giữa khối đó với
các thành phần bên ngoài. Trong VHDL, khái niệm thực thể (an entity) thực ra cũng
là một khối thành phần trong quá trình thiết kế, mà thường là nó được thiết kế ở
mức trên cùng (top level) - còn gọi là thực thể.
Cú pháp mô tả một thực thể như sau:
mô_tả_thực_thể ::=
entity tên_định_danh is
header_của_thực_thể
phần_chính_của_thực_thể
[begin
phần_khai_báo_của_thực_thể ]
end [tên_của_thực_thể];
header_của_thực_thể ::=
[mệnh_đề_nghi_thức_chung]
[mệnh_đề_nghi_thức_cổng]
mệnh_đề_chung ::= generic (liệt_kê_đặc_điểm_chung);
liệt_kê_đặc_điểm_chung ::= liệt_kê_giao_diện_chung
mệnh_đề_cổng ::= port (liệt_kê_cổng);
liệt_kê_cổng ::= liệt_kê_giao_diện_cổng
phần_chính_của_thực_thể ::= {các_thành_phần_của_thực_thể}
Phần chính của thực thể có thể được sử dụng để mô tả các thành phần sẽ được
dùng trong quá trình hoạt động của thực thể.
Thành phần header của thực thể là thành phần gần như quan trọng nhất trong
mô tả thực thể. Nó có thể chứa các thành phần hằng số chung (generic constant),
Tiếp cận lập trình cho FPGA từ Spartan -3 85

được dùng để điều khiển cấu trúc và hoạt động của thực thể, các cổng (port), là các
kênh thông tin vào và ra của thực thể.
Các hằng số chung được chỉ định sử dụng giao diện liệt kê tương tự như việc
mô tả một chương trình con. Tất cả các thành phần con đều phải là thuộc lớp hằng
số chung này. Do vậy, cú pháp chủ yếu của hằng số giao tiếp (interface constant)
được mô tả như sau:
mô_tả_hằng_số_giao_tiếp ::=
[constant] liệt_kê_định_danh : [in] biểu_diễn_kiểu_con [ := biểu_diễn_tĩnh]
Giá trị thực đối với mỗi hằng số chung được bỏ qua khi thực thể được sử dụng
như một thành phần của quá trình thiết kế.
Các cổng (port) đầu vào cũng đã được chỉ định việc sử dụng trong giao diện
đã được liệt kê, nhưng các thành phần trong danh sách liệt kê phải được phân vào
lớp tín hiệu. Cú pháp là:
mô_tả_tín_hiệu_giao_tiếp ::=
[signal] liệt_kê_định_danh : [mode] biểu_diễn_kiểu_con [bus] [ :=
biểu_diễn_tĩnh]
Do giữa các lớp phải có tín hiệu nên từ signal được bỏ qua và coi như là có
sẵn. Từ bus có thể được sử dụng nếu các cổng được nối tới nhiều hơn một đầu ra.
Để dễ hiểu, chúng ta xem xét ví dụ sau:
entity processor is
generic (max_clock_freq : frequency := 30 MHz);
port (clock : in bit;
address : out integer;
data : inout word_32;
control : out proc_control;
ready : in bit);
end processor;
Tiếp cận lập trình cho FPGA từ Spartan -3 86

Trong trường hợp này, hằng số chung là max_clock_freq được sử dụng để chỉ
rõ tần số hoạt động của thực thể. Việc mã hoá hoạt động của thực thể sẽ sử dụng giá
trị này để xác định độ trễ trong quá trình các giá trị tín hiệu thay đổi.
Tiếp theo là ví dụ về các tham số chung được sử dụng để chỉ định lớp của các
đầu vào với cấu trúc có thể thay đổi được.
Ví dụ:
entity ROM is
generic (width, depth : positive);
port (enable : in bit;
address : in bit_vector(depth–1 downto 0);
data : out bit_vector(width–1 downto 0) );
end ROM;
Ở đây hai hằng số chung là width và depth được sử dụng để chỉ định số lượng
các bit dữ liệu và các bit địa chỉ tương ứng cho ROM. Chú ý rằng không có giá trị
mặc định cho trước đối với các hằng số này.
Ví dụ cuối cùng là mô tả thực thể không có hằng số chung hay cổng nào:
entity test_bench is
end test_bench;
Việc mô tả này được biểu diễn trong hình 3.3. Thực thể mức trên cùng (top-
level) đối với việc thiết kế để kiểm tra (Design under test - DUT) được sử dụng như
một thành phần trong mạch kiểm tra chuẩn với một thực thể khác (TG).
Tiếp cận lập trình cho FPGA từ Spartan -3 87

Hình 4.3. Sơ đồ mạch kiểm tra chuẩn


4.3.2. Mô tả kiến trúc
Mỗi một thực thể có một mô tả định nghĩa riêng, tuy nhiên về hoạt động thì có
thể mỗi thực thể có nhiều chương trình khác nhau, do vậy mỗi thực thể còn có thể
được biểu diễn ở dạng cấu trúc chương trình. Việc mô tả kiến trúc này khác với mô
tả hoạt động sẽ được trình bầy trong mục 3.4.
Kiến trúc thân thực thể được mô tả theo cú pháp sau:
kiến_trúc_thân ::=
architecture tên_định_danh of tên_đầu_vào is
phần_chính_của_kiến_trúc
begin
phần_khai_báo_của_kiến_trúc
end [tên_của_kiến_trúc]
phần_chính_của_kiến_trúc ::= {các_mục_chính_trong_khối}
phần_khai_báo_của_kiến_trúc ::= {khai_báo_tương_tranh}
các_mục_chính_trong_khối ::=
mô_tả_chương_trình_con
| thân_chương_trình_con
| mô_tả_kiểu
| mô_tả_kiểu_con
| mô_tả_hằng_số
| mô_tả_tín_hiệu
| mô_tả_tín_hiệu
| mô_tả_tên_phụ_(bí_danh)
| mô_tả_thành_phần
| đặc_điểm_cấu_hình
| mệnh_đề_sử_dụng
khai_báo_tương_tranh ::=
khai_báo_khối
Tiếp cận lập trình cho FPGA từ Spartan -3 88

| khai_báo_thuyết_minh_các_thành_phần
4.3.2.1. Mô tả tín hiệu
Các tín hiệu được dùng để nối các khối con với nhau trong một thiết kế.
Chúng được mô tả theo cú pháp sau:
mô_tả_tín_hiệu ::=
signal liệt_kê_tên_định_danh : diểu_diễn_kiểu_con [loại_tín_hiệu] [:=
biểu_diễn];
loại_tín_hiệu ::= register | bus
Một điểm quan trọng cần chú ý là các cổng của một đối tượng cần phải xử lý
đúng tín hiệu cho đối tượng đó.
4.3.2.2. Các khối (Block)
Các modul con trong kiến trúc khối chính có thể được biểu diễn dưới dạng các
khối. Một khối là một đơn vị cấu trúc modul, với giao diện riêng, được nối đến các
khối khác hoặc các cổng bằng tín hiệu. Cú pháp mô tả khối như sau:
khai_báo_khối ::=
nhãn_khối :
block [(biểu_diễn_bắt_buộc)]
header_của_khối
phần_chính_của_khối
begin
phần_khai_báo_của_khối
end [nhãn_khối];
header_của_khối ::=
[ mệnh_đề_chung
[ ánh_xạ_của_mệnh_đề; ]]
[ mệnh_đề_cổng
[ ánh_xạ_của_cổng; ]]
ánh_xạ_của_mệnh_đề ::= generic map
(danh_sách_tương_ứng_các_mệnh_đề)
Tiếp cận lập trình cho FPGA từ Spartan -3 89

ánh_xạ_của_cổng ::= port map (danh_sách_tương_ứng_các_cổng)


phần_chính_của_khối ::= {các_mục_chính_của_khối}
phần_khai_báo_của_khối ::= {khai_báo_tương_tranh}
Biểu diễn header của khối cũng tương tự như header của đầu vào. Danh sách
tương ứng các mệnh đề chỉ định các giá trị đối với các hằng số chung. Danh sách
tương ứng các cổng chỉ định các tín hiệu thực hoặc các cổng từ vỏ của khối hoặc
thân kiến trúc được nối đến các cổng của khối đó.
Ví dụ về kiến trúc của một bộ vi xử lý trong ví dụ 3.3.1 ở phần 3.3.1 được mô
tả dưới dạng kiến trúc trong ví dụ dưới đây.
Ví dụ về kiến trúc có cấu trúc của một bộ vi xử lý:
architecture block_structure of processor is
type data_path_control is … ;
signal internal_control : data_path_control;
begin
control_unit : block
port (clk : in bit;
bus_control : out proc_control;
bus_ready : in bit;
control : out data_path_control);
port map (clk => clock,
bus_control => control, bus_ready => ready;
control => internal_control);
declarations for control_unit
begin
statements for control_unit
end block control_unit;
data_path : block
port (address : out integer;
data : inout word_32;
Tiếp cận lập trình cho FPGA từ Spartan -3 90

control : in data_path_control);
port map (address => address, data => data,
control => internal_control);
declarations for data_path
begin
statements for data_path
end block data_path;
end block_structure;
4.3.2.3. Mô tả các thành phần (component)
Thân kiến trúc cũng có thể sử dụng các kiểu mô tả đầu vào khác nhau có sẵn
trong thư viện. Do vậy, kiến trúc cần phải dùng để mô tả một thành phần nào đó.
Sau đó, là đặt cấu hình cho việc sử dụng thành phần đó. Cú pháp để mô tả thành
phần như sau:
mô tả thành phần ::=
component tên định danh
[ mệnh đề chung cục bộ ]
[ mệnh đề cổng cục bộ ]
end component;
Ví dụ về cổng NAND 3 đầu vào:
component nand3
generic (Tpd : Time := 1 ns);
port (a, b, c : in logic_level;
y : out logic_level);
end component;
Ví dụ về mô tả thành phần của bộ ROM với độ lớn các bit địa chỉ và dữ liệu
phụ thuộc vào các hằng số chung:
component read_only_memory
generic (data_bits, addr_bits : positive);
port (en : in bit;
Tiếp cận lập trình cho FPGA từ Spartan -3 91

addr : in bit_vector(depth–1 downto 0);


data : out bit_vector(width–1 downto 0) );
end component;
4.3.2.4. Thuyết minh thành phần
Sau khi mô tả một thành phần trong kiến trúc, chúng ta có thể mô tả thuyết
minh về nó theo cú pháp sau:
khai báo thuyết minh thành phần ::=
tên thành phần
[ ánh xạ của mệnh đề ]
[ ánh xạ của cổng ]
Ví dụ về thuyết minh thành phần đối với các mô tả thành phần trong 2 ví dụ về
cổng NAND và thành phần của ROM ở phần 3.3.2.3 như sau:
enable_gate: nand3
port map (a => en1, b => en2, c => int_req, y => interrupt);
parameter_rom: read_only_memory
generic map (data_bits => 16, addr_bits => 8);
port map (en => rom_sel, data => param, addr => a(7 downto 0);
3.4. VHDL mô tả hoạt động
Trong mục 1.1.2, chúng ta đã bắt đầu đề cập đến các hoạt động của một hệ
thống số có thể mô tả bằng ngôn ngữ lập trình. Trong phần này, chúng ta sẽ mô tả
hoạt động của một hệ thống số bao gồm các khai báo dành cho việc thay đổi giá trị
tín hiệu, đồng thời là phản ứng của hệ thống cũng thay đổi theo.
4.4.1. Chỉ định tín hiệu
Việc chỉ định tín hiệu thực hiện một hoặc nhiều giao tác đến tín hiệu (hoặc đến
cổng). Cú pháp của việc chỉ định tín hiệu là:
khai báo chỉ đinh tín hiệu ::= đích đến <= [transport] dạng sóng;
đích đến ::= tên | tập hợp
dạng sóng ::= dạng sóng phần tử { , dạng sóng phần tử }
dạng sóng phần tử ::=
Tiếp cận lập trình cho FPGA từ Spartan -3 92

biểu diễn giá trị [after biểu diễn thời gian]


| null [after biểu diễn thời gian]
Đích đến phải biểu diễn tín hiệu hoặc là kết hợp của nhiều tín hiệu. Nếu việc
biểu diễn thời gian cho quá trình trễ bị bỏ qua, thì nó được mặc định là 0 fs.
Mỗi một tín hiệu kết hợp với một dạng sóng đầu ra được ánh xạ đến (a
projected output waveform), là bảng liệt kê các giao tác cung cấp cho các giá trị sau
này cho tín hiệu. Việc chỉ định tín hiệu thêm vào các giao tác dạng sóng đó.
Ví dụ việc chỉ định tín hiệu như sau
s <= ‘0’ after 10 ns;
sẽ tạo cho tín hiệu là cho phép để thừa nhận giá trị đúng 10 ns sau khi việc chỉ
định được kích hoạt. Chúng ta có thể biểu diễn một dạng sóng đầu ra được ánh xạ
đến bằng đồ thị thông qua việc biểu diễn các giao tác theo trục thời gian. Nếu như
việc chỉ định trong ví dụ trên được kích hoạt tại thời điểm 5 ns, thì dạng sóng đầu ra
được ánh xạ đến sẽ có dạng:

15 ns
‘0’
Khi mô phỏng thời gian trong phạm vi 15 ns, giao tác này sẽ được thực hiện
và tín hiệu được cập nhập.
Giả sử rằng sau đó tại thời điểm 16 ns, việc chỉ định
s <= ‘0’ after 10 ns, ‘0’ after 20 ns;
được kích hoạt. Hai giao tác mới được thêm vào dạng sóng đầu ra được ánh xạ
đến như sau:

20 ns 36 ns
‘1’ ‘0’

Chú ý rằng khi nhiều giao tác được liệt kê trong một chỉ định tín hiệu, thì các
thời gian trễ được chỉ định phải sắp xếp theo thứ tự tăng dần.
Tiếp cận lập trình cho FPGA từ Spartan -3 93

Nếu một chỉ định tín hiệu được kích hoạt, và có một vài giao tác cũ của chỉ
định trước đã hoàn thành, thì các giao tác cũ đó có thể bị xoá. Điều này còn phụ
thuộc vào từ khoá “transport” có được thêm vào chỉ định mới không. Nếu có, chỉ
định “nói rằng” nó sử dụng “trễ chuyển tiếp” (transport delay). Trong trường hợp
này, tất cả các giao tác cũ xảy ra sau khi giao tác mới đầu tiên sẽ bị xoá trước khi
các giao tác mới được thêm vào. Chúng ta xem lại ví dụ trên, nếu chỉ định:
s <= transport ‘Z’ after 10 ns;
được kích hoạt tại thời điểm 18 ns, thì sau đó giao tác thực hiện đối với 36 ns
sẽ bị xoá, và dạng sóng đầu ra được ánh xạ đến sẽ trở thành:

20 ns 28 ns
‘T’ ‘Z’

Loại trễ thứ hai là “trễ quán tính” (inertial delay), được sử dụng trong mô hình
các thiết bị mà không phản ứng với đầu vào dao động ngắn hơn trễ đầu ra của nó.
Trễ quán tính được chỉ định bằng cách giả định từ “transport” từ việc chỉ định tín
hiệu.
Khi giao tác trễ quán tính được thêm vào dạng sóng đầu ra được ánh xạ đến,
thì đầu tiên là tất cả các giao tác cũ đang thực hiện xảy ra sau giao tác mới sẽ bị xoá,
và giao tác mới được thêm vào theo trường hợp trễ chuyển tiếp. Tiếp theo là tất cả
các giao tác cũ đang thực hiện trước giao tác mới sẽ được kiểm tra. Nếu có bất kỳ
giá trị nào khác với giao tác mới, thì tất cả các giao tác được tăng lên bằng với giao
tác cuối cùng theo giá trị khác đó đều bị xoá. Các giao tác còn lại có cùng giá trị
được đẩy sang trái.
Để phân tích việc này, chúng ta xem sơ đồ dạng sóng đầu ra được ánh xạ đến:

20 ns 28 ns 20 ns 28 ns
‘T’ ‘Z’ ‘T’ ‘Z’

và việc chỉ định:


S <= ‘1’ after 25 ns;
Tiếp cận lập trình cho FPGA từ Spartan -3 94

được kích hoạt cũng tại thời điểm 0 ns. Sau đó dạng sóng đầu ra được ánh xạ
đến mới sẽ là:

20 ns 28 ns
‘T’ ‘Z’

Khi việc chỉ định tín hiệu với đa thành phần dạng sóng được chỉ định cùng với
trễ quán tính, thì chỉ có giao tác đầu tiên sử dụng trễ quán tính, còn các giao tác còn
lại sử dụng trễ chuyển tiếp.
4.4.2. Khai báo Xử lý và Đợi
Thành phần chính của mô tả hoạt động trong VHDL là việc xử lý (process).
Một xử lý là một chương trình tuần tự được mã hoá có thể được kích hoạt khi đáp
ứng lại sự thay đổi trạng thái. Khi nhiều hơn một xử lý được kích hoạt tại cùng một
thời điểm, chúng kích hoạt trạng thái đồng thời Một xử lý được chỉ định trong một
khai báo Xử lý, với cú pháp như sau:
khai báo xử lý :: =
[nhãn xử lý : ]
process [(liệt kê các thành phần nhạy cảm)]
phần chính của xử lý
begin
phần khai báo của khối đầu vào
end process [nhãn xử lý] ;
phần chính của khối đầu vào :: = {thành phần của khối đầu vào}
thành phần của khối đầu vào :: =
mô tả chương trình con
| thân chương trình con
| mô tả kiểu
| mô tả kiểu con
| mô tả hằng số
| mô tả biến
Tiếp cận lập trình cho FPGA từ Spartan -3 95

| mô tả tên phụ (bí danh)


| mệnh đề sử dụng
phần khai báo của khối đầu vào :: = {khai báo tuần tự}
khai báo tuần tự :: =
| khai báo xác nhận
| khai báo chỉ định tín hiệu
| khai báo chỉ định biến
| khai báo gọi thủ tục (procedure_call)
| khai báo “if” (nếu)
| khai báo “case” (cây)
| khai báo “loop” (vòng lặp)
| khai báo “next” (tiếp theo)
| khai báo “exit” (thoát)
| khai báo “return” (quay lại)
| khai báo “null” (rỗng)
Một khai báo xử lý là một khai báo đồng thời mà có thể sử dụng trong một
kiến trúc kiểu cây hoặc khối. Các mô tả định nghĩa các thành phần mà có thể được
sử dụng cục bộ trong cùng một quá trình.
Một quá trình có thể chứa một số khai báo chỉ định tín hiệu đối với tín hiệu
được chỉ định, mà bắt nguồn từ một bộ điều khiển (driver) đối với tín hiệu. Thông
thường chỉ có thể có một bộ điều khiển cho một tín hiệu, và do mã chương trình xác
định một giá trị tín hiệu ràng buộc vào một quá trình.
Một quá trình được kích hoạt khởi động trong khi pha khởi động việc mô
phỏng. Nó kích hoạt tất cả các khai báo tuần tự, và sau đó lặp lại, bắt đầu lại từ khai
báo đầu tiên. Một quá trình có thể tự nó tạm thời ngừng bằng cách kích hoạt một
khai báo đợi (wait statement). Khuôn dạng của nó như sau:
khai báo đợi :: =
wait [mệnh đề độ nhạy] [mệnh đề điều kiện] [mệnh đề thời gian chờ]
mệnh đề độ nhạy :: = on danh sách độ nhạy
Tiếp cận lập trình cho FPGA từ Spartan -3 96

danh sách độ nhạy :: = tên tín hiệu [ , tên tín hiệu]


mệnh đề điều kiện :: = until điều kiện
mệnh đề thời gian chờ :: = for biểu diễn thời gian
Danh sách độ nhạy của khai báo đợi chỉ định một tập các tín hiệu mà quá trình
là nhạy cảm với chúngtrong khi nó đang bị dừng. Khi một sự kiện xảy ra trên bất kỳ
tín hiệu nào (tức là giá trị của tín hiệu bị thay đổi), quá trình quay lại trạng thái cũ
và xác định điều kiện. Nếu nó đúng hoặc nếu điều kiện bị bỏ qua, thì việc kích hoạt
quá trình sẽ theo khai báo tiếp theo, nếu không thì quá trình sẽ bị tạm ngừng trở lại.
Nếu mệnh đề độ nhạy bị bỏ qua, thì quá trình sẽ nhạy với tất cả các tín hiệu đã được
đề cập trong biểu diễn điều kiện. Biểu diễn thời gian chờ phải đánh giá khoảng thời
gian thụ động, và chỉ ra thời gian cực đại cho quá trình sẽ đợi nó. Nếu nó bị bỏ qua,
quá trình có thể phải đợi không có thời hạn (thời gian đợi bất định).
Nếu danh sách độ nhạy được đặt trong header của khai báo quá trình, thì sau
khi quá trình có thể nhận được một khai báo đợi ẩn tại điểm cuối cùng của phần
khai báo của nó. Danh sách độ nhạy của khai báo đợi ẩn này tương tự như trong
header của quá trình.
Ví dụ về các khai báo của một quá trình với danh sách độ nhạy:
process (reset, clock)
variable state : bit := false;
begin
if reset then
state := false;
elsif clock = true then
state := not state;
end if;
q <= state after prop_delay; -- implicit wait on reset, clock
end process;
Trong quá trình pha khởi động của quá trình mô phỏng, quá trình được kích
hoạt và chỉ định giá trị khởi động của trạng thái tới tín hiệu q. Sau đó nó tạm dừng
Tiếp cận lập trình cho FPGA từ Spartan -3 97

tại khai báo đợi ẩn chỉ định trong phần chú giải. Khi giá trị reset hoặc clock thay đổi
giá trị, thì quá trình sẽ hoạt động trở lại, và kích hoạt lặp lại từ đầu.
Ví dụ tiếp theo mô tả hoạt động của một thiết bị đồng bộ được gọi là Muller-C
sử dụng để xây dựng phần tử logic không đồng bộ. Đầu ra của thiết bị bắt đầu từ giá
trị “0”, tại thời điểm đầu ra thay đổi giá trị lên “1”. Đầu ra sẽ dừng lại ở giá trị “1”
đến khi cả hai đầu vào đều la “0”, tại thời điểm đầu ra thay đổi về “0”.
Ví dụ:
muller_c_2 : process
begin
wait until a = '1' and b = '1';
q <= '1';
wait until a = '0' and b = '0';
q <= '0';
end process muller_c_2 ;
Quá trình này không bao gồm danh sách độ nhạy, vì vậy các khai báo đợi hiện
được dùng để điều khiển tạm dừng và kích hoạt một quá trình. Trong cả hai khai
báo đợi, danh sách độ nhạy là tập tín hiệu a và b, được xác định từ biểu diễn điều
kiện.
4.4.3. Khai báo chỉ định tín hiệu tương tranh
Thường thì một quá trình mô tả một bộ điều khiển cho một tín hiệu chứa chỉ
một khai báo chỉ định tín hiệu. VHDL cung cấp một cách biểu diễn một cách ngắn
gọn, được gọi là khai báo chỉ định tín hiệu tương tranh, dùng để biểu diễn cho quá
trình đó. Cú pháp là:
khai báo chỉ định tín hiệu tương tranh :: =
[ nhãn : ] chỉ định tín hiệu điều kiện
| [ nhãn : ] chỉ định tín hiệu được chọn
Đối với mỗi loại chỉ định tín hiệu tương tranh, có một khai báo quá trình
tương ứng với nghĩa của nó.
Tiếp cận lập trình cho FPGA từ Spartan -3 98

4.4.3.1. Chỉ định tín hiệu điều kiện


Một chỉ định tín hiệu điều kiện là một chỉ định ngắn cho một quá trình có chứa
các chỉ định tín hiệu trong một khai báo “if”. Cú pháp là:
chỉ định tín hiệu điều kiện :: = đích <= các tuỳ chọn các dạng sóng điều kiện;
các tuỳ chọn :: = [ guarded ] [ transport ]
dạng sóng điều kiện :: =
{ dạng sóng when điều kiện else }
dạng sóng
Việc sử dụng từ guarded không được đề cập đến trong mục này. Nếu từ
transport có đi kèm, thì các chỉ định tín hiệu trong quá trình tương đương sử dụng
trễ transport.
Giả sử chúng ta có một chỉ định tín hiệu điều kiện:
s <= dạng_sóng_1 when điều_kiện_1 else
dạng_sóng_2 when điều_kiện_2 else
...
dạng_sóng_n;
thì quá trình tương đương là:
process
if điều_kiện_1 then
s <= dạng_sóng_1;
elsif điều_kiện_2 then
s <= dạng_sóng_2;
elsif ...
...
else
s <= dạng_sóng_n;
wait [ mệnh đề độ nhạy ];
end process;
Tiếp cận lập trình cho FPGA từ Spartan -3 99

Nếu không có các biểu thức giá trị dạng sóng hoặc điều kiện chứa một tham
chiếu đến một tín hiệu nào, thì khai báo đợi ở cuối của quá trình tương đương sẽ
không nhận được mệnh đề độ nhạy. Điều này có nghĩa là sau khi việc chỉ định được
thực hiện, thì quá trình sẽ tạm dừng vô hạn. Ví dụ, việc chỉ định tín hiệu:
reset <= ‘1’, ‘0’ after 10 ns when một_xung_ngắn_được_yêu_cầu else
‘1’, ‘0’ after 50 ns;
thực hiện hai giao tác trên tín hiệu reset, sau đó tạm dừng chờ báo nghỉ của
chương trình mô phỏng.
Mặt khác, nếu chúng tham chiếu đến các tín hiệu trong các biểu thức giá trị
dạng sóng hoặc điều kiện, thì khai báo đợi nhận được danh sách độ nhạy bao gồm
tất cả các tín hiệu đã được tham chiếu đến. Do đó việc chỉ định điều kiện:
mux_out <= ‘Z’ after Tpd when en = ‘0’ else
in_0 after Tpd when sel = ‘0’ else
in_1 after Tpd;
là độ nhạy đến các tín hiệu en và sel. Quá trình được kích hoạt trong thời gian
pha khởi động, và sau đó là mỗi khi en hoặc sel thay đổi giá trị.
Trường hợp phát sinh lại một chỉ định tín hiệu điều kiện, thì bao gồm không
có các thành phần điều kiện, tương đương với một quá trình chỉ có chứa một khai
báo chỉ định tín hiệu. Vì vậy:
s <= wavefom;
tương đương với:
process
s <= waveform;
wait [ mệnh đề độ nhạy ];
end process;
4.4.3.2. Chỉ định tín hiệu được chọn
Một khai báo chỉ định tín hiệu được chọn là một chỉ định ngắn gọn cho một
quá trình có chứa các chỉ định tín hiệu trong một khai báo “case”. Cú pháp của nó
là:
Tiếp cận lập trình cho FPGA từ Spartan -3 100

chỉ định tín hiệu được chọn :: =


with biểu thức select
đích <= các tuỳ chọn các dạng sóng được chọn;
các dạng sóng được chọn :: =
{ dạng sóng when chọn }
dạng sóng when chọn
chọn :: = chọn { | chọn }
Các thành phần tuỳ chọn tương tự như đối với một chỉ định tín hiệu điều kiện.
Do vậy nếu từ transport đi kèm, thì việc chỉ định tín hiệu trong quá trình tương
đương sử dụng trễ transport.
Giả sử chúng ta có một chỉ định tín hiệu được chọn:
with biểu thức select
s <= dạng_sóng_1 when danh_sách_chọn_1,
dạng_sóng_2 when danh_sách_chọn_2,
...
dạng_sóng_n when danh_sách_chọn_n;
Thì quá trình tương đương là:
process
case biểu thức is
when danh_sách_chọn_1 =>
s <= dạng_sóng_1;
when danh_sách_chọn_2 =>
s <= dạng_sóng_2;
...
when danh_sách_chọn_n =>
s <= dạng_sóng_n;
end case;
wait [ mệnh đề độ nhạy ];
end process;
Tiếp cận lập trình cho FPGA từ Spartan -3 101

Danh sách độ nhạy đối với khai báo đợi được xác định cùng một cách như đối
với một chỉ định tín hiệu điều kiện. Đó là, nếu không có các tín hiệu được tham
chiếu đến trong chỉ định tín hiệu được chọn hoặc không có các dạng sóng, thì khai
báo đợi không có mệnh đề độ nhạy. Ngược lại mệnh đề độ nhạy chứa tất cả các tín
hiệu được tham chiếu đến trong biểu thức và các dạng sóng.
Ví dụ về một khai báo chỉ định tín hiệu được chọn:
with alu_function select
alu_result <= op1 + op2 when alu_add | alu_incr,
op1 – op2 when alu_subtract,
op1 and op2 when alu_and,
op1 or op2 when alu_or,
op1 and not op2 when alu_mask;
Trong ví dụ trên, giá trị của tín hiệu alu_function được sử dụng để chọn một
chỉ định tín hiệu đến alu_result để kích hoạt. Việc khai báo là độ nhạy đến tín hiệu
alu_function, op1 và op2, do vậy mỗi khi có bất kỳ sự thay đổi giá trị nào, thì việc
chỉ định tín hiệu sẽ được kích hoạt trở lại.
4.5. Mô hình tổ chức
Trong phần trước chúng ta đã đề cập đến việc mô tả hoạt động VHDL một
cách độc lập. Mục đích của phần này là thể hiện các thức làm thế nào để kết hợp tất
cả các thành phần hoạt động lại thành một hệ thống số theo mô tả VHDL hoàn
chỉnh.
3.5.1. Các đơn vị và thư viện thiết kế
Khi chúng ta viết mô tả các hoạt động VHDL, chúng ta phải ghi lại dạng các
tệp thiết kế (design file), sau đó dùng một trình biên dịch để phân tích cú pháp của
chúng và đưa chúng vào thành lập một thư viện thiết kế (Design Library). Một số
các cấu trúc của VHDL có thể được phân tích riêng rẽ để đưa vào thư viện thiết kế.
Các cấu trúc đó được gọi là các đơn vị thư viện (Library Units). Các đơn vị thư viện
chính (primary) bao gồm các mô tả đầu vào, các mô tả đóng gói thành phần chính
và các mô tả cấu hình (xem tiếp trong mục 3.5.2). Các đơn vị thư viện phụ
Tiếp cận lập trình cho FPGA từ Spartan -3 102

(secondary) bao gồm các thân chương trình kiến trúc và các thân của các đóng gói
thành phần chính. Các đơn vị thư viện đó phụ thuộc vào đặc điểm giao diện của
chúng trong các đơn vị thư viện chính tương ứng, vì đơn vị chính phải được phân
tích trước bất kỳ đơn vị phụ nào tương ứng.
Một tệp thiết kế có thể chứa một số đơn vị thư viện. Cấu trúc của một tệp thiết
kế có thể được chỉ định như trong cú pháp sau:
tệp thiết kế :: = đơn vị thiết kế {đơn vị thiết kế}
đơn vị thiết kế :: = mệnh đề ngữ cảnh đơn vị thư viện
mệnh đề ngữ cảnh :: = {thành phần ngữ cảnh}
thành phần ngữ cảnh :: = mệnh đề thư viện | mệnh đề sử dụng
mệnh đề thư viện :: = library danh sách tên logic;
danh sách tên logic :: = tên logic { , tên logic}
đơn vị thư viện :: = đơn vị chính | đơn vị phụ
đơn vị chính :: = mô tả đầu vào | mô tả cấu hình | mô tả đóng gói
đơn vị phụ :: = thân kiến trúc | thân đóng gói
Các thư viện được tham chiếu để sử dụng các định danh được gọi là các tên
logic (logic name). Tên này phải được dịch bởi hệ điều hành chủ thành một tên lưu
trữ hoạt động độc lập. Ví dụ, các thư viện thiết kế có thể hoạt động như các tệp cơ
sở dữ liệu (database file), và tên logic phải được dùng để có thể xác định tên tệp cơ
sở dữ liệu. Các đơn vị thư viện trong thư viện đã được cung cấp có thể tham chiếu
đến thông qua hậu tố tên của chúng với tên logic thư viện. Để ví dụ, chúng ta có tên
tệp là ttl_lib.ttl_10 phải tham chiếu đến đơn vị ttl_10 trong thư viện ttl_lib.
Mệnh đề ngữ cảnh có trước mỗi đơn vị thư viện chỉ định các thư viện nào
khác nó sẽ tham chiếu đến và các dạng đóng gói nào nó sử dụng. Phạm vi của các
tên có thể nhận thấy được thông qua mệnh đề ngữ cảnh kéo dài đến hết đơn vị thiết
kế.
Có hai loại thư viện đặc biệt mà hoàn toàn có thể sử dụng được cho tất cả các
đơn vị thiết kế, và không cần đặt tên trong mệnh đề thư viện. Thư viện đầu tiên có
tên là work, nó tham chiếu thư viện thiết kế đang làm việc vào đơn vị thiết kế hiện
Tiếp cận lập trình cho FPGA từ Spartan -3 103

tại sẽ được đặt dành cho người phân tích. Sau khi đặt vào đơn vị thiết kế, các đơn vị
thiết kế được phân tích trước đó trong tệp thiết kế có thể tham chiếu để sử dụng tên
thư viện có sẵn đó là work.
Thư viện đặc biệt thứ hai được gọi là std, và nó chứa các dạng đóng gói
standard và textio. Standard bao gồm tất cả các kiểu định nghĩa sẵn và các hàm
chức năng. Tất cả các thành phần con trong các đóng gói đó đều có thể sử dụng
được, và không cần sử dụng các mệnh đề để truy xuất chúng.
4.5.2. Các cấu hình
Trong mục 4.3.2.3 và 4.3.2.4 chúng ta đã biểu diễn làm thế nào một mô tả hoạt
động khai báo đặc điểm của một thành phần và cách tạo các sự kiện của thành phần.
Chúng ta cũng đề cập rằng một thành phần đã được khai báo có thể được coi như là
một mẫu cho một thực thể thiết kế. Sự ràng buộc của một thực thể tới mẫu này đạt
được thông qua một mô tả cấu hình. Mô tả này có thể được sử dụng để chỉ định các
hằng số chung trong thực tế cho các thành phần và khối. Như vậy mô tả cấu hình
(configuration declaration) đóng vai trò chính trong việc tổ chức mô tả thiết kế khi
chuẩn bị cho việc mô phỏng hoặc các xử lý khác.
Cú pháp của một mô tả cấu hình là:
Mô tả cấu hình :: =
Configuration tên định danh of tên thực thể is
các phần mô tả cấu hình
cấu hình của khối
end [tên đơn giản của cấu hình];
các phần mô tả cấu hình :: = {các mục mô tả cấu hình}
các mục mô tả cấu hình ::= mệnh đề sử dụng
cấu hình của khối :=
for đặc điểm của khối
{mệnh đề sử dụng}
{các mục cấu hình}
end for;
Tiếp cận lập trình cho FPGA từ Spartan -3 104

đặc điểm của khối ::= tên kiến trúc | nhãn khai báo của khối
các mục cấu hình ::= cấu hình của khối | cấu hình của thành phần
cấu hình của thành phần ::=
for đặc điểm của thành phần
{use chỉ số ràng buộc}
{cấu hình của khối}
end for;
đặc điểm của thành phần ::= liệt kê thuyết minh : tên thành phần
liệt kê thuyết minh ::=
nhãn thuyết minh { , nhãn thuyết minh}
| others
| all
chỉ số ràng buộc ::=
vị trí của thực thể
[ vị trí ánh xạ chung ]
[ vị trí ánh xạ cổng ]
vị trí của thực thể ::=
entity tên thực thể [( tên định danh kiến trúc )]
| configuration tên cấu hình
| open
vị trí ánh xạ chung ::= generic map ( liệt kê kết hợp chung )
vị trí ánh xạ cổng ::= port map ( liệt kê kết hợp cổng )
Phần miêu tả của mô tả cấu hình cho phép cấu hình sử dụng các mục từ thư
viện và các dạng đóng gói. Cấu hình của khối ngoài cùng trong mô tả cấu hình định
nghĩa cấu hình cho một kiến trúc của thực thể đã được đặt tên. Ví dụ, trong phần
4.3, chúng ta đã có ví dụ về một thực thể processor và kiến trúc của nó được cho
trong ví dụ dưới đây:
Ví dụ về mô tả một thực thể processor và thân kiến trúc
entity processor is
Tiếp cận lập trình cho FPGA từ Spartan -3 105

generic (max_clock_speed : frequency := 30 MHz);


port ( port list );
end processor;
architecture block_structure of processor is
declarations
begin
control_unit : block
port ( port list );
port map ( association list );
declarations for control_unit
begin
statements for control_unit
end block control_unit;
data_path : block
port ( port list );
port map ( association list );
declarations for data_path
begin
statements for data_path
end block data_path;
end block_structure;
Toàn bộ cấu trúc của mô tả cấu hình đối với kiến trúc trên như sau:
configuration test_config of processor is
use work.processor_types.all
for block_structure
configuration items
end for;
end test_config;
Tiếp cận lập trình cho FPGA từ Spartan -3 106

Trong ví dụ này, nội dung của đóng gói được gọi là processor_types trong thư
viện đang làm việc hiện tại là có thực, và cấu hình của khối tham chiếu đến kiến
trúc block_structure của thực thể processor.
Cùng với cấu hình của khối đối với kiến trúc, các mô-dul con của kiến trúc
cũng được đặt cấu hình. Các mô-dul con này bao g ồm các khối và các sự kiện thành
phần. Ví dụ, các khối trong kiến trúc ở hình 5.1 trên có thể được đặt cấu hình như
trong ví dụ sau.
Ví dụ về cấu hình của một processor:
configuration test_config of processor is
use work.processor_types.all
for block_structure
for control_unit
configuration items
end for;
for data_path
configuration items
end for;
end for;
end test_config;
Trong một mô-dul con là một sự kiện của thành phần, một cấu hình thành
phần được dùng để ràng buộc một thực thể với sự kiện thành phần. Để phân tích rõ,
giả sử khối data_path trong ví dụ trên bao gồm một sự kiện thành phần là alu, được
mô tả như trong ví dụ dưới đây.
Ví dụ về cấu trúc của khối data_path trong processor:
data_path : block
port ( port list );
port map ( association list );
component alu
port (function : in alu_function;
Tiếp cận lập trình cho FPGA từ Spartan -3 107

op1, op2 : in bit_vector_32;


result : out bit_vector_32);
end component;
other declarations for data_path
begin
data_alu : alu
port map (function => alu_fn, op1 => b1, op2 => b2, result =>
alu_r);
other statements for data_path
end block data_path;
Giả sử rằng một thư viện project_cells chứa một thực thể được gọi là alu mà
được định nghĩa như sau:
entity alu_cell is
generic (width : positive);
port (function_code : in alu_function;
operand1, operand2 : in bit_vector(width-1 downto 0);
result : out bit_vector(width-1 downto 0);
flags : out alu_flags);
end alu_cell;
với một kiến trúc được gọi là behaviour. Thực thể này phù hợp với thành phần
alu mẫu, do toán hạng của nó và các cổng result có thể bị bắt buộc phù hợp với
thành phần trên, và các cổng flags có thể không được nối. Cấu hình của khối đối với
data_path có thể biểu diễn trong ví dụ dưới đây.
Ví dụ về cấu hình của khối sử dụng thư viện thực thể:
for data_path
for data_alu : alu
use entity project_cells.alu_cell(behaviour)
generic map (width => 32)
Tiếp cận lập trình cho FPGA từ Spartan -3 108

port map (function_code => function, operand1 => op1, operand2 =>
op2,
result => result, flags => open);
end for;
other configuration items
end for;
Ngược lại, nếu thư viện cũng cung cấp một cấu hình được gọi là alu_struct đối
với kiến trúc structure của thực thể alu_cell, thì cấu hình khối có thể sử dụng như
trong ví dụ dưới đây.
Ví dụ về cấu hình của khối sử dụng một dạng cấu hình khác
for data_path
for data_alu : alu
use configuration project_cells.alu_struct
generic map (width => 32)
port map (function_code => function, operand1 => op1, operand2 =>
op2,
result => result, flags => open);
end for;
other configuration items
end for;
4.5.3. Một ví dụ cụ thể
Để phân tích toàn bộ cấu trúc của một mô tả thiết kế, chúng ta lấy ví dụ trong
phần 3.1.4., đó là một bộ count (bộ đếm).
Toàn bộ bộ đếm này được mô tả như trong đoạn mã VHDL dưới đây. Tệp
thiết kế bao gồm một số đơn vị thiết kế.
Đơn vị thiết kế đầu tiên là mô tả thực thể của count. tiếp theo là hai đơn vị
phụ, kiến trúc của thực thể count. Chú ý là thực thể count được tham chiếu đến cấu
hình theo work.count, sử dụng trong tên thư viện.
-- khối chính: mô tả thực thể count
Tiếp cận lập trình cho FPGA từ Spartan -3 109

entity count is
generic (prop_delay : Time := 10 ns);
port (clock : in bit;
q1, q0 : out bit);
end count;
-- khối thứ 2: mô tả thân kiến trúc hoạt động của count
architecture behaviour of count is
begin
count_up: process (clock)
variable count_value : natural := 0;
begin
if clock = '1' then
count_value := (count_value + 1) mod 4;
q0 <= bit'val(count_value mod 2) after prop_delay;
q1 <= bit'val(count_value / 2) after prop_delay;
end if;
end process count_up;
end behaviour;
-- khối thứ 2: mô tả thân kiến trúc cấu trúc của count
architecture structure of count is
component t_flipflop
port (ck : in bit; q : out bit);
end component;
component inverter
port (a : in bit; y : out bit);
end component;
signal ff0, ff1, inv_ff0 : bit;
begin
bit_0 : t_flipflop port map (ck => clock, q => ff0);
Tiếp cận lập trình cho FPGA từ Spartan -3 110

inv : inverter port map (a => ff0, y => inv_ff0);


bit_1 : t_flipflop port map (ck => inv_ff0, q => ff1);
q0 <= ff0;
q1 <= ff1;
end structure;
-- khối chính: mô tả thực thể mạch kiểm tra count: test_count
entity test_count is
end test_count;
-- khối thứ 2: mô tả thân kiến trúc hoạt động của test_count
architecture structure of test_count is
signal clock, q0, q1 : bit;
component count
port (clock : in bit;
q1, q0 : out bit);
end component;
begin
counter : count
port map (clock => clock, q0 => q0, q1 => q1);
clock_driver : process
begin
clock <= '0', '1' after 50 ns;
wait for 100 ns;
end process clock_driver;
end structure;
-- khối chính: cấu hình sử dụng kiến trúc hoạt động
configuration test_count_behaviour of test_count is
for structure -- of test_count
for counter : count
use entity work.count(behaviour);
Tiếp cận lập trình cho FPGA từ Spartan -3 111

end for;
end for;
end test_count_behaviour;
-- khối chính: cấu hình sử dụng kiến trúc cấu trúc
library misc;
configuration test_count_structure of test_count is
for structure -- of test_count
for counter : count
use entity work.count(structure);
for structure -- of count_2
for all : t_flipflop
use entity misc.t_flipflop(behaviour);
end for;
for all : inverter
use entity misc.inverter(behaviour);
end for;
end for;
end for;
end for;
end test_count_structure;
Cuối cùng là việc mô tả cấu hình đối với mạch kiểm tra. Nó sử dụng hai đơn
vị thư viện từ một thư viện tham chiếu phân tách là misc. Các thư viện đơn vị từ thư
viện này được tham chiếu đến cấu hình theo misc.t_flipflop và misc.inverter.
Tiếp cận lập trình cho FPGA từ Spartan -3 112

4.6. Nguồn tham khảo về VHDL


- IEEE 1029.1-1991. IEEE Standard for Waveform and Vector Exchange
(WAVES). IEEE Std 1029.1-1991. The Institute of Electrical and Electronics
Engineers, Inc., New York.
- IEEE 1076-1993. IEEE Standard VHDL Language Reference Manual
(ANSI). IEEE Std. 1076-1993. The Institute of Electrical and Electronics Engineers,
Inc., New York.
- IEEE 1076.2-1996. Standard VHDL Language Mathematical Packages.
IEEE Ref. AD129-NYF. Approved by IEEE Standards Board on 19 September
1996. [p. 404].
- ISO 8859-1. 1987 (E). Information Processing--8-bit single-byte coded
graphic character sets--Part 1: Latin Alphabet No. 1. American National Standards
Institute, Hackensack, NJ; 1987.
-Asic lập trình được ( tập 1) và tập II của tác giả Tống Văn On.
- Ngoài ra chúng ta có thể tìm kiếm các tài liệu hướng dẫn về VHDL ở trên
mạng.
Tiếp cận lập trình cho FPGA từ Spartan -3 113

Chương V: Thiết kế và giao tiếp.

Chương này sẽ giới thiệu một số bài toán thiết kế thực hiện trên bo mạch spartan -3
starter kid board. Các bài tập theo hướng từ dễ đến khó để chúng ta dễ dàng tiếp cận
cách thức lập trình VHDL, cách thức tương tác giữa phần cứng và phần mềm, hoàn
chỉnh sản phẩm….Lưu ý rằng tất cả các bài toán này đều được ta kiểm tra kỹ lưỡng
trên bo mạch và đã hoạt động đúng theo yêu cầu thiết kế.
I.Bài 1: Mục đích của bài toán này nhằm giúp chúng ta làm quen với việc vào ra
số liệu và kiểm tra việc thực hiện trên mạch xem có đáp ứng đúng yêu cầu đặt ra
hay không.
Ø Bài toán như sau: Như đã giới thiệu ở các phần trước. Trên board
spartan – 3 có tất cả 8 led. Nhiệm vụ của chúng ta là tạo ra 4 trạng thái
chuyển led khác nhau.
Ø Phân tích bài toán : Chúng ta cần tạo ra 4 trạng thái nghĩa là tạo ra
bốn trường hợp khác nhau để chuyển led. Như vậy chỉ cần 2 bít điều
khiển là đủ ( 22 = 4 ).
Ø Quá trình thực hiện : Đầu tiên chúng ta tạo ra một đối tượng thiết kế
mới như hướng dẫn ở phần trước ( chương III). Sau khi đã tạo xong đề
án chúng ta thực hiện viết lệnh. Với bài toán như trên chương trình của
chúng ta như sau:
------------------------------------------------------------------------------
Tacgia : Tran Duc Thien.
-- Create Date: 13:59:30 08/18/06
-- Module Name: chuyenled - Behavioral
-- Project Name: Chuyen led
Target Device: Spartan -3 starter kid board
------------------------------------------------------------------------------
Tiếp cận lập trình cho FPGA từ Spartan -3 114

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity led is
port (chuyen_led:in std_logic_vector(1 downto 0); --cổng vào
led_out: out std_logic_vector(7 downto 0)); --cổng ra.
end led;
architecture Behavioral of led is
signal led_in :std_logic_vector(7 downto 0);--Tín hiệu dùng để kiểm tra.
begin
process(led_in,chuyen_led) –danh sách độ nhạy.
begin
led_in <= “10000000” --Bít thứ tám bằng một
case chuyen_led is
When "00" =>--Trang thai ra giong voi trang thai vao
led_out <= led_in;
When "01" =>--Dich trai voi '0'
led_out <= led_in(6 downto 0)& '0';
When "10" =>--Dich phai voi '0'
led_out <= '0'& led_in(7 downto 1);
When others =>--Quay phai
led_out <= led_in(0)&led_in(7 downto 1);
end case;
end process;
end Behavioral;

Ø Bước tiếp theo là chúng ta kiểm tra lỗi và xem kết cấu logic của
mạch đảm nhiệm chức năng như chúng ta vừa thiết kế. Ở bước này
Tiếp cận lập trình cho FPGA từ Spartan -3 115

thông thường ta gộp lại làm một là kiểm tra kết cấu logic của mạch
vì trong bước này đã bao gồm quá trình kiểm tra lỗi.

Kích chuột vào led-behavioral ( chính là file


chuyenled.vhdl).
Bước tiếp theo là kích đúp vào : View
RTL schematic để vào xem kết cấu logic
mạch chức năng tương ứng với cấu trúc lệnh
mà ta vừa viết ở trên.Khi đó ta có sơ đồ RTL
mạch theo mức thiết kế trên như sau:

Ø Tiếp theo là thực hiện gán chân linh kiện. Để gán đúng chân linh
kiện ta cần phải căn cứ vào danh sách các chi tiết kết nối trong
phần Gui của Spartan -3. Sau đây xin giới thiệu danh sách kết nối
của 8 chuyển mạch đầu vào như sau :
Switch SW7 SW6 SW5 SW4 SW3 SW2 SW1 SW0
Pin K13 K14 J13 J14 H13 H14 G12 F12

Ở bài này ta dùng SW0 và SW1 để làm biến chuyển trạng thái.Tương ứng ta
có 8 led_out được kết nối theo bảng dưới đây:
Tiếp cận lập trình cho FPGA từ Spartan -3 116

Led LD7 LD6 LD5 LD4 LD3 LD2 LD1 LD0


Pin P11 P12 N12 P13 N14 L12 P14 K12

.
Bây giờ ta tiến hành kết nối
theo đúng thứ tự đã cho ở bảng
trên. Ta kích vào
Userconstrains à Create Area
Constraints ( cũng có thể ta
chọn Assign Package Pins) .

Khi đó trình dịch sẽ kiểm tra xem có lỗi cú pháp hoặc chính tả hay không. Mặt khác
nó cũng phân tích mức logic và tối ưu hoá cách kết nối sao cho đạt hiệu quả tốt
nhất.
I/O name là tên của các cổng
vào ra mà ta đã định nghĩa ở
phần trên.
Loc : Các chân FPGA ins
tương ứng với các cổng vào ra
theo bảng kết nối đã giới thiệu
ở trên. Lưu ý là gán xong một
chân thì Enter để chấp nhận.

Ø Bước tiếp theo ta tiến hành nạp để kiểm tra. Có hai cách nạp là nạp
trực tiếp vào Rom hoặc cách thứ hai là nạp vào JATG để kiểm tra.
Trước tiên ta trình bày cách nạp theo kiểu kiểm tra.
Tiếp cận lập trình cho FPGA từ Spartan -3 117

Kích vào Generate Programming File


à Configure Device. Khi đó trình dịch sẽ
chạy kiểm tra lại một lần nữa bao gồm cú
pháp, tổng hợp logic, mức độ hợp lý, phân
tích tối ưu hoá cách đi dây, kiểm tra việc
gán chân có phù hợp với loại Kid mà ta
khai báo hay không. Sau khi kiểm tra mọi
thứ đều thoả mãn lúc đó trình dịch sẽ tạo
ra một file .bit và gửi ra thông báo. Lúc đó tốt nhất chúng ta cứ theo lựa chọn mặc
định của trình dịch. Sau khi kết thúc việc xác nhận phương thức nạp lúc đó trình
dịch sẽ tự động kiểm tra việc kết nối giữa máy tính và bo mạch của chúng ta. Khi đã
xác định đã nhận định được việc kết nối và đường truyền đã thông, trình dịch gửi
thông báo tìm thấy phần cứng đã được kết nối và sau đó nó sẽ tự động kết nối để
sẵn sàng cho việc truyền dữ liệu từ máy tính sang.

Để tiến hành nạp, kích chuột phải vào Xc3s200 device, chọn Getdevice sau đó add
file led.bit vào. Xong thủ tục này ta kích tiếp chuột phải một lần nữa, chọn Progam

Có thế
chọn verify
cũng được
hoặc bỏ qua
Tiếp cận lập trình cho FPGA từ Spartan -3 118

cũng không sao. Nhấn OK để chấp nhận

Sau khi nhấn OK khi đó trên màn


hình sẽ hiển thị trạng thái truyền dữ
liệu từ máy tính sang. Toàn bộ cấu
hình cũng như cách thức xử lý mà ta
đã thực hiện trong phần lập trình sẽ
được tải sang kid.
Sau khi thực hiện truyền tải thành công sẽ có thông báo màu xanh “ programming
succeeded”:

Vấn đề tiếp theo là kiểm tra lại hoạt động của kid có thoả mãn những dự định thiết
kế ban đầu hay không. Nếu như vẫn chưa thoả mãn ta phải thực hiện kiểm tra lại
phần chương trình, kiểm tra lại thuật toán. Khi tiến hành thay đổi ở khâu bất kì nào
ta đều phải lưu lại. Các bước tiếp theo tiến hành theo trình tự đã hướng dẫn ở trên
đến khi đạt được đúng ý định thiết kế.
Tiếp cận lập trình cho FPGA từ Spartan -3 119

II. Bài số hai.


Ø Mục đích : Đây là một bài toán tổng hợp bao gồm thực hiện việc
chia tần số và làm quen với cách thức hiển thị lên dàn led 7 đoạn.
Sau khi hoàn thành được bài toán này chúng ta có thể thực hiện
được rất nhiều bài toán đo đạc.
Ø Nội dung bài toán : Trên cơ sở 4 led 7 đoạn có sẵn trên Spartan -3
tiến hành thiết kế một đồng hồ số hiển thị phút và giây.
Ø Phân tích : Thực tế ta hoàn toàn có thể thiết kế một đồng hồ hoàn
chỉnh hiển thị đầy đủ giờ phút giây. Trong khuôn khổ bài toán này
ta sử dụng ngay bốn led 7 đoạn sẵn có trên Sp3 để thử nghiệm.
Với việc hiển thị phút và giây ta tổ chức như sau. Với 2 led cuối là led 1 và led 2 sẽ
hiển thị giây. Như vậy led 1 và led 3 hiển thị các số từ 0 đến 9, led 2 và led 4 sẽ hiên
thị các số đếm từ 0 đến 5. Như vậy khi viết chương trình ta cần tổ chức việc chọn
led cho phù hợp, với cách thức thể hiện như vậy ta sẽ chọn led 1 đếm tối đa đến 9
sau đó về không, khi led 1 đếm đến 9 thì tương ứng led2 sẽ đếm lên một.Khi 2 led
cuối đếm đến 59 sẽ được được reset về 0, và lúc đó led thứ 3 sẽ được tăng lên một.
Khi led thứ 3 tăng đến 9 thì led số 4 được tăng lên 1. Và kết quả khi là 59.59 thì các
led sẽ trở về 00.00 và thực hiện đếm lại từ đầu.
Bộ đếm lên sẽ thực hiện đếm sau mỗi giây một. Một giây đồng hồ tương ứng
với tần số là 1 hz. Trên Sp3 tần số thạch anh có tần số dao động ổn định là 50 Mhz,
vì vậy ta phải thực hiện chia tần số tạo ra tần số 1hz. Cách thức chia tần số thực
hiện như sau:
Đầu tiên ta tạo ra tần số 1Mhz bằng cách chia tần số 50 Mhz cho 50. Nghĩa
là cứ có 50 xung clock 50Mhz thì cho ra một xung clock 1Mhz. Tương tự như vậy
ta tạo ra tần số 1Khz từ tần số 1Mhz. Nghĩa là cứ có 1000 xung clock 1Mhz thì cho
Tiếp cận lập trình cho FPGA từ Spartan -3 120

ra 1 xung 1Khz. Từ tần số 1Khz ta tạo ra tần số 1 Hz cũng tương tự. Như vậy, trong
chương trình ta chỉ cần tổ chức các bộ đếm tương ứng để tạo các clock đầu ra phù
hợp.
Các led được nối theo kiểu Anod chung. Việc chọn led nào sáng được thực
hiện bởi 4 bít AN3 đến AN0, chúng ta có thể thấy việc điều khiển này thông qua 4
bít digit( 3 downto 0) ở phần chương trình. Cụ thể việc chọn led theo bảng sau:
Digit <= “1110” --Chọn led 1 ( bên phải cùng)
Digit <= “1101” --Chọn led 2;
Digit <= “1011” --Chọn led 3;
Digit <= “0111” --Chọn led 4; Do nối Anod chung nên sử dụng mức logic thấp.
Việc hiển số lên led 7 đoạn dựa trên việc giải mã BCD sang 7 đoạn. Cụ thể như sau
when "0000" => seg <= "0000001" & dp ; --số 0
when "0001" => seg <= "1001111" & dp ; --số 1
when "0010" => seg <= "0010010" & dp ; --số 2
when "0011" => seg <= "0000110" & dp ; --số 3
when "0100" => seg <= "1001100" & dp ; --số 4
when "0101" => seg <= "0100100" & dp ; --số 5
when "0110" => seg <= "0100000" & dp ; --số 6
when "0111" => seg <= "0001111" & dp ; --số 7
when "1000" => seg <= "0000000" & dp ; --số 8
when others => seg <= "0000100" & dp ; --số 9
Tiếp cận lập trình cho FPGA từ Spartan -3 121

Hình 5.1: Điều khiển chọn led và hiển thị trên Led 7 đoạn

Từ việc phân tích trên ta thực hiện việc viết chương trình như sau:
-- File name : clock.vhd
-------------------Đồng hồ số hiển thị phút giây---------------------------------
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all ;
library unisim ;
use unisim.vcomponents.all ;
entity clock is port (
clk50in : in std_logic ; --Đầu vào 50Mhz
pb_in : in std_logic_vector(3 downto 0); -- 4 Nút ấn.
sw_in : in std_logic_vector(7 downto 0); -- 8 chuyển mạch đầu vào
digit_out :out std_logic_vector(3 downto 0);--Tương ứng là (AN0..AN3)
led_out : out std_logic_vector(7 downto 0);-- 8 LEDs
seg_out :out std_logic_vector(7 downto 0));--Led 7 thanh và d ấu chấm
end clock ;

architecture arch_clock of clock is


Tiếp cận lập trình cho FPGA từ Spartan -3 122

--(Lưu ý trong bài này chúng ta có sử dụng một số thành phần đã có sẵn trong
thư viện của ISE. Nhằm muốn chúng ta ôn lại các lệnh Component ở trong VHDL,
thấy được việc ánh xạ thành cổng ra sao. Như đã thấy trong mô tả ở phía dưới sử
dụng thành phần Ibuf_lvcmoss33, thực tế nó chỉ là thành phần mô tả các cổng vào
ra khi có xung đồng bộ)
component ibuf_lvcmos33 port (i : in std_logic; o : out std_logic);
end component;
component ibufg_lvcmos33 port(i : in std_logic; o : out std_logic);
end component;
component obuf_lvcmos33 port(i : in std_logic; o : out std_logic);
end component;
component bufg port(i : in std_logic; o : out std_logic);
end component;
--Khai báo các tín hiệu sử dụng.
signal low : std_logic ; -- logic '0'
signal high : std_logic ; -- logic '1'
signal rst : std_logic ; -- logic '1'
signal clk50int : std_logic ; --
signal clk50 : std_logic ; --
signal pb : std_logic_vector(3 downto 0) ; --
signal sw : std_logic_vector(7 downto 0) ; --
signal led : std_logic_vector(7 downto 0) ; --
signal digit : std_logic_vector(3 downto 0) ; --
signal seg : std_logic_vector(7 downto 0) ; --
signal mhertz_count : std_logic_vector(5 downto 0) ; --
signal khertz_count : std_logic_vector(9 downto 0) ; --
signal hertz_count : std_logic_vector(9 downto 0) ; --
signal mhertz_en : std_logic ; --
signal khertz_en : std_logic ; --
Tiếp cận lập trình cho FPGA từ Spartan -3 123

signal hertz_en : std_logic ; --


signal bcdint : std_logic_vector(15 downto 0) ; --
signal sốthực : std_logic_vector(3 downto 0) ; --
signal cd : std_logic_vector(2 downto 0) ; --
signal point : std_logic ; --
signal dp : std_logic ; --

begin
low <= '0' ;
high <= '1' ;
rst <= pb(0) ;
clk50in_ibuf : ibufg_lvcmos33 port map(i => clk50in, o => clk50int );
rxclka_bufg : bufg port map(i => clk50int, o => clk50 ) ;
--clk50in à clk50intà clk50. Việc ánh xạ các cổng như trên chính là ta đã
thực hiện việc đồng nhất tín hiệu tạo ra bởi bộ tạo dao động thạch anh clk50in bằng
tín hiệu clk50 mà ta định nghĩa. Khi đó việc xử lý với xung clock clk50in sẽ được
thay thế bằng việc xử lý với tín hiệu mà ta định nghĩa.
--Như đã nói ở trên, mục đích của việc làm này nhằm giúp chúng ta làm quen
với việc ánh xạ cấu hình thành cổng ra sao để phục vụ cho các bài toán lớn. Chúng
ta hình dung nếu như một bài toán bao gồm rất nhiều thực thể ( entity) mà chúng
đều cần sử dụng một tín hiệu hay một thành phần giống nhau. Khi đó thay cho việc
mô tả nhiều lần, chúng ta chỉ cần mô tả trong một thực thể sau đó ánh xạ các thành
phần của thực thể đó dùng cho tất cả các thực thể khác sẽ tiết kiệm được rất nhiều
thời gian.
loop0 : for i in 0 to 3 generate
digit_obuf : obuf_lvcmos33 port map(i => digit(i),o=> digit_out(i));
pb_ibuf : ibuf_lvcmos33 port map(i => pb_in(i), o => pb(i));
end generate ;
Tiếp cận lập trình cho FPGA từ Spartan -3 124

loop1 : for i in 0 to 7 generate


led_obuf : obuf_lvcmos33 port map(i => led(i), o => led_out(i));
digit_obuf : obuf_lvcmos33 port map(i => seg(i),o => seg_out(i));
sw_ibuf : ibuf_lvcmos33 port map(i => sw_in(i), o => sw(i));
end generate ;
process (clk50, rst)
begin
if rst = '1' then
led <= "00000001" ; Mặc định cho led 1 luôn sáng.
point <= '0' ;
dp <= '0' ;
elsif clk50'event and clk50 = '1' then
if hertz_en = '1' then
point <= not point ;
led <= led(6 downto 0) & led(7) ;--Các led sẽ sáng quay vòng lần lượt
sau mỗi giây. Tín hiệu 1hz được tạo ra ở phần dưới.
end if ;
if cd(1 downto 0) = "10" then
dp <= point ;
else
dp <= '1' ; --Nhằm tạo ra dấu chấm ngăn cách phút và giây.
end if ;
end if ;
end process ;
-- tao tin hieu 1 megahec tu tin hieu chuan 50 megahec
process (clk50, rst)
begin
if rst = '1' then
mhertz_count <= (others => '0') ;
Tiếp cận lập trình cho FPGA từ Spartan -3 125

mhertz_en <= '0' ;


elsif clk50'event and clk50 = '1' then
mhertz_count <= mhertz_count + 1 ;
if mhertz_count = "110010" then –chia cho 50 để tạo 1Mhz
mhertz_en <= '1' ;
mhertz_count <= (others => '0') ;
else
mhertz_en <= '0' ;
end if ;
end if ;
end process ;
-- tao tin hieu 1 kilohec tu tin hieu 1 megahec
process (clk50, rst)
begin
if rst = '1' then
khertz_count <= (others => '0') ;
khertz_en <= '0' ;
elsif clk50'event and clk50 = '1' then
if mhertz_en = '1' then
khertz_count <= khertz_count + 1 ;
if khertz_count = "1111101000" then –Chia 1 Mhz cho 1000
khertz_en <= '1' ;
khertz_count <= (others => '0') ;
else
khertz_en <= '0' ;
end if ;
else
khertz_en <= '0' ;
end if ;
Tiếp cận lập trình cho FPGA từ Spartan -3 126

end if ;
end process ;
-- Tao tin hieu 1 hec tu tin hieu 1 kilohec
process (clk50, rst)
begin
if rst = '1' then
hertz_count <= (others => '0') ;
hertz_en <= '0' ;
elsif clk50'event and clk50 = '1' then
if khertz_en = '1' then
hertz_count <= hertz_count + 1 ;
if hertz_count = "1111101000" then –Chia 1 Khz cho 1000
hertz_en <= '1' ;
hertz_count <= (others => '0') ;
else
hertz_en <= '0' ;
end if ;
else
hertz_en <= '0' ;
end if ;
end if ;
end process ;

-- Bat dau dem


process (clk50, rst)
begin
if rst = '1' then
bcdint <= (others => '0') ;
elsif clk50'event and clk50 = '1' then
Tiếp cận lập trình cho FPGA từ Spartan -3 127

if hertz_en = '1' then


--(Đoạn mã này nhằm định ra số đếm tối đa cho các led 7 thanh như đã phân
tích ở trên. Các bcdint ( 3 downto 0), bcdint(7 downto 4) dành cho vi ệc hiển thị
giây; bcdint(11 downto 8) , bcdint (15 downto 12) dành cho vi ệc hiển thị phút. Số
đếm tối đa tạo ra 59:59 ).
if bcdint(3 downto 0) = "1001" then --9
if bcdint(7 downto 4) = "0101" then --5
if bcdint(11 downto 8) = "1001" then --9
if bcdint(15 downto 12) = "0101" then --5
bcdint <= "0000000000000000" ;
else
bcdint <= (bcdint(15 downto 12) + 1) & "000000000000" ;
end if ;
else
bcdint <= bcdint(15 downto 12) & (bcdint(11 downto 8) + 1) & "00000000" ;
end if ;
else
bcdint <= bcdint(15 downto 8) & (bcdint(7 downto 4) + 1) & "0000" ;
end if ;
else
bcdint <= bcdint(15 downto 4) & (bcdint(3 downto 0) + 1) ;
end if ;
end if ;
end if ;
end process ;
--Hien thi phut , giay lên led 7 thanh
process (clk50, rst)
begin
if rst = '1' then
Tiếp cận lập trình cho FPGA từ Spartan -3 128

seg <= (others => '1') ;


digit <= (others => '1') ;
cd <= (others => '0') ;
sốthực <= (others => '0') ;
elsif clk50'event and clk50 = '1' then
cd(2) <= '1' ;
if khertz_en = '1' then --Tạo tốc độ quét led là 1Khz.
cd(1 downto 0) <= cd(1 downto 0) + 1 ;
end if ;
case cd(1 downto 0) is
when "00" => sốthực <= bcdint(3 downto 0) ; digit <= "1110" ;
when "01" => sốthực <= bcdint(7 downto 4) ; digit <= "1101" ;
when "10" => sốthực <= bcdint(11 downto 8) ; digit <= "1011" ;
when others => sốthực <= bcdint(15 downto 12) ; digit <= "0111" ;
end case ;
if cd(2) = '1' then
case sốthực is
when "0000" => seg <= "0000001" & dp ;
when "0001" => seg <= "1001111" & dp ;
when "0010" => seg <= "0010010" & dp ;
when "0011" => seg <= "0000110" & dp ;
when "0100" => seg <= "1001100" & dp ;
when "0101" => seg <= "0100100" & dp ;
when "0110" => seg <= "0100000" & dp ;
when "0111" => seg <= "0001111" & dp ;
when "1000" => seg <= "0000000" & dp ;
when others => seg <= "0000100" & dp ;
end case ;
else
Tiếp cận lập trình cho FPGA từ Spartan -3 129

seg <= (others => '1') ;


end if ;
end if ;
end process ;
end arch_clock;

Sau khi hoàn thành xong việc viết lệnh, chúng ta kiểm tra theo các bước đã trình
bày ở ví dụ một. Sau khi chạy xong phần RTL logic ta có sơ đồ lôgic và việc kết
nối các thành phần mà ta đã mô tả trong phần viết mã ở trên như sau:
Tiếp cận lập trình cho FPGA từ Spartan -3 130
Tiếp cận lập trình cho FPGA từ Spartan -3 131

Khi trình dịch đã đưa ra kết quả mô tả logic truyền thanh ghi (RTL) như trên đồng
nghĩa rằng quá trình viết lệnh đã không có lỗi. Bước tiếp theo là chúng ta thực hiện
việc gán các cổng vào ra tương ứng với danh sách kết nối sau :
Bảng 5.1: FPGA kết nối tới hiển thị led 7 thanh( mức logic thấp)
Segment FPGA Pin
A E14
B G13
C N15
D P15
E R16
F F13
G N16
DP P16

Bảng 5.2 : Các tín hiệu điều khiển việc chọn led
Anod chung Digit3 Digit2 Digit1 Digit0
FPGA Pin E13 F14 G14 D14
Bảng 5.3 : Kết nối với các Switch
Switch SW7 SW6 SW5 SW4 SW3 SW2 SW1 SW0
FPGA K13 K14 J13 J14 H13 H14 G12 F12
Bảng 5.5 : Kết nối với các nút ấn.
Button BTN(3) BTN(2) BTN(1) BTN(0)
FPGA L14 L13 M14 M13

Danh sách kết nối với các led chúng ta xem lại ở bài tập số 1. Căn cứ vào danh sách
kết nối như trên chúng ta tiến hành gán chân cho các cổng vào mà ta đã định nghĩa
như sau:
Tiếp cận lập trình cho FPGA từ Spartan -3 132

Hình 5.2: Cửa sổ các cổng tương ứng với danh sách kết nối

Các bước tiếp theo là nạp cấu hình cho kid, chạy kiểm tra như đã làm ở bài tập 1.
Như vậy, thông qua bài tập này chúng ta đã biết cách chia tần số đầu vào để tạo ra
các tần số mong muốn, biết cách hiển thị các số lên dàn led, biết cách điều khiển tốc
độ quét cho các led sao cho đảm bảo đủ sáng mà vẫn tạo cảm giác các đèn được
thắp sáng liên tục.
Thông qua bài thực hành này, chúng ta có thể mở rộng việc thiết kế với một
vài ứng dụng cụ thể như đo tần số hiển thị lên led bảy đoạn, thiết kế việc đếm sản
phẩm theo quy tắc xung…
Tiếp cận lập trình cho FPGA từ Spartan -3 133

II. Bài toán số 3 :Mục đích của bài này là giúp chúng ta ôn lại cách thức
hiển thị dữ liệu lên dàn đèn led bảy đoạn. Một vấn đề mới đề cập là khai
thác cổng kết nối RS232 thực hiện việc truyền dữ liệu nối tiếp từ máy
tính sang.
Ø Nội dung bài toán như sau: Viết chương trình truyền thông giữa
máy tính và Spartan – 3 thông qua cổng kết nối RS232, hiển thị các kí
tự nhận được lên dàn led bảy đoạn dưới dạng các số hex.
Ø Phân tích và thiết kế: Để hoàn thành được bài toán giao tiếp và hiển
thị này chúng ta cần phải ôn lại kiến thức về cách thức truyền tin nối
tiếp thông qua Rs232. Với bài toán này chúng ta sẽ tổ chức đường
truyền 8 bít dữ liệu, không bít parity, 1 bít stop, 1 bít start. Như vậy
khung tin cần truyền là 10 bít.
Hoạt động truyền dữ liệu nối tiếp diễn ra như sau: Ở phần thiết bị truyền, lúc bắt
đầu làm việc nó sẽ đưa ra một tín hiệu txd_rdy báo là nó đã sẵn sàng làm việc. Ở
phần thu cũng vậy, nó sẽ gửi ra tín hiệu rxd_rdy báo là nó sẵn sàng nhận dữ liệu và
sau đó nó sẽ gửi sang phía phát tín hiệu enable để gọi sang phía bên kia để kết nối
liên lạc.
Việc truyền tin được gửi đi từng bít một và một khung tin là 10 bít ( bắt đầu
bằng bít start và kết thúc bằng bít stop). Quá trình truyền tin khởi đầu bởi lệnh bất
kì có sử dụng thanh ghi đệm truyền như một thanh ghi đích ( đó là Tbuff ở trong
chương trình). Tín hiệu “ ghi vào đệm truyền” ( tương ứng TBufL = '1' ) sẽ nạp giá
tị 1 vào vị trí bít thứ 9 của thanh ghi dịch truyền và báo cho khối điều khiển tx về
yêu cầu cần truyền tin. Quá trình truyền tin bắt đầu bằng sự kích hoạt của tín hiệu
đồng bộ bằng một ( LoadS = '1') dùng để mở cổng cho Txd ( đặt bít khởi đầu tại
Txd), cùng với việc tín hiệu này được kích hoạt thì dữ liệu cũng được truyền đến
thanh ghi đệm truyền và từ đó đưa đến thanh ghi dịch truyền đến đầu ra TxD. Xung
nhịp để dịch các bít trong thanh ghi dịch truyền sẽ được xuất hiện ngay sau đó. Khi
các bít dữ liệu dịch sang phải thì các gía trị 0 được đưa vào từ bên trái. Khi MSB
của byte dữ liệu ở đầu ra của thanh ghi dịch thì giá trị 1 ( ban đầu đã được nạp vào
Tiếp cận lập trình cho FPGA từ Spartan -3 134

vị trí thứ 9) sẽ được điền vào ngay bên trái của bít MSB còn các bít kể từ nó sang
trái đều có giá trị 0. Điều kiện này sẽ chỉ thị để khối điều khiển TX thực hiện lần
dịch cuối cùng và sau đó đưa trả tín hiệu TbufL về trạng thái thụ động, đồng thời
xác lập cờ ngắt cho TI ( gửi thông báo Tx Busy= ‘1’). Thời điểm này cũng đồng
nhất với thời điểm “ cho phép ghi vào đệm thu” của quá trình thu tin. Toàn bộ mức
phân tích này tương ứng với việc viết lệnh trong thực thể Txunit.
Quá trình nhận tin được khởi đầu bằng phát hiện sự chuyển trạng thái từ 1
đến 0 ở đường thu nối tiếp RxD. Để phát hiện chính xác , tín hiệu RxD được lấy
mẫu ở tốc độ gấp 16 lần tốc độ baud của đường truyền. Khi một bít được phát hiện
thì bộ đếm 16 được tái xác lập ngay và gía trị 1FFH được ghi vào thanh ghi dịch
đầu vào. Việc tái thiết lập bộ đếm 16 sẽ đồng nhất thời điểm tràn của bộ đếm với cá
biên thời gian của bít đang đi tới đầu thu. Bằng cách đó mỗi bít được chia thành 16
phần bằng nhau. Tại các thành phần thời gian thứ 7, 8, 9 của mỗi bít, bộ phát hiện
sẽ trích mẫu RxD. Giá trị được chấp nhận là giá trị đã có ít nhất là 2 trong 3 mẫu.
Phương pháp này đựơc thực hiện nhằm chống nhiễu đường truyền. Khi các bít dữ
liệu đi vào từ phía bên phải của thanh ghi dịch, thì các giá trị 1 được dịch sang bên
trái nó. Khi bít khởi đầu đến vị trí trái cùng của thanh ghi dịch thì nó chỉ thị cho
khối điều khiển RX thực hiện phép dịch chuyển cuối cùng rồi nạp vào đệm thu rồi
xác lập RI kết thúc quá trình nhận một byte và yêu cầu gửi byte tiếp theo. Toàn bộ
quá trình phân tích trên tương ứng với phần viết lệnh trong thực thể RxUnit.
Ta biết rằng quá trình nhận tin và quá trình truyền tin không diễn ra tách biệt.
Việc đồng bộ cần phải được thiết lập. Đó là thực thể UART để kết hợp và đồng bộ
các thành phần lại với nhau.
Giả sử đường truyền đã thông. Nghĩa là toàn bộ các thành phần thiết kế cho
một đường truyền nối tiếp đã thực hiện đúng chức năng. Bây giờ ta sẽ hiển thị các
dữ liệu đó lên dàn led 7 đoạn. Việc hiển thị đó rất đơn giản, ta chỉ cần gán một byte
dữ liệu cho một thanh ghi 8 bít sau đó hiển thị từng bốn bít một theo mã hex tương
ứng với các kí tự nhận được. Như vậy chỉ cần hai đèn là đủ. Cách thức hiển thị ta
cũng vẫn phải tạo ra một tín hiệu 1khz để quét led. Vẫn phải tổ chức việc giải mã.
Tiếp cận lập trình cho FPGA từ Spartan -3 135

Còn việc hiển thị ra sao các bạn xem lại ở bài tập 2 hoặc có thể đọc chương trình
mà ta đã viết. Chú ý là ở đây tất cả các chương trình đều đã được nạp vào kid và
kiểm tra việc hiển thị kết quả đúng theo yêu cầu thiết kế.
Hình vẽ 5.3a và 5.3b: một mô hình trạng thái đơn giản mô tả việc chuyển các
trạng thái khi thực hiện truyền và nhận dữ liệu.

Hình 5.3a Hình 5.3b

Dưới đây xin trích toàn bộ chương trình ta đã thực hiện để chúng ta tham
khảo. Chú ý là việc đọc chương trình tuân theo các bước sau : Đầu tiên là tạo tín
hiệu đồng bộ cho quá trình phát. Sau đó viết một chương trình tổ chức việc đếm để
dùng chung cho các thực thể sau đó. Vì phải thực hiện tạo tốc độ baud là 9600 nên
ta chọn hệ số chia là (50,000,000 / 9600) / 4 = 1302.
--Chương trình tạo tín hiệu đồng bộ
library IEEE,STD;
use IEEE.Std_Logic_1164.all;
Tiếp cận lập trình cho FPGA từ Spartan -3 136

entity synchroniser is
port (
C1 : in Std_Logic; -- Tín hiệu dị bộ
C : in Std_Logic; -- Clock
O : out Std_logic); -- Tín hiệu đồng bộ
end entity;

architecture Behaviour of synchroniser is


signal C1A : Std_Logic;
signal C1S : Std_Logic;
signal R : Std_Logic;
begin
RiseC1A : process(C1,R)
begin
if Rising_Edge(C1) then
C1A <= '1';
end if;
if (R = '1') then
C1A <= '0';
end if;
end process;

SyncP : process(C,R)
begin
if Rising_Edge(C) then
if (C1A = '1') then
C1S <= '1';
else C1S <= '0';
end if;
Tiếp cận lập trình cho FPGA từ Spartan -3 137

if (C1S = '1') then


R <= '1';
else R <= '0';
end if;
end if;
if (R = '1') then
C1S <= '0';
end if;
end process;
O <= C1S;
end Behaviour;

-------------------------------------------------------------------------------
-- Bộ đếm dùng chung
-------------------------------------------------------------------------------
library IEEE,STD;
use IEEE.Std_Logic_1164.all;

entity Counter is
generic(Count: INTEGER range 0 to 65535);
port (
Clk : in Std_Logic; -- Clock
Reset : in Std_Logic; -- Reset input
CE : in Std_Logic; -- Chip Enable
O : out Std_Logic); -- Output
end entity;

architecture Behaviour of Counter is


begin
Tiếp cận lập trình cho FPGA từ Spartan -3 138

counter : process(Clk,Reset)
variable Cnt : INTEGER range 0 to Count-1;
begin
if Reset = '1' then
Cnt := Count - 1;
O <= '0';
elsif Rising_Edge(Clk) then
if CE = '1' then
if Cnt = 0 then
O <= '1';
Cnt := Count - 1;
else
O <= '0';
Cnt := Cnt - 1;
end if;
else O <= '0';
end if;
end if;
end process;
end Behaviour;

-------------------------------------------------------------------------------
-Dưới đây là mô đun thực hiện việc truyền dữ liệu
------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;

entity TxUnit is
Tiếp cận lập trình cho FPGA từ Spartan -3 139

port (
Clk : in Std_Logic;
Reset : in Std_Logic;
Enable : in Std_Logic;
LoadA : in Std_Logic;
TxD : out Std_Logic;
Busy : out Std_Logic; -- Tx Busy
DataI : in Std_Logic_Vector(7 downto 0)); --Byte truy ền.
end entity;

architecture Behaviour of TxUnit is


--Khai báo thành phần đồng bộ tín hiệu cung cấp cho quá trình truyền .
component synchroniser is
port (
C1 : in Std_Logic; -- Tín hiệu thiếu đồng bộ
C : in Std_Logic; -- Xung clock
O : out Std_logic);-- Tín hiệu đồng bộ
end component;

signal TBuff : Std_Logic_Vector(7 downto 0); -- Đệm truyền


signal TReg : Std_Logic_Vector(7 downto 0); -- Thanh ghi dịch truyền
signal TBufL : Std_Logic; -- Cho phép ghi vào đệm truyền
signal LoadS : Std_Logic;-- Tín hiệu đồng bộ

begin
-- Synchronise Load on Clk
SyncLoad : Synchroniser port map (LoadA, Clk, LoadS);
Busy <= LoadS or TBufL;
Tiếp cận lập trình cho FPGA từ Spartan -3 140

-- Tx process
TxProc : process(Clk, Reset, Enable, DataI, TBuff, TReg, TBufL)
variable BitPos : INTEGER range 0 to 10; -- Vị trí của các bít trong khung
tin.
begin
if Reset = '1' then
TBufL <= '0';
BitPos := 0;
TxD <= '1';
elsif Rising_Edge(Clk) then
if LoadS = '1' then
TBuff <= DataI;
TBufL <= '1';
end if;
if Enable = '1' then
case BitPos is
when 0 => -- idle or stop bit
TxD <= '1';
if TBufL = '1' then – Cho phép ghi vào đệm truyền. Bít tiếp theo là
start bit.
TReg <= TBuff;
TBufL <= '0';
BitPos := 1;
end if;
when 1 => -- Start bit
TxD <= '0';
BitPos := 2;
when others =>
TxD <= TReg(BitPos-2); --Từng bít một được gửi ra TxD
Tiếp cận lập trình cho FPGA từ Spartan -3 141

BitPos := BitPos + 1;
end case;
if BitPos = 10 then
BitPos := 0;
end if;
end if;
end if;
end process;
end Behaviour;
-------------------------------------------------------------------------------

--Mô đun nhận dữ liệu từ đường truyền.

-------------------------------------------------------------------------------
-- File : Rxunit.vhd
------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;

entity RxUnit is
port (
Clk : in Std_Logic; -- Xung clock hệ thống
Reset : in Std_Logic; -- Reset input
Enable : in Std_Logic; -- Enable input
ReadA : in Std_logic;
RxD : in Std_Logic;
RxAv : out Std_Logic; -- Sẵn sàng nhận dữ liệu
DataO : out Std_Logic_Vector(7 downto 0)); -- Byte nh ận
end entity;
Tiếp cận lập trình cho FPGA từ Spartan -3 142

architecture Behaviour of RxUnit is


signal RReg : Std_Logic_Vector(7 downto 0); -- Thanh ghi d ịch thu
signal RRegL : Std_Logic;
begin
-- RxAv process
RxAvProc : process(RRegL,Reset,ReadA)
begin
if ReadA = '1' or Reset = '1' then
RxAv <= '0'; -- Phủ định RxAv khi đang thực hiện truyền
elsif Rising_Edge(RRegL) then
RxAv <= '1';
end if;
end process;

-- Rx Process
RxProc : process(Clk,Reset,Enable,RxD,RReg)
variable BitPos : INTEGER range 0 to 10; -- Vị trí của bít trong khung tin
variable SampleCnt : INTEGER range 0 to 3; -- Đếm từ 0 đến 3 cho mỗi bít.
Như đã phân tích ở trên, bộ đếm mẫu trong phần thu nhằm chống nhiễu đường
truyền.
begin
if Reset = '1' then -- Reset
RRegL <= '0';
BitPos := 0;
elsif Rising_Edge(Clk) then
if Enable = '1' then
case BitPos is
when 0 => --không làm gì cả
Tiếp cận lập trình cho FPGA từ Spartan -3 143

RRegL <= '0';


if RxD = '0' then -- Start Bit
SampleCnt := 0;
BitPos := 1;
end if;
when 10 => -- Stop Bit
BitPos := 0;
RRegL <= '1';
DataO <= RReg; -- Lưu byte nhận được từ đường truyền.
when others =>
if SampleCnt = 1 then
RReg(BitPos-2) <= RxD;
end if;
if SampleCnt = 3 then
BitPos := BitPos + 1;
end if;
end case;
if SampleCnt = 3 then
SampleCnt := 0;
else
sampleCnt := SampleCnt + 1;
end if;

end if;
end if;
end process;
end Behaviour;
Tiếp cận lập trình cho FPGA từ Spartan -3 144

-------------------------------------------------------------------------------
-- Tổ chức một UART hoàn chỉnh.
------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;

entity UART is
generic(BRDIVISOR: INTEGER range 0 to 65535 := 130);
port (
WB_CLK_I : in Std_Logic; --xung dong ho
WB_RST_I : in Std_Logic; -- Dau vao reset
WB_ADR_I : in Std_Logic_Vector(1 downto 0); -- bus dia chia
WB_DAT_I : in Std_Logic_Vector(7 downto 0); -- bus du lieu vao
WB_DAT_O : out Std_Logic_Vector(7 downto 0); -- bus du lieu ra
WB_WE_I : in Std_Logic; -- Cho phep ghi
WB_STB_I : in Std_Logic; -- Biến đợi
WB_ACK_O : out Std_Logic;-- Bao cho biet da nhan duoc.
--Cac tin hieu xu ly
IntTx_O : out Std_Logic; -- Ngat truyen:chi ra thoi gian doi cho moi byte
truyen
IntRx_O : out Std_Logic; -- Ngat thu:///////////////////////////////////// nhan
BR_Clk_I : in Std_Logic; -- xung thoi gian cho qua trinh truyen va nhan
du lieu
TxD_PAD_O: out Std_Logic; -- Duong Tx Rs232
RxD_PAD_I: in Std_Logic); -- Duong Rx Rs232
end entity;
architecture Behaviour of UART is
Tiếp cận lập trình cho FPGA từ Spartan -3 145

component Counter is
generic(COUNT: INTEGER range 0 to 65535);
port (
Clk : in Std_Logic; -- Clock
Reset : in Std_Logic; -- Reset input
CE : in Std_Logic; -- Chip Enable
O : out Std_Logic); -- Output
end component;
--Khai báo thành phần RxUnit
component RxUnit is
port (
Clk : in Std_Logic;
Reset : in Std_Logic;
Enable : in Std_Logic;
ReadA : in Std_logic;
RxD : in Std_Logic;
RxAv : out Std_Logic;
DataO : out Std_Logic_Vector(7 downto 0));
end component;
--Khai báo thành phần TxUnit
component TxUnit is
port (
Clk : in Std_Logic;
Reset : in Std_Logic;
Enable : in Std_Logic;
LoadA : in Std_Logic;
TxD : out Std_Logic;
Busy : out Std_Logic;
DataI : in Std_Logic_Vector(7 downto 0));
Tiếp cận lập trình cho FPGA từ Spartan -3 146

end component;

signal RxData : Std_Logic_Vector(7 downto 0); -- So byte cuoi cung nhan duoc
signal TxData : Std_Logic_Vector(7 downto 0); -- So byte cuoi cung truyen di
signal SReg : Std_Logic_Vector(7 downto 0); -- Thanh ghi trang thai duong truyen
signal EnabRx : Std_Logic; -- Enable RX
signal EnabTx : Std_Logic; -- Enable TX
signal RxAv : Std_Logic; -- Du lieu nhan duoc
signal TxBusy : Std_Logic; -- Duong truyen ban
signal ReadA : Std_Logic; -- Dem truyen
signal LoadA : Std_Logic; -- Dem thu
signal Sig0 : Std_Logic; --Tin hieu muc '0'
signal Sig1 : Std_Logic; --Tin hieu muc '1'

begin
sig0 <= '0';
sig1 <= '1';
Uart_Rxrate : Counter -- Dieu chinh lai toc do Baud
generic map (COUNT => BRDIVISOR)
port map (BR_CLK_I, sig0, sig1, EnabRx);
Uart_Txrate : Counter --he so chia 4 cho Tx
generic map (COUNT => 4)
port map (BR_CLK_I, Sig0, EnabRx, EnabTx);
Uart_TxUnit : TxUnit port map (BR_CLK_I, WB_RST_I, EnabTX, LoadA,
TxD_PAD_O, TxBusy, TxData);
Uart_RxUnit : RxUnit port map (BR_CLK_I, WB_RST_I, EnabRX, ReadA,
RxD_PAD_I, RxAv, RxData);
IntTx_O <= not TxBusy;
IntRx_O <= RxAv;
Tiếp cận lập trình cho FPGA từ Spartan -3 147

SReg(0) <= not TxBusy;


SReg(1) <= RxAv;

-- Cac thanh phan chinh trong viec trao doi du lieu.


-- Viec dong bo duoc thuc hien boi xung clock va reset
WBctrl : process(WB_CLK_I, WB_RST_I, WB_STB_I, WB_WE_I,
WB_ADR_I)
variable StatM : Std_Logic_Vector(4 downto 0);
begin
if Rising_Edge(WB_CLK_I) then
if (WB_RST_I = '1') then
ReadA <= '0';
LoadA <= '0';
else
if (WB_STB_I = '1' and WB_WE_I = '1' and WB_ADR_I = "00") then –
Ghi byte ra Tx
TxData <= WB_DAT_I;
LoadA <= '1'; -- Tin hieu nap
else LoadA <= '0';
end if;
if (WB_STB_I = '1' and WB_WE_I = '0' and WB_ADR_I = "00") then --
Đọc byte từ Rx
ReadA <= '1'; -- Tin hieu doc
else ReadA <= '0';
end if;
end if;
end if;
end process;
WB_ACK_O <= WB_STB_I;
Tiếp cận lập trình cho FPGA từ Spartan -3 148

WB_DAT_O <=
RxData when WB_ADR_I = "00" else -- Doc byte tu Rx
SReg when WB_ADR_I = "01" else -- Doc trang thai thanh ghi.
X"00";
end Behaviour;

--------------------------------------------------------------------
--Hiển thị dữ liệu nhận được ra led bảy đoạn dưới dạng số Hex.
--------------------------------------------------------------------

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;

entity rs232 is
port
( seg :out std_logic_vector(8 downto 1);
clk : in std_logic; -- Tan so 50Mhz tu Sp3
td : out std_logic; -- Duong truyen Rs232
rd : in std_logic; -- Duong nhan Rs232
reset_n : in std_logic;
barled : out std_logic_vector(8 downto 1);
ethernet_cs_n : out std_logic ; --
digit : out std_logic_vector(3 downto 0));
end rs232;

architecture arch of rs232 is


--Khai báo thành phần UART để thực hiện việc ánh xạ cổng.
Tiếp cận lập trình cho FPGA từ Spartan -3 149

component UART is
generic(BRDIVISOR : integer range 0 to 65535 := 130);
port (
WB_CLK_I : in std_logic;
WB_RST_I : in std_logic;
WB_ADR_I : in std_logic_vector(1 downto 0);
WB_DAT_I : in std_logic_vector(7 downto 0);
WB_DAT_O : out std_logic_vector(7 downto 0);
WB_WE_I : in std_logic; -- Write Enable
WB_STB_I : in std_logic; -- Strobe
WB_ACK_O : out std_logic;
IntTx_O : out std_logic;
IntRx_O : out std_logic;
BR_Clk_I : in std_logic;
TxD_PAD_O : out std_logic;
RxD_PAD_I : in std_logic);
end component;

signal reset : std_logic;


signal rcv, tx, tx_x : std_logic_vector(7 downto 0); -- các dữ liệu truyền
đi và nhận về.
signal write, strobe, ack : std_logic;
signal td_rdy, rd_rdy : std_logic; -- C ờ báo truyền và nhận
signal reg0 : std_logic_vector(1 downto 0); -Địa chỉ thanh ghi của
mô đun UART.
type fsm_state is (read_char_state, write_char_state); --Trạng thái ghi và đọc
signal state, state_x : fsm_state; -- Biến điều khiển trạng thái
signal sốthực : std_logic_vector(3 downto 0);
signal bcdint:std_logic_vector(16 downto 9);
Tiếp cận lập trình cho FPGA từ Spartan -3 150

signal cd: std_logic_vector(2 downto 0);


signal mhertz_count : std_logic_vector(5 downto 0) ; --
signal khertz_count : std_logic_vector(9 downto 0) ; --
signal dp : std_logic ; --
signal khertz_en : std_logic ;
signal mhertz_en : std_logic ; --
begin
ethernet_cs_n <= '1';
--Reset co muc tich cuc cao.
reset <= not reset_n;
reg0 <= "0" & not(rd_rdy or td_rdy);
--Ánh xạ cổng
u1 : uart
generic map(
--Vì tần số thạch anh là 50 Mhz, cần tạo tốc độ baud là 9600 tương ứng hệ
số chia là(50,000,000 / 9600) / 4 = 1302.
brdivisor => 1302
)
port map(
wb_clk_i => clk,
wb_rst_i => reset,
wb_adr_i => reg0,
wb_dat_i => tx,
wb_dat_o => rcv,
wb_we_i => write,
wb_stb_i => strobe,
wb_ack_o => ack,
inttx_o => td_rdy,
intrx_o => rd_rdy,
Tiếp cận lập trình cho FPGA từ Spartan -3 151

br_clk_i => clk,


txd_pad_o => td,
rxd_pad_i => rd
);
barled <=tx;
bcdint(16 downto 9)<=tx;
--Hien thi cac ki tu nhan duoc ra led va led bay doan.
--Qua trinh do duoc dieu khien boi mot mo hinh may trang thai don gian.
process(rcv, td_rdy, rd_rdy, state)
begin
state_x <= state;
tx_x <= tx;
strobe <= '0';
write <= '0';
case state is
when read_char_state =>
if rd_rdy = '1' then
strobe <= '1';
tx_x <= rcv;
state_x <= write_char_state; -- Trạng thái tiếp theo : Ghi kí tự nhận
được vào bộ đệm.
end if;
when write_char_state =>
if td_rdy = '1' then
strobe <= '1';
write <= '1';
state_x <= read_char_state;
end if;
when others =>
Tiếp cận lập trình cho FPGA từ Spartan -3 152

state_x <= read_char_state;


end case;
end process;

process(clk, reset)
begin --Thuc hien chia tan tao tan so quet led bay doan
if reset = '1' then
mhertz_count <= (others => '0') ;
mhertz_en <= '0' ;
elsif clk'event and clk = '1' then
mhertz_count <= mhertz_count + 1 ;
if mhertz_count = "110010" then
mhertz_en <= '1' ;
mhertz_count <= (others => '0') ;
else
mhertz_en <= '0' ;
end if ;
end if ;
end process ;
-- tao tin hieu 1 kilohec tu tin hieu 1 megahec
process (clk, reset )
begin
if reset = '1' then
khertz_count <= (others => '0') ;
khertz_en <= '0' ;
elsif clk'event and clk = '1' then
if mhertz_en = '1' then
khertz_count <= khertz_count + 1 ;
if khertz_count = "1111101000" then
Tiếp cận lập trình cho FPGA từ Spartan -3 153

khertz_en <= '1' ;


khertz_count <= (others => '0') ;
else
khertz_en <= '0' ;
end if ;
else
khertz_en <= '0' ;
end if ;
end if ;
end process ;
process (clk, reset )
begin
if reset = '1' then
state <= read_char_state;
seg<=(others=>'1');
cd <= (others => '0') ;
sốthực <= (others => '0') ;
elsif rising_edge(clk) then
tx <= tx_x;
state <= state_x;
cd(2) <= '1' ;
if khertz_en = '1' then
cd(1 downto 0) <= cd(1 downto 0) + 1 ;
end if ;
case cd(1 downto 0) is
when "00" => sốthực <= bcdint(12 downto 9) ; digit <= "1011" ;--Chon 2 led ben
trai
when others => sốthực <= bcdint(16 downto 13) ; digit <= "0111" ;
end case ;
Tiếp cận lập trình cho FPGA từ Spartan -3 154

if cd(2) = '1' then


case sốthực is
when "0000" => seg <= "0000001" & dp ;--0
when "0001" => seg <= "1001111" & dp ;--1
when "0010" => seg <= "0010010" & dp ;--2
when "0011" => seg <= "0000110" & dp ;--3
when "0100" => seg <= "1001100" & dp ;--4
when "0101" => seg <= "0100100" & dp ;--5
when "0110" => seg <= "0100000" & dp ;--6
when "0111" => seg <= "0001111" & dp ;--7
when "1000" => seg <= "0000000" & dp ;--8
when "1001" => seg <= "0000100" & dp ;--9
When "1010" =>seg <= "0001000" & dp ;--Chu A
When "1011" =>seg <= "1100000" & dp ;--Chu b
When "1100" => seg <= "0110001" & dp ;--Chu C
When "1101" => seg <= "1000010" & dp; --Chu d
When "1110" => seg <= "0110000" & dp; -- Chu E
When others=> seg <= "0111000" & dp; --Chu F
end case ;
else
seg <= (others => '1') ;
end if ;
end if ;
end process ;
end arch;

Sau khi đã viết xong toàn bộ chương trình chúng ta kiểm tra lại cấu trúc cũng như
ngữ pháp. Sơ đồ logic truyền thanh ghi và netlish theo cách mô tả của ta ở trên như
sau
Tiếp cận lập trình cho FPGA từ Spartan -3 155

Hình 5.4: Sơ đồ logic truyền thanh ghi của Rs232


Ø Việc gán chân căn cứ vào sơ đồ kết nối sau. Nghĩa là TxD gán với
R13; RxD gán với T13.
Tiếp cận lập trình cho FPGA từ Spartan -3 156

Ø Các thành phần còn lại bao gồm việc kết nối cho các led, led 7 đoạn,
các nút ấn, các chuyển mạch, xung clock đầu vào chúng ta xem lại ở
các bài tập trên. Khi đó ta tiến hành tạo file rs232.ucf như sau:

Hình 5.4: kết nối các cổng sử dụng trong Rs232


Tiến hành nạp vào kid để kiểm tra quá trình thiết kế.
Hướng dẫn kiểm tra việc truyền thông: Chúng ta có thể tự viết một
chương trình kiểm tra quá trình truyền và nhận tin qua Rs232 Tuy vậy ở bài tập này
để tiết kiệm thời gian ta sử dụng Hyper terminal là một tiện ích sẵn có trong
WinXP. Vào All Progams à Accessories à communications à Hyper terminal.
Khi đó cửa sổ sau sẽ hiện ra :
Tiếp cận lập trình cho FPGA từ Spartan -3 157

Hình 5.5: Giao diện khi gọi chương trình Hyper teminal
Điền một cái tên gì đó trong phần name, ví dụ ta đặt là thu nghiem. Sau đó nhấn OK
để chấp nhận. Cửa sổ sau sẽ hiện ra.

Ở phần Country/region chọn VietNam(84), Area code ta chọn một mã số bất kì (vì
ta không thực hiện việc kết nối thực), connect using chọn Com1. Nhấn OK để chấp
nhận. Khi đó cửa sổ tiếp theo sẽ hiện ra.
Tiếp cận lập trình cho FPGA từ Spartan -3 158

Chọn tốc độ bít là 9600 tương ứng với mức thiết kế, số bít dữ liệu là 8, không bít
Parity, một bít stop, flow control chọn None. Sau đó apply rồi OK. Tiếp theo ta thử
nghiệm bằng cách viết một kí tự bất kì giả sử ta chọn chữ a khi đó led 7 đoạn hiển
thị đúng số 61 tương ứng với mã ASCII của kí tự a. Nếu như ta đồng bộ và viết
chương trình đúng thì trên màn hình của chúng ta cũng hiển thị đúng kí tự a do
Spartan-3 trả về.

Hình 5.6: Truyền tin thành công và đúng yêu cầu thiết kế.
Tiếp cận lập trình cho FPGA từ Spartan -3 159

Bài 4. Xuất dữ liệu ra màn hình.


Ø Mục đích:Có thể nói đây là một bài toán khá khó. Tuy vậy nếu như
chúng ta hiểu được cách thức tạo ảnh trên màn hình và việc phối hợp giữa quá trình
quét hàng, quét cột và tốc độ đọc dữ liệu thì chúng ta có thể làm chủ được việc xuất
tín hiệu lên màn hình.Như vậy với bài toán này nhằm giúp chúng ta có thể làm hiểu
được cách thức tạo ảnh trên màn hình, việc tạo các điểm màu và đặc biệt là việc
phối hợp đồng bộ giữa các tín hiệu sử dụng trong kỹ thuật truyền hình.
Ø Nội dung: Viết chương trình cho Spartan-3 vẽ các ô vuông liên tiếp
nhau trên màn hình sao cho các ô vuông đó có màu sắc thay đổi từ
đỏ, xanh, chàm và viền màu đen.
Ø Phân tích và thiết kế: Việc hiển thị lên màn hình được tiến hành
dựa trên các đường quét ngang từ phải qua trái và quét dọc từ trên xuống dưới.Việc
hiển thị lên trên màn hình dưới dạng các điểm ảnh ( các picel). Số Picel để quét hết
một đường ngang và số Picel để quét hết một đường dọc gọi là độ phân giải.Để dễ
dàng hình dung cách tạo ảnh trên màn hình chúng ta xét hình vẽ sau

Hình vẽ trên là một ví dụ minh hoạ cho việc hiển thị trên màn hình CRT với độ
phân giải là 640*480. Như vậy, để tạo ra điện áp răng cưa thực hiện việc quét cần
Tiếp cận lập trình cho FPGA từ Spartan -3 160

phải cung cấp một tín hiệu sao cho ứng với điện áp quét ngược khi đó sẽ là tín hiệu
xoá cho một dòng quét. Dưới đây là biểu đồ thời gian tín hiệu VGA

Trong đó các tham số TPW, TS,Tdisp, Tfp, Tbp với các độ phân giải khác nhau sẽ khác
nhau. Có hai độ phân giải ta thường sử dụng hơn cả là 640*480 và 800*600. Dưới
đây là bảng tham số TPW, TS,Tdisp, Tfp, Tbp ứng với độ phân giải 640*480 là độ phân
giải mà ta hay sử dụng trong quá trình thiết kế. Thực ra các giá trị này chúng ta có
thể thay đổi trong một vài giá trị vẫn đảm bảo việc đồng bộ quét và tạo ảnh trên
màn hình theo đúng ý đồ thiết kế việc hiển thị.

Như vậy để tạo ra tín hiệu quét như trên đơn giản ta tạo ra một bộ đếm với số đếm
tối đa lên đến 800 sẽ reset về không. Bộ đếm đó thực hiện đếm đến các giá trị tương
ứng cho trên bảng trên thì xuất tín hiệu đó lên mức ‘1’. Một điều lưu ý là tần số ứng
với mỗi Picel là 25 Mhz. Chính vì vậy ta phải tạo ra xung clock 25Mhz để tạo sự
kiện đếm đối với mỗi giá trị Picel. Để tạo ra ô vuông màu thì chỉ cần số điểm ảnh
trên đường quét ngang bằng số điểm ảnh trên đường quét dọc và xuất màu tại vị trí
đó. Từ các phân tích trên ta đi đến chương trình cụ thể như sau:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
Tiếp cận lập trình cho FPGA từ Spartan -3 161

use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity VGA is
port(clk50_in : in std_logic;
red_out : out std_logic;
green_out : out std_logic;
blue_out : out std_logic;
hs_out : out std_logic;
vs_out : out std_logic);
end VGA;
architecture Behavioral of VGA is
signal clk25 : std_logic;
signal horizontal_counter : std_logic_vector (9 downto 0);
signal vertical_counter : std_logic_vector (9 downto 0);
begin
--Tạo tín hiệu 25Mhz.
process (clk50_in)
begin
if clk50_in'event and clk50_in='1' then
if (clk25 = '0') then
clk25 <= '1';
else
clk25 <= '0';
end if;
end if;
end process;
process (clk25)
begin
if clk25'event and clk25 = '1' then
Tiếp cận lập trình cho FPGA từ Spartan -3 162

if (horizontal_counter >= "0010010000" ) -- 144


and (horizontal_counter < "1100010000" ) -- 784
and (vertical_counter >= "0000100111" ) -- 39
and (vertical_counter < "1000000111" ) -- 519
then
red_out <= horizontal_counter(3)
and vertical_counter(3);
green_out <= horizontal_counter(4)
and vertical_counter(4);
blue_out <= horizontal_counter(5)
and vertical_counter(5);
else
red_out <= '0';
green_out <= '0';
blue_out <= '0';
end if;
if (horizontal_counter > "0000000000" )
and (horizontal_counter < "0001100001" ) -- 96+1
then
hs_out <= '0';
else
hs_out <= '1';
end if;
if (vertical_counter > "0000000000" )
and (vertical_counter < "0000000011" ) -- 2+1
then
vs_out <= '0';
else
vs_out <= '1';
Tiếp cận lập trình cho FPGA từ Spartan -3 163

end if;
horizontal_counter <= horizontal_counter+"0000000001";
if (horizontal_counter="1100100000") then
vertical_counter <= vertical_counter+"0000000001";
horizontal_counter <= "0000000000";
end if;
if (vertical_counter="1000001001") then
vertical_counter <= "0000000000";
end if;
end if;
end process;
end Behavioral;
Phân tích logic truyền thanh ghi ứng với chương trình trên như sau:

Hình 5.5: Sơ đồ logic mức truyền thanh ghi của thực thể VGA.
Tiếp cận lập trình cho FPGA từ Spartan -3 164

Bây giờ ta thực hiện kết nối VGA với Spartan -3. Việc kết nối căn cứ theo hình vẽ
sau:

Hình 5.6: Sơ đồ kết nối VGA với Spartan -3


Như vậy ta cần phải kết nối theo bảng dưới đây:

Căn cứ vào sơ đồ và bảng kết nối cho ở trên ta tạo ra file VGA.ucf để gán các cổng
vào ra tương ứng :
Tiếp cận lập trình cho FPGA từ Spartan -3 165

Bài 5: Bài toán t ổng hợp.


• Mục đích: Giao tiếp với bàn phím thường được dùng đến đối với các
thiết kế dựa trên bộ vi điều khiển. Nhập từ bàn phím và xuất ra led là
sự lựa chọn kinh tế để giao tiếp với người sử dụng và thường thích
hợp với các ứng dụng phức tạp. Với các bài toán nhỏ chúng ta thường
tự tạo một bàn phím đơn giản với chức năng do ta định nghĩa. Bài toán này đưa ra
nhằm giúp chúng ta làm quen với cách thức giao tiếp với bàn phím PS2 bao gồm
việc nhập kí tự số từ bàn phím, biến đổi thành mã ASCII, gửi ra đường truyền
RS232 và hiển thị lên màn hình.
• Nội dung bài toán: Viết chương trình cho Spartan-3 thực hiện việc
quét bàn phím Ps2, gửi kí tự đã biến đổi thành mã ASCII ra đường truyền RS232,
khi đó trên màn hình máy tính ta nhận được các kí tự tương ứng.
• Phân tích và hướng thiết kế: Có thể nói đây là một chương trình rất
khó và đòi hỏi phải có sự tổng hợp các kiến thức bao gồm các bài toán giao tiếp với
bàn phím PS2( bàn phím IBM), giao tiếp với máy tính thông qua Rs232.Bài toán
giao tiếp với máy tính thông qua RS232 chúng ta đã làm quen qua hai bài tập trên, ở
phần này ta không phân tích lại. Ta chỉ tập trung vào việc phân tích cách kết nối với
Ps2 ra sao?
- Đầu tiên là bảng mã hoá bàn phím PS2 do IBM cung cấp được đưa vào Rom
nhằm mục đích giải mã 7 bít sang mã ASCII, bít địa chỉ thứ 7 dành cho phím
Caplock, bits thứ 8 dành cho phím Shift. Dưới đây là bảng mã hoá bàn phím PS2 do
IBM cung cấp.
x"00327761737a0000003171000000000000600900000000000000000000000000", -- 1F - 00
x"003837756a6d00000036796768626e0000357274667620000033346564786300", -- 3F - 20
x"00005c005d0d000000003d5b00270000002d703b6c2f2e000039306f696b2c00", -- 5F - 40
x"0000000000000000001b000000007f0000000000000000000008000000000000", -- 7F - 60
x"00325741535a00000031510000000000007e0900000000000000000000000000", -- 9F - 80
x"003837554a4d00000036594748424e0000355254465620000033344544584300", -- BF - A0
x"00005c005d0d000000003d5b00270000002d503b4c2f2e000039304f494b2c00", -- DF - C0
x"0000000000000000001b000000007f0000000000000000000008000000000000", -- FF - E0
Tiếp cận lập trình cho FPGA từ Spartan -3 166

x"00405741535a00000021510000000000007e0900000000000000000000000000", -- 1F - 00
x"002a26554a4d0000005e594748424e0000255254465620000023244544584300", -- 3F - 20
x"00007c007d0d000000002b7b00220000005f503a4c3f3e000028294f494b3c00", -- 5F - 40
x"0000000000000000001b000000007f0000000000000000000008000000000000", -- 7F - 60
x"00407761737a0000002171000000000000600900000000000000000000000000", -- 9F - 80
x"002a26756a6d0000005e796768626e0000257274667620000023246564786300", -- BF - A0
x"00007c007d0d000000002b7b00220000005f703a6c3f3e000028296f696b3c00", -- DF - C0
x"0000000000000000001b000000007f0000000000000000000008000000000000" -- FF - E0
Chúng ta không nhất thiết phải hiểu quá rõ vì sao họ lại mã hoá như vậy vì
thực ra ta không có mục đích sẽ chế tạo ra bàn phím kiểu này. Ở đây nhiệm vụ
chính của chúng ta là sử dụng nó để giai quyết bài toán mà chúng ta đặt ra. Các kí
hiệu giải thích ở bên phải cùng là các mã hex tương ứng với các kí tự ASCII.
Các dữ liệu được truyền từ bàn phím theo phương pháp truyền nối tiếp vì vậy
cũng giống như bất cứ cách thức thực hiện truyền tin nối tiếp nào, dữ liệu truyền đi
đều phải đưa qua bộ đệm truyền, cũng tương tự như vậy, dữ liệu nhận từ bàn phím
cũng phải thực hiện đọc lần lượt từ bộ đệm tương ứng.Chính vì vậy cần phải sử
dụng một mô hình máy trạng thái để điều khiển thao tác chuyển đổi tuần tự sang
song song và từ song song sang tuần tự. Quá trình chuyển đổi tuần tự sang song
song để đưa ra đệm truyền ở thao tác quét phím và quá trình ngược lại cho thao tác
khôi phục mã ASCII.
Để tương xứng với bảng mã của IBM ở đây chúng ta có khai báo bít Parity để
nhận dạng cạnh và các lệnh khác nhau từ bàn phím, tuy nhiên do mục đích của
chúng ta không cần quá chú trọng điều đó vì vậy ta đã không xử lý với bít này.
Thay vào đó ta dùng hai bít "Extended" (0xE0) và "Released" (0xF0) để làm công
việc đó.
Cũng giống như quá trình truyền thông tin nối tiếp khác, ở đây chúng ta cần
phải xử dụng các cờ báo hiệu, loạt cờ chức năng bao gồm xác nhận phím ấn, cờ báo
cho phép chuyển đổi song song, nối tiếp, cờ báo lỗi trạng thái bàn phím do có quá
nhiều phím bị nhấn, cờ báo nhận….đều phải được khai báo để sử dụng.
Tiếp cận lập trình cho FPGA từ Spartan -3 167

Ở đây, cũng cần phải lưu ý rằng để tránh tình trạng các phím được ấn đồng
loạt, ta xử lý đồng thời hai tín hiệu Ps2_clk và Ps2_data_line thỉnh thoảng được kéo
xuống thấp và tương đương với các hàng điện trở đặt ở trạng thái trở kháng cao,
đồng nghĩa với việc xác nhận không có dòng dữ liệu nào được phép truyền đi cả.
Hơn nữa trong chương trình cũng đặt ra việc xử lý chống rung cho bàn phím bằng
cách đưa ra khoảng thời gian hãm “debounce timer”.
Điều cần quan tấm cuối cùng là việc đồng bộ tốc độ quét là xung clock 60us
được tạo ra bằng cách chia tần số. Cũng nhấn mạnh thêm rằng tần số chia ở đây sử
dụng tần số 12.5Mhz( nghĩa là tần số đã được chia ra ở trong thực thể giao tiếp với
máy tính qua Rs232 (thực thể rs232 ở trên)) vì bài toán chúng ta không chỉ dừng lại
ở việc quét phím. Chính vì vậy trong việc chia tần số tạo ra 60us chính là sử dụng
tần số 12.5Mhz để chia chứ không phải dùng xung clock 50Mhz từ hệ thống( do
vậy trong chương trình có hằng số 750, là hệ số tần số phải chia, vì
750/60us=12,5Mhz-đây là điều hết sức lưu ý khi các bạn đọc chương trình). Một
tham số thứ hai nữa là xung clock 5us được sử dụng cho mục đích chống rung do
hiện tượng nẩy phím.
Dưới đây là toàn bộ chương trình dùng để quét bàn phím và chuyển đổi các
phím ra mã ASCII tương ứng:
Thực thể dưới đây đưa ra bảng mã của PS2
---------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
library unisim;
use unisim.all;

entity key_b4 is
Port (
clk : in std_logic;
Tiếp cận lập trình cho FPGA từ Spartan -3 168

rst : in std_logic;
cs : in std_logic;
rw : in std_logic;
addr : in std_logic_vector (8 downto 0);
rdata : out std_logic_vector (7 downto 0);
wdata : in std_logic_vector (7 downto 0) );
end key_b4;

architecture rtl of key_b4 is


--Thành phần mô tả cấu trúc Ram.
component RAMB4_S8
generic (
INIT_00, INIT_01, INIT_02, INIT_03,
INIT_04, INIT_05, INIT_06, INIT_07,
INIT_08, INIT_09, INIT_0A, INIT_0B,
INIT_0C, INIT_0D, INIT_0E, INIT_0F : bit_vector (255 downto 0)
);

port (
clk : in std_logic;
rst : in std_logic;
en : in std_logic;
we : in std_logic;
addr : in std_logic_vector(8 downto 0);
di : in std_logic_vector(7 downto 0);
do : out std_logic_vector(7 downto 0)
);
end component RAMB4_S8;
signal we : std_logic;
Tiếp cận lập trình cho FPGA từ Spartan -3 169

begin
ROM : RAMB4_S8
Generic map (
INIT_00 => x"00327761737a0000003171000000000000600900000000000000000000000000", -- 1F - 00
INIT_01 => x"003837756a6d00000036796768626e0000357274667620000033346564786300", -- 3F - 20
INIT_02 => x"00005c005d0d000000003d5b00270000002d703b6c2f2e000039306f696b2c00", -- 5F - 40
INIT_03 => x"0000000000000000001b000000007f0000000000000000000008000000000000", -- 7F - 60
INIT_04 => x"00325741535a00000031510000000000007e0900000000000000000000000000", -- 9F - 80
INIT_05 => x"003837554a4d00000036594748424e0000355254465620000033344544584300", -- BF - A0
INIT_06 => x"00005c005d0d000000003d5b00270000002d503b4c2f2e000039304f494b2c00", -- DF - C0
INIT_07 => x"0000000000000000001b000000007f0000000000000000000008000000000000", -- FF - E0
INIT_08 => x"00405741535a00000021510000000000007e0900000000000000000000000000", -- 1F - 00
INIT_09 => x"002a26554a4d0000005e594748424e0000255254465620000023244544584300", -- 3F - 20
INIT_0A => x"00007c007d0d000000002b7b00220000005f503a4c3f3e000028294f494b3c00", -- 5F - 40
INIT_0B => x"0000000000000000001b000000007f0000000000000000000008000000000000", -- 7F - 60
INIT_0C => x"00407761737a0000002171000000000000600900000000000000000000000000", -- 9F - 80
INIT_0D => x"002a26756a6d0000005e796768626e0000257274667620000023246564786300", -- BF - A0
INIT_0E => x"00007c007d0d000000002b7b00220000005f703a6c3f3e000028296f696b3c00", -- DF - C0
INIT_0F => x"0000000000000000001b000000007f0000000000000000000008000000000000" -- FF - E0
)

port map ( clk => clk,


en => cs,
we => we,
rst => rst,
addr => addr,
di => wdata,
do => rdata);
my_ram_512 : process ( rw )
begin
we <= not rw;
end process;
end architecture rtl;
Tiếp cận lập trình cho FPGA từ Spartan -3 170

--Thực thể dưới đây mô tả giao diện chính của chương trình quét phím và
chuyển sang mã ASCII:
---------------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.all;
entity ps2_keyboard_interface is
port(
clk : in std_logic;
reset : in std_logic;
ps2_clk : inout std_logic;
ps2_data : inout std_logic;
rx_extended : out std_logic;
rx_released : out std_logic;
rx_shift_key_on : out std_logic;
rx_ascii : out std_logic_vector(7 downto 0);
rx_data_ready : out std_logic; -- rx_read_o
rx_read : in std_logic; -- rx_read_ack_i
tx_data : in std_logic_vector(7 downto 0);
tx_write : in std_logic;
tx_write_ack : out std_logic; --đã ghi xong??
tx_error_no_keyboard_ack : out std_logic
);
end ps2_keyboard_interface;

-------------------------------------------------------------------------------
Tiếp cận lập trình cho FPGA từ Spartan -3 171

architecture banphimPs2 of ps2_keyboard_interface is


constant TOTAL_BITS : integer := 11;
constant EXTEND_CODE : integer := 16#E0#; -tương ứng X “E0”(Mã Hex)
constant RELEASE_CODE : integer := 16#F0#;
constant LEFT_SHIFT : integer := 16#12#;
constant RIGHT_SHIFT : integer := 16#59#;
constant CTRL_CODE : integer := 16#14#;
constant CAPS_CODE : integer := 16#58#;
-- Các hằng số để tạo clock 60us từ 12.5Mhz khi đã thực hiện chia ở thực thể
VDU.
constant TIMER_60USEC_VALUE_PP : integer := 750; -- Hệ số chia tạo 60us
constant TIMER_60USEC_BITS_PP : integer := 10; -- Tương ứng số 750 thì cần
10 bít.
constant TIMER_5USEC_VALUE_PP : integer := 62; --Hệ số chia tạo 5us
constant TIMER_5USEC_BITS_PP : integer := 6; -- S ố bít tương ứng
constant TRAP_SHIFT_KEYS_PP : integer := 1; -- Mặc định không dùng phím
Trap-phím lật.
--Quá trình mã hoá trạng thái tương ứng với những hằng số dành cho tính linh
hoạt của môđun khởi tạo.
type m1_type is ( m1_rx_clk_h, m1_rx_clk_l, m1_tx_wait_clk_h,
m1_tx_force_clk_l, m1_tx_clk_h, m1_tx_clk_l,
m1_tx_wait_keyboard_ack, m1_tx_done_recovery, m1_tx_error_no_keyboard_ack,
m1_tx_rising_edge_marker, m1_tx_first_wait_clk_h, m1_tx_first_wait_clk_l,
m1_tx_reset_timer, m1_rx_falling_edge_marker, m1_rx_rising_edge_marker );
signal timer_60usec_done : std_logic; --đầu ra clock 60us
signal timer_5usec_done : std_logic; --đầu ra clock 5 us
signal extended : std_logic; -- tín hiệu nhận dạng cạnh ứng với mã x “0E”
signal released : std_logic; --tín hiệu nhận dạng cạnh ứng với mã x “0F”
signal shift_key_on : std_logic; --cho phép thực hiện chuyển dịch
Tiếp cận lập trình cho FPGA từ Spartan -3 172

signal ctrl_key_on : std_logic;


signal caps_key_on : std_logic;
signal rx_output_event : std_logic;
signal rx_output_strobe : std_logic;
signal tx_parity_bit : std_logic;
signal rx_shifting_done : std_logic;
signal tx_shifting_done : std_logic;
signal shift_key_plus_code: std_logic_vector(8 downto 0);
signal q : std_logic_vector(TOTAL_BITS-1 downto 0);
signal m1_state : m1_type;
signal m1_next_state : m1_type;
signal bit_count : std_logic_vector(3 downto 0);
signal enable_timer_60usec: std_logic;
signal enable_timer_5usec : std_logic;
signal timer_60usec_count : std_logic_vector(TIMER_60USEC_BITS_PP-1
downto 0);
signal timer_5usec_count : std_logic_vector(TIMER_5USEC_BITS_PP-1
downto 0);
signal ascii : std_logic_vector(7 downto 0);
signal left_shift_key : std_logic;
signal right_shift_key : std_logic;
signal hold_extended : std_logic;
signal hold_released : std_logic;
signal ps2_clk_s : std_logic;
signal ps2_data_s : std_logic;
signal ps2_clk_hi_z : std_logic; --2 tín hiệu tạo trở kháng cao
signal ps2_data_hi_z : std_logic;
signal tx_write_ack_o : std_logic;
Tiếp cận lập trình cho FPGA từ Spartan -3 173

component key_b4
Port (
clk : in std_logic;
rst : in std_logic;
cs : in std_logic;
rw : in std_logic;
addr : in std_logic_vector (8 downto 0);
rdata : out std_logic_vector (7 downto 0);
wdata : in std_logic_vector (7 downto 0)
);
end component;

begin
my_key_map : key_b4
Port map (
clk => clk,
rst => reset,
cs => '1',
rw => '1',
addr => shift_key_plus_code,
rdata => ascii,
wdata => "00000000"
);
--xử lý với hai tín hiệu ps2_clk_hi_z, ps2_data_hi_z tạo ra trở kháng cao
nhằm không cho truyền dữ liệu khi có nhiều phím được ấn.
ps2_direction : process( ps2_clk_hi_z, ps2_data_hi_z )
begin
if( ps2_clk_hi_z = '1' ) then
ps2_clk <= 'Z';
Tiếp cận lập trình cho FPGA từ Spartan -3 174

else
ps2_clk <= '0';
end if;
if( ps2_data_hi_z = '1' ) then
ps2_data <= 'Z';
else
ps2_data <= '0';
end if;
end process;
--Quá trình xử lý tiếp theo nhằm đồng bộ hoá các logic đầu vào
--như vậy có thể tránh được những lỗi liên quan đến việc chuyển trạng thái sai.
ps2_synch : process(clk, ps2_clk, ps2_data)
begin
if clk'event and clk='0' then
ps2_clk_s <= ps2_clk;
ps2_data_s <= ps2_data;
end if;
end process;

m1_state_register : process( clk, reset, m1_state )


begin
if clk'event and clk='0' then
if (reset = '1') then
m1_state <= m1_rx_clk_h;
else
m1_state <= m1_next_state;
end if;
end if;
end process;
Tiếp cận lập trình cho FPGA từ Spartan -3 175

m1_state_logic : process( m1_state, q,tx_shifting_done, tx_write, ps2_clk_s,


ps2_data_s, timer_60usec_done, timer_5usec_done )
begin
ps2_clk_hi_z <= '1';
ps2_data_hi_z <= '1';
tx_error_no_keyboard_ack <= '0';
enable_timer_60usec <= '0';
enable_timer_5usec <= '0';
case (m1_state) is
when m1_rx_clk_h =>
enable_timer_60usec <= '1';
if (tx_write = '1') then
m1_next_state <= m1_tx_reset_timer;
elsif (ps2_clk_s = '0') then
m1_next_state <= m1_rx_falling_edge_marker;
else
m1_next_state <= m1_rx_clk_h;
end if;

when m1_rx_falling_edge_marker =>


enable_timer_60usec <= '0';
m1_next_state <= m1_rx_clk_l;
when m1_rx_clk_l =>
enable_timer_60usec <= '1';
if (tx_write = '1') then
m1_next_state <= m1_tx_reset_timer;
elsif (ps2_clk_s = '1') then
m1_next_state <= m1_rx_rising_edge_marker;
Tiếp cận lập trình cho FPGA từ Spartan -3 176

else
m1_next_state <= m1_rx_clk_l;
end if;

when m1_rx_rising_edge_marker =>


enable_timer_60usec <= '0';
m1_next_state <= m1_rx_clk_h;

when m1_tx_reset_timer =>


enable_timer_60usec <= '0';
m1_next_state <= m1_tx_force_clk_l;

when m1_tx_force_clk_l =>


enable_timer_60usec <= '1';
ps2_clk_hi_z <= '0'; -- Force the ps2_clk line low.
if (timer_60usec_done = '1') then
m1_next_state <= m1_tx_first_wait_clk_h;
else
m1_next_state <= m1_tx_force_clk_l;
end if;

when m1_tx_first_wait_clk_h =>


enable_timer_5usec <= '1';
ps2_data_hi_z <= '0'; -- Start bit.
if (ps2_clk_s = '0') and (timer_5usec_done = '1') then
m1_next_state <= m1_tx_clk_l;
else
m1_next_state <= m1_tx_first_wait_clk_h;
end if;
Tiếp cận lập trình cho FPGA từ Spartan -3 177

--Thời gian trễ có thể lên tới 10miligiây trước khi bắt đầu một xung clock
--Trong suốt thời gian trễ, chúng ta không thể điều khiển dữ liệu (q[0])
when m1_tx_first_wait_clk_l =>
ps2_data_hi_z <= '0';
if (ps2_clk_s = '0') then
m1_next_state <= m1_tx_clk_l;
else
m1_next_state <= m1_tx_first_wait_clk_l;
end if;
when m1_tx_wait_clk_h =>
enable_timer_5usec <= '1';
ps2_data_hi_z <= q(0);
if (ps2_clk_s = '1') and (timer_5usec_done = '1') then
m1_next_state <= m1_tx_rising_edge_marker;
else
m1_next_state <= m1_tx_wait_clk_h;
end if;

when m1_tx_rising_edge_marker =>


ps2_data_hi_z <= q(0);
m1_next_state <= m1_tx_clk_h;

when m1_tx_clk_h =>


ps2_data_hi_z <= q(0);
if (tx_shifting_done = '1') then
m1_next_state <= m1_tx_wait_keyboard_ack;
elsif (ps2_clk_s = '0') then
m1_next_state <= m1_tx_clk_l;
else
Tiếp cận lập trình cho FPGA từ Spartan -3 178

m1_next_state <= m1_tx_clk_h;


end if;

when m1_tx_clk_l =>


ps2_data_hi_z <= q(0);
if (ps2_clk_s = '1') then
m1_next_state <= m1_tx_wait_clk_h;
else
m1_next_state <= m1_tx_clk_l;
end if;

when m1_tx_wait_keyboard_ack =>


if (ps2_clk_s = '0') and (ps2_data_s = '1') then
m1_next_state <= m1_tx_error_no_keyboard_ack;
elsif (ps2_clk_s = '0') and (ps2_data_s = '0') then
m1_next_state <= m1_tx_done_recovery;
else
m1_next_state <= m1_tx_wait_keyboard_ack;
end if;

when m1_tx_done_recovery =>


if (ps2_clk_s = '1') and (ps2_data_s = '1') then
m1_next_state <= m1_rx_clk_h;
else
m1_next_state <= m1_tx_done_recovery;
end if;

when m1_tx_error_no_keyboard_ack =>


tx_error_no_keyboard_ack <= '1';
Tiếp cận lập trình cho FPGA từ Spartan -3 179

if (ps2_clk_s = '1') and (ps2_data_s ='1') then


m1_next_state <= m1_rx_clk_h;
else
m1_next_state <= m1_tx_error_no_keyboard_ack;
end if;

when others =>


m1_next_state <= m1_rx_clk_h;
end case;
end process;
--Thực hiện đếm đến 11.
bit_counter: process(clk, reset, m1_state, bit_count )
begin
if clk'event and clk = '0' then
if ( reset = '1' ) or
( rx_shifting_done = '1' ) or
(m1_state = m1_tx_wait_keyboard_ack) then
bit_count <= "0000"; -- normal reset
elsif (timer_60usec_done = '1' ) and (m1_state = m1_rx_clk_h) and
(ps2_clk_s = '1') then
bit_count <= "0000";
elsif (m1_state = m1_rx_falling_edge_marker) or
(m1_state = m1_tx_rising_edge_marker) then
bit_count <= bit_count + 1;
end if;
end if;
end process;
assign: process( bit_count, tx_write, tx_write_ack_o, m1_state )
begin
Tiếp cận lập trình cho FPGA từ Spartan -3 180

if (bit_count = TOTAL_BITS) then


rx_shifting_done <= '1';
else
rx_shifting_done <= '0';
end if;

if (bit_count = (TOTAL_BITS-1)) then


tx_shifting_done <= '1';
else
tx_shifting_done <= '0';
end if;
if ((tx_write = '1') and (m1_state = m1_rx_clk_h)) or
((tx_write = '1') and (m1_state = m1_rx_clk_l)) then
tx_write_ack_o <= '1';
else
tx_write_ack_o <= '0';
end if;
tx_write_ack <= tx_write_ack_o;
end process;
-- gán tx_parity_bit = ~^tx_data;
tx_parity_bit <= not( tx_data(7) xor tx_data(6) xor tx_data(5) xor tx_data(4)
xor tx_data(3) xor tx_data(2) xor tx_data(1) xor tx_data(0) );

-- Thanh ghi dịch


q_shift : process(clk, tx_write_ack_o, tx_parity_bit, tx_data,
m1_state, q, ps2_data_s, rx_shifting_done )
begin
if clk'event and clk='0' then
if (reset = '1') then
Tiếp cận lập trình cho FPGA từ Spartan -3 181

q <= "00000000000";
elsif (tx_write_ack_o = '1') then
q <= "1" & tx_parity_bit & tx_data & "0";
elsif ( (m1_state = m1_rx_falling_edge_marker) or
(m1_state = m1_tx_rising_edge_marker) ) then
q <= ps2_data_s & q((TOTAL_BITS-1) downto 1);
end if;
end if;
--Tạo ra hai tín hiệu đặc biệt cho việc quét phím gồm Caplock và Shift
if (q(8 downto 1) = EXTEND_CODE) and (rx_shifting_done = '1') then
extended <= '1';
else
extended <= '0';
end if;
if (q(8 downto 1) = RELEASE_CODE) and (rx_shifting_done = '1') then
released <= '1';
else
released <= '0';
end if;
end process;

-- Tạo clock 60us


timer60usec: process(clk, enable_timer_60usec, timer_60usec_count)
begin
if clk'event and clk = '0' then
if (enable_timer_60usec = '0') then
timer_60usec_count <= "0000000000";
elsif (timer_60usec_done = '0') then
timer_60usec_count <= timer_60usec_count + 1;
Tiếp cận lập trình cho FPGA từ Spartan -3 182

end if;
end if;

if (timer_60usec_count = (TIMER_60USEC_VALUE_PP - 1)) then


timer_60usec_done <= '1';
else
timer_60usec_done <= '0';
end if;
end process;

-- Tạo tín hiệu 5 us


timer5usec : process(clk, enable_timer_5usec, timer_5usec_count )
begin
if clk'event and clk = '0' then
if (enable_timer_5usec = '0') then
timer_5usec_count <= "000000";
elsif (timer_5usec_done = '0') then
timer_5usec_count <= timer_5usec_count + 1;
end if;
end if;
if( timer_5usec_count = (TIMER_5USEC_VALUE_PP - 1)) then
timer_5usec_done <= '1';
else
timer_5usec_done <= '0';
end if;
end process;
--Quét các phím đặc biệt:
special_scan : process(clk, reset, rx_output_event, rx_shifting_done, extended,
released )
Tiếp cận lập trình cho FPGA từ Spartan -3 183

begin
if clk'event and clk='0' then
if (reset = '1') or (rx_output_event = '1') then
hold_extended <= '0';
hold_released <= '0';
else
if (rx_shifting_done = '1') and (extended = '1') then
hold_extended <= '1';
end if;
if (rx_shifting_done = '1') and (released = '1') then
hold_released <= '1';
end if;
end if;
end if;
end process;
--Dịch trái
left_shift_proc : process(clk, reset, q, rx_shifting_done, hold_released )
begin
if clk'event and clk = '0' then
if (reset = '1') then
left_shift_key <= '0';
elsif (q(8 downto 1) = LEFT_SHIFT) and
(rx_shifting_done = '1') and
(hold_released = '0') then
left_shift_key <= '1';
elsif (q(8 downto 1) = LEFT_SHIFT) and
(rx_shifting_done = '1') and
(hold_released = '1') then
left_shift_key <= '0';
Tiếp cận lập trình cho FPGA từ Spartan -3 184

end if;
end if;
end process;
--Dịch phải
right_shift_proc : process(clk, reset, q, rx_shifting_done, hold_released )
begin
if clk'event and clk = '0' then
if (reset = '1') then
right_shift_key <= '0';
elsif (q(8 downto 1) = RIGHT_SHIFT) and
(rx_shifting_done = '1') and
(hold_released = '0') then
right_shift_key <= '1';
elsif (q(8 downto 1) = RIGHT_SHIFT) and
(rx_shifting_done = '1') and
(hold_released = '1') then
right_shift_key <= '0';
end if;
end if;
end process;

shift_key_on <= left_shift_key or right_shift_key;


rx_shift_key_on <= shift_key_on;
--Xử lý phím Ctr
ctrl_proc : process(clk, reset, q, rx_shifting_done, hold_released )
begin
if clk'event and clk = '0' then
if (reset = '1') then
ctrl_key_on <= '0';
Tiếp cận lập trình cho FPGA từ Spartan -3 185

elsif (q(8 downto 1) = CTRL_CODE) and


(rx_shifting_done = '1') and
(hold_released = '0') then
ctrl_key_on <= '1';
elsif (q(8 downto 1) = CTRL_CODE) and
(rx_shifting_done = '1') and
(hold_released = '1') then
ctrl_key_on <= '0';
end if;
end if;
end process;

--
-- Phím Caplock
--
caps_proc : process(clk, reset, q, rx_shifting_done, hold_released,
caps_key_on )
begin
if clk'event and clk = '0' then
if (reset = '1') then
caps_key_on <= '0';
elsif (q(8 downto 1) = CAPS_CODE) and
(rx_shifting_done = '1') and
(hold_released = '0') then
caps_key_on <= not caps_key_on;
end if;
end if;
end process;
Tiếp cận lập trình cho FPGA từ Spartan -3 186

special_scan_proc : process(clk, reset, hold_extended, hold_released, q, ascii,


ctrl_key_on )
begin
if clk'event and clk = '0' then
if (reset = '1') then
rx_extended <= '0';
rx_released <= '0';
-- rx_scan_code <= "00000000";
rx_ascii <= "00000000";
elsif (rx_output_strobe = '1') then
rx_extended <= hold_extended;
rx_released <= hold_released;
-- rx_scan_code <= q(8 downto 1);
elsif ctrl_key_on = '1' then
rx_ascii <= ascii and x"1f";
else
rx_ascii <= ascii;
end if;
end if;
end process;
rx_output_proc : process( clk, reset, rx_shifting_done, rx_output_strobe,
extended, released, q, ascii, rx_read )
begin
if (rx_shifting_done = '1') and (extended = '0') and (released = '0') then
rx_output_event <= '1';
else
rx_output_event <= '0';
end if;
Tiếp cận lập trình cho FPGA từ Spartan -3 187

if clk'event and clk = '0' then


if reset = '1' then
rx_output_strobe <= '0';
elsif (rx_shifting_done = '1') and
(rx_output_strobe = '0') and
(extended = '0') and
(released = '0') and
(hold_released = '0' ) and
(ascii /= x"00" ) then
-- ((TRAP_SHIFT_KEYS_PP = 0) or
-- ( (q(8 downto 1) /= RIGHT_SHIFT) and
-- (q(8 downto 1) /= LEFT_SHIFT) and
-- (q(8 downto 1) /= CTRL_CODE) ) )then
rx_output_strobe <= '1';
elsif rx_read = '1' then
rx_output_strobe <= '0';
end if;
end if;
rx_data_ready <= rx_output_strobe;
end process;
--Giải mã sang ASCII
shift_key_plus_code <= shift_key_on & caps_key_on & q(7 downto 1);

--shift_map : process( shift_key_plus_code )


--begin
-- case shift_key_plus_code is
-- when x"066" => ascii <= x"08"; -- Backspace ("backspace" key)
-- when x"166" => ascii <= x"08"; -- Backspace ("backspace" key)
-- when x"00d" => ascii <= x"09"; -- Horizontal Tab
Tiếp cận lập trình cho FPGA từ Spartan -3 188

-- when x"10d" => ascii <= x"09"; -- Horizontal Tab


-- when x"05a" => ascii <= x"0d"; -- Phím Enter
-- when x"15a" => ascii <= x"0d";
-- when x"076" => ascii <= x"1b"; -- Escape ("esc" key)
-- when x"176" => ascii <= x"1b"; -- Escape ("esc" key)
-- when x"029" => ascii <= x"20"; -- Space
-- when x"129" => ascii <= x"20"; -- Space
-- when x"116" => ascii <= x"21"; -- !
-- when x"152" => ascii <= x"22"; -- "
-- when x"126" => ascii <= x"23"; -- #
-- when x"125" => ascii <= x"24"; -- $
-- when x"12e" => ascii <= x"25"; --
-- when x"13d" => ascii <= x"26"; --
-- when x"052" => ascii <= x"27"; --
-- when x"146" => ascii <= x"28"; --
-- when x"145" => ascii <= x"29"; --
-- when x"13e" => ascii <= x"2a"; -- *
-- when x"155" => ascii <= x"2b"; -- +
-- when x"041" => ascii <= x"2c"; -- ,
-- when x"04e" => ascii <= x"2d"; -- -
-- when x"049" => ascii <= x"2e"; -- .
-- when x"04a" => ascii <= x"2f"; -- /
-- when x"045" => ascii <= x"30"; -- 0
-- when x"016" => ascii <= x"31"; -- 1
-- when x"01e" => ascii <= x"32"; -- 2
-- when x"026" => ascii <= x"33"; -- 3
-- when x"025" => ascii <= x"34"; -- 4
-- when x"02e" => ascii <= x"35"; -- 5
-- when x"036" => ascii <= x"36"; -- 6
Tiếp cận lập trình cho FPGA từ Spartan -3 189

-- when x"03d" => ascii <= x"37"; -- 7


-- when x"03e" => ascii <= x"38"; -- 8
-- when x"046" => ascii <= x"39"; -- 9
-- when x"14c" => ascii <= x"3a"; -- :
-- when x"04c" => ascii <= x"3b"; -- ;
-- when x"141" => ascii <= x"3c"; -- <
-- when x"055" => ascii <= x"3d"; -- =
-- when x"149" => ascii <= x"3e"; -- >
-- when x"14a" => ascii <= x"3f"; -- ?
-- when x"11e" => ascii <= x"40"; -- @
-- when x"11c" => ascii <= x"41"; -- A
-- when x"132" => ascii <= x"42"; -- B
-- when x"121" => ascii <= x"43"; -- C
-- when x"123" => ascii <= x"44"; -- D
-- when x"124" => ascii <= x"45"; -- E
-- when x"12b" => ascii <= x"46"; -- F
-- when x"134" => ascii <= x"47"; -- G
-- when x"133" => ascii <= x"48"; -- H
-- when x"143" => ascii <= x"49"; -- I
-- when x"13b" => ascii <= x"4a"; -- J
-- when x"142" => ascii <= x"4b"; -- K
-- when x"14b" => ascii <= x"4c"; -- L
-- when x"13a" => ascii <= x"4d"; -- M
-- when x"131" => ascii <= x"4e"; -- N
-- when x"144" => ascii <= x"4f"; -- O
-- when x"14d" => ascii <= x"50"; -- P
-- when x"115" => ascii <= x"51"; -- Q
-- when x"12d" => ascii <= x"52"; -- R
-- when x"11b" => ascii <= x"53"; -- S
Tiếp cận lập trình cho FPGA từ Spartan -3 190

-- when x"12c" => ascii <= x"54"; -- T


-- when x"13c" => ascii <= x"55"; -- U
-- when x"12a" => ascii <= x"56"; -- V
-- when x"11d" => ascii <= x"57"; -- W
-- when x"122" => ascii <= x"58"; -- X
-- when x"135" => ascii <= x"59"; -- Y
-- when x"11a" => ascii <= x"5a"; -- Z
-- when x"054" => ascii <= x"5b"; -- [
-- when x"05d" => ascii <= x"5c"; -- \
-- when x"05b" => ascii <= x"5d"; -- ]
-- when x"136" => ascii <= x"5e"; -- ^
-- when x"14e" => ascii <= x"5f"; -- _
-- when x"00e" => ascii <= x"60"; -- `
-- when x"01c" => ascii <= x"61"; -- a
-- when x"032" => ascii <= x"62"; -- b
-- when x"021" => ascii <= x"63"; -- c
-- when x"023" => ascii <= x"64"; -- d
-- when x"024" => ascii <= x"65"; -- e
-- when x"02b" => ascii <= x"66"; -- f
-- when x"034" => ascii <= x"67"; -- g
-- when x"033" => ascii <= x"68"; -- h
-- when x"043" => ascii <= x"69"; -- i
-- when x"03b" => ascii <= x"6a"; -- j
-- when x"042" => ascii <= x"6b"; -- k
-- when x"04b" => ascii <= x"6c"; -- l
-- when x"03a" => ascii <= x"6d"; -- m
-- when x"031" => ascii <= x"6e"; -- n
-- when x"044" => ascii <= x"6f"; -- o
-- when x"04d" => ascii <= x"70"; -- p
Tiếp cận lập trình cho FPGA từ Spartan -3 191

-- when x"015" => ascii <= x"71"; -- q


-- when x"02d" => ascii <= x"72"; -- r
-- when x"01b" => ascii <= x"73"; -- s
-- when x"02c" => ascii <= x"74"; -- t
-- when x"03c" => ascii <= x"75"; -- u
-- when x"02a" => ascii <= x"76"; -- v
-- when x"01d" => ascii <= x"77"; -- w
-- when x"022" => ascii <= x"78"; -- x
-- when x"035" => ascii <= x"79"; -- y
-- when x"01a" => ascii <= x"7a"; -- z
-- when x"154" => ascii <= x"7b"; -- {
-- when x"15d" => ascii <= x"7c"; -- |
-- when x"15b" => ascii <= x"7d"; -- }
-- when x"10e" => ascii <= x"7e"; -- ~
-- when x"071" => ascii <= x"7f"; -- Delete hoặc phím del
-- when x"171" => ascii <= x"7f"; -- //////////////////////////////////
-- when others => ascii <= x"ff";
-- end case;
--end process;
end my_ps2_keyboard;
--Dưới đây là tổng hợp và là giao diện bàn phím của chúng ta:
library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.numeric_std.all;
Tiếp cận lập trình cho FPGA từ Spartan -3 192

entity keyboard is
port(
clk : in std_logic;
rst : in std_logic;
cs : in std_logic;
rw : in std_logic;
addr : in std_logic;
data_in : in std_logic_vector(7 downto 0);
data_out : out std_logic_vector(7 downto 0);
irq : out std_logic;
kbd_clk : inout std_logic;
kbd_data : inout std_logic
);
end keyboard;

architecture my_keyboard of keyboard is

signal kbd_stat : std_logic_vector(7 downto 0);


signal kbd_ctrl : std_logic_vector(7 downto 6);
signal kbd_ascii_code : std_logic_vector(7 downto 0);
signal kbd_tx_data : std_logic_vector(7 downto 0);
signal kbd_read : std_logic;
signal kbd_write : std_logic;
signal kbd_write_ack : std_logic;
component ps2_keyboard_interface
port(
clk : in std_logic;
reset : in std_logic;
ps2_clk : inout std_logic;
Tiếp cận lập trình cho FPGA từ Spartan -3 193

ps2_data : inout std_logic;


rx_extended : out std_logic;
rx_released : out std_logic;
rx_shift_key_on : out std_logic;
-- rx_scan_code : out std_logic_vector(7 downto 0);
rx_ascii : out std_logic_vector(7 downto 0);
rx_data_ready : out std_logic;
rx_read : in std_logic;
tx_data : in std_logic_vector(7 downto 0);
tx_write : in std_logic;
tx_write_ack : out std_logic;
tx_error_no_keyboard_ack : out std_logic
);
end component;

begin
--Ánh xạ thành phần thành cổng.
my_ps2_keyboard_interface : ps2_keyboard_interface
port map(
clk => clk,
reset => rst,
ps2_clk => kbd_clk,
ps2_data => kbd_data,
rx_extended => kbd_stat(2),
rx_released => kbd_stat(3),
rx_shift_key_on => kbd_stat(4),
rx_ascii => kbd_ascii_code,
rx_data_ready => kbd_stat(0),
rx_read => kbd_read,
Tiếp cận lập trình cho FPGA từ Spartan -3 194

tx_data => kbd_tx_data,


tx_write => kbd_write,
tx_write_ack => kbd_write_ack,
tx_error_no_keyboard_ack => kbd_stat(5)
);

keyboard_strobe : process( clk, rst, cs, rw, kbd_stat )


begin
if( rst = '1' ) then
kbd_read <= '0';
elsif( clk'event and clk='0' ) then
if( cs='1' and rw='1' and addr='1' ) then
kbd_read <= '1';
elsif( kbd_stat(0)='1' ) then
kbd_read <= '0';
else
kbd_read <= kbd_read;
end if;
end if;
end process;

-- Đọc từ thanh ghi của bàn phím.


keyboard_read : process( addr, kbd_ascii_code, kbd_stat )
begin
if( addr = '1' ) then
data_out <= kbd_ascii_code;
else
data_out <= kbd_stat;
end if;
Tiếp cận lập trình cho FPGA từ Spartan -3 195

end process;
-- ghi vào thanh ghi bàn phím
keyboard_write : process( clk, rst, cs, rw, addr, data_in, kbd_tx_data,
kbd_write, kbd_write_ack )
begin
if(rst = '1' ) then
kbd_ctrl <= "00";
kbd_tx_data <= "00000000";
kbd_write <= '0';
elsif( clk'event and clk='0' ) then
if( cs='1' and rw='0' ) then
if( addr='1' ) then
kbd_tx_data <= data_in;
kbd_ctrl <= kbd_ctrl;
kbd_write <= '1';
else
kbd_tx_data <= kbd_tx_data;
kbd_ctrl <= data_in(7 downto 6);
kbd_write <= kbd_write;
end if;
elsif( kbd_write_ack='1' ) then
kbd_write <= '0';
else
kbd_write <= kbd_write;
end if;
kbd_stat(1) <= not kbd_write;
end if;
end process;
Tiếp cận lập trình cho FPGA từ Spartan -3 196

keyboard_interrupt : process( kbd_ctrl, kbd_stat )


begin
kbd_stat(6) <= kbd_ctrl(6);
kbd_stat(7) <= kbd_ctrl(7) and kbd_stat(0);
irq <= kbd_stat(7);
end process;
end my_keyboard;
Dưới đây là sơ đồ logic mức truyền thanh ghi của thực thể keyboard

Hình 5.7. Sơ đồ logic mức truyền thanh ghi của keyboard(mức 1)


Tiếp cận lập trình cho FPGA từ Spartan -3 197

Hình 5.8.Sơ đồ logic mức truyền thanh ghi của keyboard (mức 2)

Hình 5.9. Sơ đồ logic mức truyền thanh ghi của keyboard (mức 3)
Tiếp cận lập trình cho FPGA từ Spartan -3 198