Chia sẻ:
Notifications
Clear all

Viết đoạn code Verilog để tạo bộ chia tần (frequency divider).

2 Bài viết
2 Thành viên
0 Reactions
77 Lượt xem
(@admin)
Thành Viên Moderator
Tham gia: 6 năm trước
Bài viết: 27
Topic starter  

Viết đoạn code Verilog để tạo bộ chia tần (frequency divider).


   
Trích dẫn
Thẻ chủ đề
(@Anonymous)
New Member Khách
Tham gia: 1 giây trước
Bài viết: 0
 

```verilog
module frequency_divider (
input wire clk_in, // Clock đầu vào
input wire rst_n, // Reset (active low)
input wire divider, // divisor (Ví dụ: divider = 1: chia 2, divider = 2: chia 3, ...)
output reg clk_out // Clock đầu ra đã chia tần
);

reg [31:0] count; // Bộ đếm để theo dõi các xung clock

always @(posedge clk_in or negedge rst_n) begin
if (!rst_n) begin
count <= 0;
clk_out <= 0;
end else begin
if (count == (divider - 1)) begin // So sánh bộ đếm với divisor
count <= 0; // Reset bộ đếm
clk_out <= ~clk_out; // Đảo trạng thái clock đầu ra
end else begin
count <= count + 1; // Tăng bộ đếm
end
end
end

endmodule
```

**Giải thích code:**

* **`module frequency_divider (...)`**: Khai báo module Verilog có tên `frequency_divider`.
* **`input wire clk_in`**: Đầu vào clock gốc.
* **`input wire rst_n`**: Đầu vào reset tích cực mức thấp (active-low). Khi `rst_n` là 0, module sẽ reset.
* **`input wire divider`**: Đầu vào xác định hệ số chia. Ví dụ: `divider = 1` sẽ chia tần số cho 2 (một nửa tần số), `divider = 2` sẽ chia tần số cho 3, v.v. **Quan trọng:** Giá trị của `divider` phải lớn hơn 0.
* **`output reg clk_out`**: Đầu ra clock đã chia tần số. Khai báo là `reg` vì giá trị của nó sẽ được gán bên trong khối `always`.
* **`reg [31:0] count`**: Một thanh ghi 32-bit được sử dụng làm bộ đếm. Kích thước này đủ lớn cho hầu hết các ứng dụng. Bạn có thể điều chỉnh kích thước nếu cần thiết.
* **`always @(posedge clk_in or negedge rst_n)`**: Khối `always` này được kích hoạt khi có cạnh lên của tín hiệu clock đầu vào (`clk_in`) hoặc cạnh xuống của tín hiệu reset (`rst_n`).
* **`if (!rst_n) begin ... end`**: Kiểm tra tín hiệu reset. Nếu `rst_n` là 0 (active low), bộ đếm (`count`) và clock đầu ra (`clk_out`) được reset về 0.
* **`else begin ... end`**: Nếu tín hiệu reset không hoạt động, thực hiện các thao tác chia tần số.
* **`if (count == (divider - 1)) begin ... end`**: Kiểm tra xem bộ đếm đã đạt đến giá trị `divider - 1` chưa. Ví dụ: nếu `divider = 2` (chia cho 3), điều kiện này sẽ đúng khi `count` bằng 1. **Lưu ý quan trọng:** Khi `count` bằng `divider - 1`, ta thực hiện các bước sau:
* **`count <= 0;`**: Reset bộ đếm về 0.
* **`clk_out <= ~clk_out;`**: Đảo trạng thái của clock đầu ra. Đây là cách tạo ra tín hiệu clock đã chia tần số.
* **`else begin count <= count + 1; end`**: Nếu bộ đếm chưa đạt đến giá trị `divider - 1`, tăng bộ đếm lên 1.

**Cách sử dụng:**

1. **Khai báo và khởi tạo:** Trong module top-level của bạn, khởi tạo module `frequency_divider` này.
2. **Kết nối các tín hiệu:** Kết nối các chân `clk_in`, `rst_n` và `divider` với các tín hiệu thích hợp trong thiết kế của bạn.
3. **Sử dụng `clk_out`:** Sử dụng tín hiệu `clk_out` làm tín hiệu clock đã chia tần số trong các phần khác của thiết kế của bạn.

**Ví dụ sử dụng trong module top-level:**

```verilog
module top_level (
input wire sys_clk, // System clock
input wire sys_rst_n, // System reset (active low)
output wire slower_clk // Clock đã chia tần số
);

wire divider_value;

// Thay đổi giá trị này để điều chỉnh hệ số chia
assign divider_value = 2; // Chia cho 3 (divider = 2)

frequency_divider freq_div (
.clk_in (sys_clk),
.rst_n (sys_rst_n),
.divider (divider_value),
.clk_out (slower_clk)
);

// Các phần khác của thiết kế sử dụng slower_clk ở đây...

endmodule
```

**Những điều cần lưu ý:**

* **Độ chính xác:** Phương pháp này tạo ra một tín hiệu clock đã chia tần số, nhưng nó có thể không có duty cycle chính xác là 50% (tức là thời gian xung cao và xung thấp có thể không bằng nhau). Nếu duty cycle 50% là bắt buộc, bạn cần một thiết kế phức tạp hơn (ví dụ: sử dụng counter và flip-flop).
* **Giá trị của `divider`:** Phải đảm bảo rằng giá trị của `divider` luôn lớn hơn 0.
* **Clock domain crossing (CDC):** Nếu bạn sử dụng `clk_out` trong một clock domain khác với `clk_in`, bạn cần xử lý clock domain crossing một cách cẩn thận để tránh các vấn đề metastability. Sử dụng các kỹ thuật CDC tiêu chuẩn như double flip-flop synchronizers hoặc FIFO.
* **Tổng hợp và mô phỏng:** Hãy nhớ mô phỏng và tổng hợp đoạn code này để đảm bảo rằng nó hoạt động như mong đợi trong phần cứng thực tế.

**Phiên bản cải tiến (duty cycle ~50%):**

Để tạo ra một tín hiệu clock đầu ra với duty cycle gần 50%, bạn có thể sử dụng một biến `toggle` để đảm bảo rằng clock đầu ra chuyển đổi trạng thái một cách đối xứng.

```verilog
module frequency_divider_50 (
input wire clk_in,
input wire rst_n,
input wire [31:0] divider, // Sử dụng vector để truyền tham số
output reg clk_out
);

reg [31:0] count;
reg toggle;

always @(posedge clk_in or negedge rst_n) begin
if (!rst_n) begin
count <= 0;
clk_out <= 0;
toggle <= 0;
end else begin
if (count == ((divider - 1) / 2)) begin // Thay đổi điều kiện
count <= 0;
toggle <= ~toggle; // Đảo trạng thái toggle
clk_out <= toggle; // Gán toggle cho clk_out
end else begin
count <= count + 1;
end
end
end

endmodule
```

Trong phiên bản này, `clk_out` được gán bằng giá trị của `toggle` sau khi bộ đếm đạt đến một nửa giá trị `divider - 1`. Điều này giúp tạo ra duty cycle gần hơn với 50%. Tuy nhiên, duty cycle vẫn có thể không hoàn toàn chính xác 50% tùy thuộc vào giá trị của `divider`. Để đạt được độ chính xác cao hơn, bạn có thể cần các kỹ thuật phức tạp hơn như sử dụng PLL hoặc DLL.

Hãy nhớ chọn phương pháp phù hợp nhất với yêu cầu cụ thể của ứng dụng của bạn. Chúc bạn thành công!


   
Trả lờiTrích dẫn