Tự viết driver VGA bằng SystemVerilog trên FPGA: từ xung đồng bộ đến hiển thị hình ảnh

Công nghệ20 tháng 10, 2026·8 phút đọc

Một bài viết kỹ thuật chi tiết ghi lại quá trình xây dựng driver VGA từ con số 0 bằng SystemVerilog trên bo mạch FPGA DE10-lite, từ việc tạo xung HSync/VSync, đếm pixel đến hiển thị hình ảnh có hỗ trợ kênh alpha. Đây là ví dụ thực tế tuyệt vời về thiết kế logic số và lập trình phần cứng.

Tự viết driver VGA bằng SystemVerilog trên FPGA: từ xung đồng bộ đến hiển thị hình ảnh

Tự viết driver VGA bằng SystemVerilog trên FPGA: từ xung đồng bộ đến hiển thị hình ảnh

Việc điều khiển một màn hình hiển thị tưởng chừng là công việc của các kỹ sư phần cứng chuyên nghiệp, nhưng thực tế nó lại là một bài học nhập môn đầy thú vị trong các phòng lab thiết kế logic số. Một bài viết kỹ thuật gần đây đã chia sẻ chi tiết toàn bộ quá trình tự xây dựng một driver VGA bằng SystemVerilog trên bo mạch FPGA, từ việc tạo ra các xung đồng bộ tín hiệu đến hiển thị hình ảnh có kênh trong suốt.

Bo mạch FPGA và màn hình VGA hiển thị hình ảnhBo mạch FPGA và màn hình VGA hiển thị hình ảnh

Bài viết tập trung vào trải nghiệm thực tế trong phòng lab Digital Logic Design – một trong những môn học được tác giả yêu thích nhất. Hai năm trước, những buổi lab cuối kỳ của môn học này chính là nơi sinh viên được giao nhiệm vụ xây dựng một driver VGA hoàn chỉnh. Nghe có vẻ đáng sợ, nhưng đây hóa ra lại là một dự án hoàn toàn khả thi, dù thời gian gỡ lỗi chiếm không ít.

VGA là gì và vì sao nó lại "khó" đến vậy?

VGA (Video Graphics Array) là một chuẩn hiển thị ra đời cùng với dòng máy IBM PS/2 vào năm 1987. Giao thức này khá hợp lý nếu bạn hiểu cách các màn hình thời đó vận hành.

Công nghệ hiển thị thời bấy giờ là CRT (Cathode Ray Tubes) – những màn hình lớn với mặt kính cong, nặng tới hơn 36 kg. Nguyên lý hoạt động của chúng là bắn một chùm tia điện tử lên lớp phốt-pho, khiến các electron tán xạ thành ánh sáng khả kiến. Vì chỉ dùng một chùm tia duy nhất nên màn hình phải vẽ từng điểm ảnh một, khác hoàn toàn với màn hình hiện đại vốn hiển thị cả khung hình cùng lúc.

Do chùm tia phải quét từ dòng này sang dòng khác, nó cần thời gian để di chuyển cả theo phương ngang lẫn phương dọc. Chính hành vi quét này được điều khiển bởi hai tín hiệu HSync (đồng bộ ngang) và VSync (đồng bộ dọc).

Điều khiến màn hình trông như được lấp đầy hoàn toàn chính là hiện tượng lưu ảnh của mắt người. Trên thực tế, tại mỗi thời điểm chỉ có một điểm ảnh "bật", nhưng mắt ta cảm nhận được cả bức ảnh nhờ tốc độ quét cực nhanh.

Cách tiếp cận triển khai

Tần số điểm ảnh và xung nhịp

Theo các bảng thông số từ trang TinyVGA nổi tiếng, để xuất toàn bộ điểm ảnh kịp thời ở tần số quét 60 Hz, ta cần xung nhịp khoảng 25,175 MHz. May mắn thay, bo mạch DE10-lite của hãng terasIC mà khóa học sử dụng có sẵn xung nhịp 50 MHz, có thể dùng làm nguồn tham chiếu.

Điểm hay là vì chúng ta kiểm soát các tín hiệu HSync và VSync, màn hình có thể được đồng bộ lại theo xung nhịp không chuẩn của mình, nên không cần lo lắng về việc lệch nhịp so với con số 25,175 MHz chính xác.

Một trong những gợi ý thiết kế được đưa ra là xây dựng một module SystemVerilog cơ bản có chức năng đảo tín hiệu sau mỗi hai chu kỳ xung nhịp – về bản chất là một bộ chia tần số thô. Thủ thuật đơn giản này đưa xung nhịp từ 50 MHz xuống còn 25 MHz cho VGA.

Đếm pixel và vùng hiển thị

Vấn đề là không thể lúc nào cũng xuất tín hiệu màu ra màn hình. Trên một màn CRT thật, làm vậy có thể gây hỏng thành ống vì chùm electron sẽ bắn vào vách panel thay vì lớp phốt-pho.

Để biết khi nào bật/tắt tín hiệu màu và khi nào kéo HSync, VSync xuống mức thấp, ta cần các bộ đếm theo dõi vị trí hiện tại trong một khung hình.

