Khi các ứng dụng trở nên phức tạp, việc quản lý luồng thực thi tuần tự trở nên khó khăn. Bài học này sẽ giới thiệu kiến trúc Máy Trạng Thái (State Machine), một mô hình thiết kế nền tảng để xây dựng các hệ thống FPGA có trật tự, dễ bảo trì và mạnh mẽ.
Phần 1: Giới Thiệu về Kiến Trúc Máy Trạng Thái
Hầu hết các ứng dụng FPGA không chỉ thực hiện một tác vụ lặp đi lặp lại. Chúng thường phải trải qua nhiều giai đoạn khác nhau: khởi tạo, chờ đợi một sự kiện, xử lý dữ liệu, báo cáo kết quả, xử lý lỗi, v.v. Việc quản lý các giai đoạn và sự chuyển đổi giữa chúng là nhiệm vụ của một máy trạng thái.
Máy Trạng Thái là gì?
Một máy trạng thái là một mô hình tính toán mô tả hành vi của một hệ thống có một số lượng hữu hạn các “trạng thái”. Tại bất kỳ thời điểm nào, hệ thống chỉ có thể ở trong một trạng thái duy nhất. Nó sẽ chuyển từ trạng thái này sang trạng thái khác dựa trên các điều kiện đầu vào hoặc các sự kiện nội tại. Đây được gọi là “sự chuyển đổi” (transition).
A. Các Thành Phần Cốt Lõi của một Máy Trạng Thái
- Các Trạng Thái (States): Mỗi trạng thái đại diện cho một giai đoạn hoạt động cụ thể của hệ thống. Bên trong mỗi trạng thái, hệ thống thực hiện một tập hợp các hành động (actions) nhất định. Ví dụ: trạng thái “Khởi tạo”, trạng thái “Chờ Trigger”, trạng thái “Thu thập Dữ liệu”.
- Sự Chuyển Đổi (Transitions): Đây là các quy tắc quyết định khi nào và làm thế nào hệ thống chuyển từ trạng thái hiện tại sang trạng thái tiếp theo. Sự chuyển đổi được kích hoạt bởi các điều kiện (conditions) – ví dụ, một tín hiệu đầu vào thay đổi, một bộ đếm đạt đến giá trị ngưỡng, v.v.
- Hành Động (Actions): Là các công việc được thực hiện khi hệ thống đang ở trong một trạng thái cụ thể.
B. Triển Khai Máy Trạng Thái Kinh Điển trong LabVIEW FPGA
Kiến trúc máy trạng thái được triển khai trong LabVIEW một cách tự nhiên và hiệu quả bằng sự kết hợp của ba thành phần cơ bản:
- Vòng lặp While Loop: Cung cấp “động cơ” cho máy trạng thái, cho phép nó chạy liên tục, lặp đi lặp lại việc kiểm tra và thực thi.
- Cấu trúc Case Structure: Đóng vai trò là “bộ não” của máy trạng thái. Mỗi case (trường hợp) trong cấu trúc này tương ứng với một trạng thái. Mã nguồn bên trong một case chính là các “hành động” của trạng thái đó.
- Thanh ghi dịch (Shift Register): Đóng vai trò là “bộ nhớ” của máy trạng thái. Nó lưu giữ trạng thái hiện tại của hệ thống. Sau mỗi lần lặp, mã nguồn sẽ quyết định trạng thái tiếp theo và ghi giá trị đó vào thanh ghi dịch để vòng lặp tiếp theo có thể thực thi đúng trạng thái.
Việc sử dụng kiểu dữ liệu Enum (Enumerated Type) để định nghĩa các trạng thái là một thực hành tốt nhất, giúp mã nguồn trở nên cực kỳ dễ đọc và dễ bảo trì.
Phần 2: Ứng Dụng – Xây Dựng Logic Tùy Chỉnh
Lý thuyết về máy trạng thái trở nên hữu ích nhất khi được áp dụng để xây dựng các khối logic tùy chỉnh, giải quyết các bài toán cụ thể mà các hàm có sẵn không đáp ứng được. Dưới đây là một số ví dụ điển hình.
A. Xây Dựng Bộ Trigger Theo Mẫu Kỹ Thuật Số (Digital Pattern Trigger)
Giả sử chúng ta cần một hệ thống chỉ bắt đầu thu thập dữ liệu khi một mẫu bit cụ thể xuất hiện trên nhiều đường tín hiệu số. Chúng ta có thể xây dựng một máy trạng thái đơn giản cho việc này.
- Trạng thái 1:
Wait for Pattern
- Hành động: Liên tục đọc các đường I/O kỹ thuật số.
- Chuyển đổi: So sánh giá trị đọc được với một mẫu bit (pattern) đã định trước. Nếu trùng khớp, chuyển sang trạng thái `Acquire Data`. Nếu không, giữ nguyên ở trạng thái `Wait for Pattern`.
- Trạng thái 2:
Acquire Data
- Hành động: Bắt đầu quá trình thu thập dữ liệu (ví dụ: ghi dữ liệu vào FIFO).
- Chuyển đổi: Sau khi thu thập đủ số lượng mẫu, chuyển sang trạng thái `Idle` hoặc `Wait for Pattern` để chờ lần trigger tiếp theo.
B. Xây Dựng Bộ Đếm Sự Kiện (Event Counter)
Một bộ đếm đơn giản có thể được tạo bằng vòng lặp, nhưng một máy trạng thái cho phép chúng ta thêm các chức năng như Start, Stop, và Reset một cách có cấu trúc.
- Trạng thái 1:
Idle
- Hành động: Không làm gì, giữ giá trị đếm không đổi.
- Chuyển đổi: Nếu nhận được lệnh `Start`, chuyển sang trạng thái `Counting`.
- Trạng thái 2:
Counting
- Hành động: Giám sát một đường tín hiệu đầu vào. Mỗi khi phát hiện một sườn lên (rising edge), tăng giá trị của một thanh ghi đếm.
- Chuyển đổi: Nếu nhận được lệnh `Stop`, chuyển về trạng thái `Idle`. Nếu nhận được lệnh `Reset`, chuyển sang trạng thái `Resetting`.
- Trạng thái 3:
Resetting
- Hành động: Đặt giá trị của thanh ghi đếm về 0.
- Chuyển đổi: Chuyển ngay lập tức về trạng thái `Idle`.
C. Xây Dựng Bộ Đo Độ Rộng Xung (Pulse Width Measurement)
Để đo thời gian một tín hiệu ở mức cao, chúng ta có thể sử dụng máy trạng thái kết hợp với hàm `Tick Count`.
- Trạng thái 1:
Wait for Rising Edge
- Hành động: Giám sát tín hiệu đầu vào.
- Chuyển đổi: Khi phát hiện sườn lên, ghi lại giá trị `Tick Count` hiện tại (thời gian bắt đầu) và chuyển sang trạng thái `Wait for Falling Edge`.
- Trạng thái 2:
Wait for Falling Edge
- Hành động: Tiếp tục giám sát tín hiệu.
- Chuyển đổi: Khi phát hiện sườn xuống (falling edge), ghi lại giá trị `Tick Count` hiện tại (thời gian kết thúc) và chuyển sang trạng thái `Calculate Duration`.
- Trạng thái 3:
Calculate Duration
- Hành động: Lấy thời gian kết thúc trừ đi thời gian bắt đầu để có độ rộng xung. Ghi kết quả này vào một thanh ghi hoặc FIFO.
- Chuyển đổi: Chuyển về trạng thái `Wait for Rising Edge` để chuẩn bị cho lần đo tiếp theo.