雖然程式設計都交給Claude Code, 但這麼龐大的軟體安裝還是要自己來才行,畢竟建環境這件事是每個新手一定要go through的過程:
Vivado安裝及使用可以參考底下兩篇,這邊就不做教學:
AMD(Xilinx) Vivado 安裝教學(伴伴學)
2018鐵人賽文章 [Day2] tool安裝
簡單的說,就是把Claude Code產生的Verilog程式碼貼上執行,再燒錄到板子上, 這邊好玩的地方是筆者這邊是用兩塊板子,所以有tx跟rx兩支.v的程式,要分別燒錄到Board A跟Board B:
module top_rx(
input sysclk,
input pio1_in, // 通道 1 輸入 (Pin 1)
input pio2_in, // 通道 2 輸入 (Pin 2)
output uart_txd_out,// UART 輸出
output [1:0] led // 狀態指示燈
);
// 1. 生成 100MHz
wire clk_100m;
clk_wiz_0 clk_gen (.clk_in1(sysclk), .clk_out1(clk_100m));
// ==========================================
// 通道 1 處理邏輯
// ==========================================
reg sig1_s1, sig1_s2;
always @(posedge clk_100m) begin
sig1_s1 <= pio1_in;
sig1_s2 <= sig1_s1;
end
wire sig1_rising = (sig1_s1 && !sig1_s2);
reg [31:0] cnt1_raw;
reg [31:0] freq1_res;
// ==========================================
// 通道 2 處理邏輯
// ==========================================
reg sig2_s1, sig2_s2;
always @(posedge clk_100m) begin
sig2_s1 <= pio2_in;
sig2_s2 <= sig2_s1;
end
wire sig2_rising = (sig2_s1 && !sig2_s2);
reg [31:0] cnt2_raw;
reg [31:0] freq2_res;
// ==========================================
// 系統計時與心跳
// ==========================================
reg [31:0] timer_1s;
localparam TIME_1S = 100000000;
reg update_flag;
reg heartbeat_toggle; // 用來讓 LED 閃爍的暫存器
always @(posedge clk_100m) begin
if (timer_1s == TIME_1S - 1) begin
timer_1s <= 0;
freq1_res <= cnt1_raw;
freq2_res <= cnt2_raw;
cnt1_raw <= 0;
cnt2_raw <= 0;
update_flag <= 1;
heartbeat_toggle <= ~heartbeat_toggle; // 每秒翻轉一次狀態
end else begin
timer_1s <= timer_1s + 1;
update_flag <= 0;
if (sig1_rising) cnt1_raw <= cnt1_raw + 1;
if (sig2_rising) cnt2_raw <= cnt2_raw + 1;
end
end
// ==========================================
// UART 發送邏輯 (雙通道)
// ==========================================
reg [7:0] tx_data;
reg tx_start;
wire tx_busy;
uart_tx my_uart (
.clk(clk_100m),
.start(tx_start),
.data(tx_data),
.tx_busy(tx_busy),
.tx_pin(uart_txd_out)
);
reg [4:0] send_step;
always @(posedge clk_100m) begin
tx_start <= 0;
if (update_flag) begin
send_step <= 1;
end
if (send_step != 0 && !tx_busy) begin
case (send_step)
// Header (0xAA)
1: begin tx_data <= 8'hAA; tx_start <= 1; send_step <= 2; end
// Channel 1 Data
2: if(!tx_start) begin tx_data <= freq1_res[31:24]; tx_start <= 1; send_step <= 3; end
3: if(!tx_start) begin tx_data <= freq1_res[23:16]; tx_start <= 1; send_step <= 4; end
4: if(!tx_start) begin tx_data <= freq1_res[15:8]; tx_start <= 1; send_step <= 5; end
5: if(!tx_start) begin tx_data <= freq1_res[7:0]; tx_start <= 1; send_step <= 6; end
// Channel 2 Data
6: if(!tx_start) begin tx_data <= freq2_res[31:24]; tx_start <= 1; send_step <= 7; end
7: if(!tx_start) begin tx_data <= freq2_res[23:16]; tx_start <= 1; send_step <= 8; end
8: if(!tx_start) begin tx_data <= freq2_res[15:8]; tx_start <= 1; send_step <= 9; end
9: if(!tx_start) begin tx_data <= freq2_res[7:0]; tx_start <= 1; send_step <= 10; end
// Tail (0x55)
10: if(!tx_start) begin tx_data <= 8'h55; tx_start <= 1; send_step <= 0; end
endcase
end
end
// ==========================================
// LED 顯示邏輯 (視覺回饋區)
// ==========================================
// LED 0 (Signal): 只要任一通道有頻率輸入 (>1kHz),這顆燈就會亮
assign led[0] = (freq1_res > 1000) || (freq2_res > 1000);
// LED 1 (System): 只要系統還活著,這顆燈就會每秒閃爍一次 (Heartbeat)
// 另外加上 update_flag 讓傳輸瞬間閃一下,增加科技感
assign led[1] = heartbeat_toggle;
endmodule
module top_rx(
input sysclk,
input pio1_in, // 通道 1 輸入
input pio2_in, // 通道 2 輸入 (新增)
output uart_txd_out,// UART 輸出
output [1:0] led
);
// 1. 生成 100MHz
wire clk_100m;
clk_wiz_0 clk_gen (.clk_in1(sysclk), .clk_out1(clk_100m));
// ==========================================
// 通道 1 處理邏輯 (Pin 1)
// ==========================================
reg sig1_s1, sig1_s2;
always @(posedge clk_100m) begin
sig1_s1 <= pio1_in;
sig1_s2 <= sig1_s1;
end
wire sig1_rising = (sig1_s1 && !sig1_s2);
reg [31:0] cnt1_raw;
reg [31:0] freq1_res; // 結果 1
// ==========================================
// 通道 2 處理邏輯 (Pin 2) - 完全複製一份硬體電路
// ==========================================
reg sig2_s1, sig2_s2;
always @(posedge clk_100m) begin
sig2_s1 <= pio2_in;
sig2_s2 <= sig2_s1;
end
wire sig2_rising = (sig2_s1 && !sig2_s2);
reg [31:0] cnt2_raw;
reg [31:0] freq2_res; // 結果 2
// ==========================================
// 共用計時器 (1秒)
// ==========================================
reg [31:0] timer_1s;
localparam TIME_1S = 100000000;
reg update_flag;
always @(posedge clk_100m) begin
if (timer_1s == TIME_1S - 1) begin
timer_1s <= 0;
// 同時鎖存兩個結果
freq1_res <= cnt1_raw;
freq2_res <= cnt2_raw;
// 重置兩個計數器
cnt1_raw <= 0;
cnt2_raw <= 0;
update_flag <= 1;
end else begin
timer_1s <= timer_1s + 1;
update_flag <= 0;
// 並行計數:這兩個 if 語句是同時執行的,互不影響
if (sig1_rising) cnt1_raw <= cnt1_raw + 1;
if (sig2_rising) cnt2_raw <= cnt2_raw + 1;
end
end
// ==========================================
// UART 發送邏輯 (擴充為發送 2 個數據)
// ==========================================
reg [7:0] tx_data;
reg tx_start;
wire tx_busy;
uart_tx my_uart (
.clk(clk_100m),
.start(tx_start),
.data(tx_data),
.tx_busy(tx_busy),
.tx_pin(uart_txd_out)
);
reg [4:0] send_step;
always @(posedge clk_100m) begin
tx_start <= 0;
if (update_flag) begin
send_step <= 1;
end
if (send_step != 0 && !tx_busy) begin
case (send_step)
// Header
1: begin tx_data <= 8'hAA; tx_start <= 1; send_step <= 2; end
// --- 發送 Freq 1 (4 Bytes) ---
2: if(!tx_start) begin tx_data <= freq1_res[31:24]; tx_start <= 1; send_step <= 3; end
3: if(!tx_start) begin tx_data <= freq1_res[23:16]; tx_start <= 1; send_step <= 4; end
4: if(!tx_start) begin tx_data <= freq1_res[15:8]; tx_start <= 1; send_step <= 5; end
5: if(!tx_start) begin tx_data <= freq1_res[7:0]; tx_start <= 1; send_step <= 6; end
// --- 發送 Freq 2 (4 Bytes) - 新增 ---
6: if(!tx_start) begin tx_data <= freq2_res[31:24]; tx_start <= 1; send_step <= 7; end
7: if(!tx_start) begin tx_data <= freq2_res[23:16]; tx_start <= 1; send_step <= 8; end
8: if(!tx_start) begin tx_data <= freq2_res[15:8]; tx_start <= 1; send_step <= 9; end
9: if(!tx_start) begin tx_data <= freq2_res[7:0]; tx_start <= 1; send_step <= 10; end
// Tail
10: if(!tx_start) begin tx_data <= 8'h55; tx_start <= 1; send_step <= 0; end
endcase
end
end
assign led[0] = (freq1_res > 1000);
assign led[1] = (freq2_res > 1000); // 現在兩顆燈分別監控兩個通道
endmodule