Về phương ngang, vùng hiển thị gồm 640 pixel, cộng thêm vùng ngoài màn hình: Front Porch 16, Sync Pulse 96 và Back Porch 16 – tổng cộng 800 "pixel", vừa vặn trong 10 bit. Về phương dọc, vùng hiển thị gồm 480 dòng, cộng thêm Front Porch 33, Sync Pulse 2 và Back Porch 10 – tổng cộng 525 "pixel".

Bằng cách dùng thiết kế có tham số hóa, tác giả chỉ cần một module duy nhất giữ đầu ra ở mức thấp trong một số chu kỳ và kéo lên cao cho đến khi reset ở một giá trị đếm khác. Dữ liệu hình ảnh chỉ được xuất trong giai đoạn hiển thị: từ 35 đến 784 theo phương ngang và từ 33 đến 520 theo phương dọc.

Sơ đồ khối và mô phỏng tín hiệu VGASơ đồ khối và mô phỏng tín hiệu VGA

Từ 10 công tắc sang 4.096 màu

Bo DE10-lite chỉ có 10 công tắc, nhưng dải màu đầy đủ của VGA là 4.096 màu (3 kênh màu, mỗi kênh 4 bit, tổng 12 bit). Giải pháp là dùng một bộ ghép kênh (mux) để ánh xạ ít công tắc hơn sang dải màu rộng hơn: cặp bit 00 thành 0000, 01 thành 0101, 10 thành 1010 và 11 thành 1111.

Tác giả ước tính đã dành hơn 10 giờ chỉ để chỉnh thời gian cho đúng, cùng vài lần "đuổi theo trăng" chỉ để rồi nhận ra phải làm lại một phần thiết kế.

Khoảnh khắc "Nó chạy rồi!" trên phần cứng

Mô phỏng chỉ đưa bạn đi được đến một mức nhất định; cảm giác thấy màn hình thực sự sáng lên lại là một trải nghiệm khác hẳn. Chỉ cần vài phép gán chân đơn giản cho cổng VGA, và một màu cam "beaver" rực rỡ đã hiện lên trên màn hình VGA – minh chứng cho driver hoạt động thành công.

Hình ảnh hiển thị trên màn hình VGA từ driver tự viếtHình ảnh hiển thị trên màn hình VGA từ driver tự viết

Hiển thị hình ảnh và hỗ trợ kênh alpha

Buổi lab thứ hai của dự án là làm sao hiển thị một hình ảnh thay vì chỉ một màu đơn sắc. Nội dung cốt lõi là nạp hình ảnh vào ROM của bo FPGA và hiển thị nó khi cần – trong trường hợp này là ở góc trên bên trái màn hình.

Trên thực tế, việc này bao gồm tạo một tệp khởi tạo bộ nhớ từ hình ảnh mong muốn, với tác giả là nhân vật Asterix trong bộ phim hoạt hình Asterix và Obelix. Vì chương trình gốc viết bằng Python gặp vấn đề phụ thuộc, tác giả quyết định viết lại bằng Go để hiểu rõ hơn về định dạng tệp và tăng tính đa nền tảng cho các sinh viên khác. (Điều này cũng giúp tác giả có được công việc trợ giảng cho khóa học.)

Từ đó, công việc khá đơn giản: các bộ đếm ngang và dọc (dùng cho HSync và VSync) về cơ bản chính là số thứ tự pixel, chỉ cần dùng một độ lệch cơ bản vào ROM là có thể truy cập hình ảnh thay vì màu.

Để lấy điểm cộng, tác giả đã triển khai hỗ trợ trong suốt dựa trên dữ liệu kênh alpha được lưu trong byte cuối của mỗi pixel. Có ba cách đã được thử: cách đầu tiên chỉ kiểm tra pixel có trong suốt hay không rồi chỉ hiển thị khi hoàn toàn đục – kết quả chạy được nhưng hình bị vỡ thành khối ở các cạnh. Hai cách sau cố gắng làm alpha compositing thật sự. Vì phép nhân được cho là tốn tài nguyên cổng logic, tác giả ban đầu thử một mẹo dịch bit để "lách" – chạy được nhưng có vấn đề làm tròn khiến màu nền trông hơi khác. Phiên bản cuối cùng dùng phép nhân thật sự đã được nộp để chấm điểm.

Hình ảnh Asterix hiển thị với kênh alpha trên driver VGA tự chếHình ảnh Asterix hiển thị với kênh alpha trên driver VGA tự chế

Góc nhìn cho người học công nghệ tại Việt Nam

Câu chuyện này là một ví dụ tuyệt vời cho thấy thiết kế logic số và lập trình phần cứng không hề xa vời. Với các bạn sinh viên ngành điện tử, kỹ thuật máy tính tại Việt Nam, những dự án như thế này – dù chỉ ở quy mô môn học – là cách rèn luyện tư duy về tín hiệu, thời gian và tối ưu tài nguyên phần cứng rất hiệu quả.

Điều đáng chú ý là nền tảng FPGA như DE10-lite ngày càng dễ tiếp cận, và các chuẩn hiển thị "cổ điển" như VGA lại là môi trường lý tưởng để học vì tính minh bạch và dễ quan sát của nó.

Nếu bạn quan tâm đến lĩnh vực hệ thống nhúng hay thiết kế vi mạch, việc tự tay điều khiển một màn hình bằng FPGA là bước khởi đầu đầy cảm hứng để tiến xa hơn trong lĩnh vực phần cứng.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