Khi viết VHDL, tôi khuyên bạn nên sử dụng std_logic_vector (slv) thay vì số nguyên (int) cho TÍN HIỆU . (Mặt khác, sử dụng int cho generic, một số hằng và một số biến có thể rất hữu ích.) Nói một cách đơn giản, nếu bạn khai báo tín hiệu của kiểu int hoặc phải chỉ định một phạm vi cho một số nguyên thì có lẽ bạn đang làm Có gì đó không đúng.
Vấn đề với int là lập trình viên VHDL không biết biểu diễn logic bên trong của int là gì và vì vậy chúng ta không thể tận dụng lợi thế của nó. Ví dụ, nếu tôi xác định một int của phạm vi từ 1 đến 10, tôi không biết trình biên dịch mã hóa các giá trị đó như thế nào. Hy vọng rằng nó sẽ được mã hóa thành 4 bit, nhưng chúng ta không biết nhiều hơn thế. Nếu bạn có thể thăm dò các tín hiệu bên trong FPGA, nó có thể được mã hóa thành "0001" thành "1010" hoặc được mã hóa thành "0000" đến "1001". Cũng có thể nó được mã hóa theo cách hoàn toàn vô nghĩa đối với con người chúng ta.
Thay vào đó chúng ta chỉ nên sử dụng slv thay vì int, vì sau đó chúng ta có quyền kiểm soát mã hóa và cũng có quyền truy cập trực tiếp vào các bit riêng lẻ. Có quyền truy cập trực tiếp là rất quan trọng, như bạn sẽ thấy sau.
Chúng ta chỉ có thể chuyển một int thành slv bất cứ khi nào chúng ta cần truy cập vào các bit riêng lẻ, nhưng điều đó thực sự rất lộn xộn, rất nhanh. Điều đó giống như nhận được điều tồi tệ nhất của cả hai thế giới thay vì tốt nhất của cả hai thế giới. Mã của bạn sẽ khó để trình biên dịch tối ưu hóa và hầu như bạn không thể đọc được. Tôi không khuyên bạn điều này.
Vì vậy, như tôi đã nói, với slv, bạn có quyền kiểm soát mã hóa bit và truy cập trực tiếp vào các bit. Vậy bạn có thể làm gì với điều này? Tôi sẽ chỉ cho bạn một vài ví dụ. Giả sử bạn cần xuất xung một lần sau mỗi 4.294.000.000 đồng hồ. Đây là cách bạn sẽ làm điều này với int:
signal count :integer range 0 to 4293999999; -- a 32 bit integer
process (clk)
begin
if rising_edge(clk) then
if count = 4293999999 then -- The important line!
count <= 0;
pulse <= '1';
else
count <= count + 1;
pulse <= '0';
end if;
end if;
end process;
Và cùng một mã sử dụng slv:
use ieee.numeric_std.all;
signal count :std_logic_vector (32 downto 0); -- a 33 bit integer, one extra bit!
process (clk)
begin
if rising_edge(clk) then
if count(count'high)='1' then -- The important line!
count <= std_logic_vector(4293999999-1,count'length);
pulse <= '1';
else
count <= count - 1;
pulse <= '0';
end if;
end if;
end process;
Hầu hết các mã này là giống hệt nhau giữa int và slv, ít nhất là theo nghĩa kích thước và tốc độ của logic kết quả. Tất nhiên một người đang đếm ngược và người kia đang đếm ngược, nhưng điều đó không quan trọng đối với ví dụ này.
Sự khác biệt là trong "dòng quan trọng".
Với ví dụ int, điều này sẽ dẫn đến một bộ so sánh 32 đầu vào. Với 4 LUT đầu vào mà Xilinx Spartan-3 sử dụng, điều này sẽ đòi hỏi 11 LUT và 3 cấp độ logic. Một số trình biên dịch có thể chuyển đổi điều này thành phép trừ sẽ sử dụng chuỗi mang và trải rộng tương đương với 32 LUT nhưng có thể chạy nhanh hơn 3 cấp độ logic.
Với ví dụ slv, không có so sánh 32 bit nên "không LUT, mức logic bằng không". Hình phạt duy nhất là bộ đếm của chúng tôi là thêm một chút. Bởi vì thời gian bổ sung cho bit bổ sung này là tất cả trong chuỗi mang, nên có độ trễ thời gian bổ sung "gần như bằng không".
Tất nhiên đây là một ví dụ cực đoan, vì hầu hết mọi người sẽ không sử dụng bộ đếm 32 bit theo cách này. Nó áp dụng cho các quầy nhỏ hơn, nhưng sự khác biệt sẽ ít kịch tính hơn mặc dù vẫn còn đáng kể.
Đây chỉ là một ví dụ về cách sử dụng slv trên int để có thời gian nhanh hơn. Có nhiều cách khác để sử dụng slv-- chỉ cần một số trí tưởng tượng.
Cập nhật: Đã thêm nội dung để giải quyết ý kiến của Martin Thompson về việc sử dụng int với "if (Count-1) <0"
(Lưu ý: Tôi giả sử bạn có nghĩa là "nếu tính <0", vì điều đó sẽ làm cho nó tương đương với phiên bản slv của tôi hơn và loại bỏ sự cần thiết cho phép trừ thêm đó.)
Trong một số trường hợp, điều này có thể tạo ra việc thực hiện logic dự định nhưng nó không được đảm bảo để hoạt động mọi lúc. Nó sẽ phụ thuộc vào mã của bạn và cách trình biên dịch của bạn mã hóa giá trị int.
Tùy thuộc vào trình biên dịch của bạn và cách bạn chỉ định phạm vi int của mình, hoàn toàn có thể giá trị int bằng 0 không mã hóa thành một vectơ bit "0000 ... 0000" khi nó đưa nó vào logic Logic. Để biến thể của bạn hoạt động, nó phải mã hóa thành "0000 ... 0000".
Ví dụ: giả sử bạn xác định một int có phạm vi từ -5 đến +5. Bạn đang mong đợi giá trị 0 được mã hóa thành 4 bit như "0000" và +5 là "0101" và -5 là "1011". Đây là sơ đồ mã hóa bổ sung twos điển hình.
Nhưng đừng cho rằng trình biên dịch sẽ sử dụng bổ sung twos. Mặc dù khác thường, bổ sung có thể dẫn đến logic "tốt hơn". Hoặc, trình biên dịch có thể sử dụng một loại mã hóa "thiên vị" trong đó -5 được mã hóa thành "0000", 0 là "0101" và +5 là "1010".
Nếu mã hóa của int là "chính xác" thì trình biên dịch có thể sẽ suy ra phải làm gì với bit carry. Nhưng nếu nó không chính xác thì logic kết quả sẽ rất kinh khủng.
Có thể việc sử dụng một int theo cách này có thể dẫn đến kích thước và tốc độ logic hợp lý, nhưng nó không phải là một sự đảm bảo. Chuyển sang một trình biên dịch khác (ví dụ XST sang Synopsis) hoặc chuyển sang một kiến trúc FPGA khác có thể gây ra sự cố chính xác xảy ra.
Chưa ký / Đã ký so với slv vẫn là một cuộc tranh luận khác. Bạn có thể cảm ơn ủy ban chính phủ Hoa Kỳ đã cho chúng tôi rất nhiều lựa chọn trong VHDL. :) Tôi sử dụng slv vì đó là tiêu chuẩn để giao tiếp giữa các mô-đun và lõi. Ngoài ra, và một số trường hợp khác trong mô phỏng, tôi không nghĩ rằng có một lợi ích to lớn khi sử dụng slv trên chữ ký / không dấu. Tôi cũng không chắc chắn nếu tín hiệu đã ký / không dấu hỗ trợ ba tín hiệu đã nêu.