功能简介
串行通信接口常常用于在计算机和低速外部设备之间传输数据。串口通信存在多种标准,以RS422为例,它将数据分成多个位,采用异步通信方式进行传输。
本文基于Xilinx VCU128 FPGA开发板,对RS422串口通信进行学习。
根据用户手册ug1302,128中采用了一款来自未来科技(Future Technology Devices International Ltd.)的USB转UART的芯片FT4232HL(芯片手册)。
FT4232HL芯片能够将USB接口转化为4个串口通道,并支持配置4个串口通道为不同类型的串口协议,根据FT4232HL芯片手册(P10)可以看到在配置为RS422模式下串口通道各引脚功能如下:
在实际使用中,Xilinx配置芯片的通道A为JTAG模式用于JTAG调试链,通道B与通道C用于UART串口通信,通道D用于SYSCTLR。其中通道B、C仅引出了TXD、RXD、RTS_n、CTS_n四根引脚。其中通道C的TXD、RXD的引脚位置可通过如下约束获取
set_property BOARD_PART_PIN USB_UART1_TX [get_ports channel_tx]
set_property BOARD_PART_PIN USB_UART1_RX [get_ports channel_rx]
SystemVerilog实现(ft4232hl_uart.sv)
根据422协议规定,编写串口接收代码如下,主要功能包括:
- 采用偶校验、1停止位、8数据位。
- 采样采用mmcm产生的400MHz时钟(800MHz时ila存在时序违例),采样串口接收到的数据时采取多次采样方式,即总样本里超过75%为1则为1,少于25%为1则为0。
- vio用于将采样次数适配到串口波特率,由于采样时钟为400MHz,当需要波特率为115200bps时,需要vio设置采样次数为3472。
- ila用于抓取串口接收到的字节数据、以及是否存在错误(无停止位错误、校验位错误、采样结果不确定错误)。
`timescale 1ns / 1ps
//
// Company:
// Engineer: wjh776a68
//
// Create Date: 03/15/2024 07:45:09 PM
// Design Name:
// Module Name: ft4232hl_uart
// Project Name:
// Target Devices: XCVU37P
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
// ===================================================================
// =
// = https://ftdichip.com/wp-content/uploads/2020/08/DS_FT4232H.pdf
// = P15 signals difinition
// =
// ===================================================================
module ft4232hl_uart(
input logic default_clk_p ,
input logic default_clk_n ,
input logic reset ,
input logic channel_rx ,
output logic channel_tx
// input logic channel_rts_n ,
// output logic channel_cts_n
);
// assign channel_cts_n = 1;
logic clk_100MHz;
IBUFDS #(
.DIFF_TERM("FALSE"), // Differential Termination
.IBUF_LOW_PWR("TRUE"), // Low power="TRUE", Highest performance="FALSE"
.IOSTANDARD("DEFAULT") // Specify the input I/O standard
) IBUFDS_inst (
.O(clk_100MHz), // Buffer output
.I(default_clk_p), // Diff_p buffer input (connect directly to top-level port)
.IB(default_clk_n) // Diff_n buffer input (connect directly to top-level port)
);
logic mmcm_fbclk_s;
logic mmcm_locked_s;
logic clk_800MHz;
MMCME4_BASE #(
.BANDWIDTH("OPTIMIZED"), // Jitter programming
.CLKFBOUT_MULT_F(8.0), // Multiply value for all CLKOUT
.CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB
.CLKIN1_PERIOD(10.0), // Input clock period in ns to ps resolution (i.e., 33.333 is 30 MHz).
.CLKOUT0_DIVIDE_F(2.0), // Divide amount for CLKOUT0
.CLKOUT0_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT0
.CLKOUT0_PHASE(0.0), // Phase offset for CLKOUT0
.CLKOUT1_DIVIDE(1), // Divide amount for CLKOUT (1-128)
.CLKOUT1_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT outputs (0.001-0.999).
.CLKOUT1_PHASE(0.0), // Phase offset for CLKOUT outputs (-360.000-360.000).
.CLKOUT2_DIVIDE(1), // Divide amount for CLKOUT (1-128)
.CLKOUT2_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT outputs (0.001-0.999).
.CLKOUT2_PHASE(0.0), // Phase offset for CLKOUT outputs (-360.000-360.000).
.CLKOUT3_DIVIDE(1), // Divide amount for CLKOUT (1-128)
.CLKOUT3_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT outputs (0.001-0.999).
.CLKOUT3_PHASE(0.0), // Phase offset for CLKOUT outputs (-360.000-360.000).
.CLKOUT4_CASCADE("FALSE"), // Divide amount for CLKOUT (1-128)
.CLKOUT4_DIVIDE(1), // Divide amount for CLKOUT (1-128)
.CLKOUT4_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT outputs (0.001-0.999).
.CLKOUT4_PHASE(0.0), // Phase offset for CLKOUT outputs (-360.000-360.000).
.CLKOUT5_DIVIDE(1), // Divide amount for CLKOUT (1-128)
.CLKOUT5_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT outputs (0.001-0.999).
.CLKOUT5_PHASE(0.0), // Phase offset for CLKOUT outputs (-360.000-360.000).
.CLKOUT6_DIVIDE(1), // Divide amount for CLKOUT (1-128)
.CLKOUT6_DUTY_CYCLE(0.5), // Duty cycle for CLKOUT outputs (0.001-0.999).
.CLKOUT6_PHASE(0.0), // Phase offset for CLKOUT outputs (-360.000-360.000).
.DIVCLK_DIVIDE(1), // Master division value
.IS_CLKFBIN_INVERTED(1'b0), // Optional inversion for CLKFBIN
.IS_CLKIN1_INVERTED(1'b0), // Optional inversion for CLKIN1
.IS_PWRDWN_INVERTED(1'b0), // Optional inversion for PWRDWN
.IS_RST_INVERTED(1'b0), // Optional inversion for RST
.REF_JITTER1(0.0), // Reference input jitter in UI (0.000-0.999).
.STARTUP_WAIT("FALSE") // Delays DONE until MMCM is locked
)
MMCME4_BASE_inst (
.CLKFBOUT(mmcm_fbclk_s), // 1-bit output: Feedback clock pin to the MMCM
.CLKFBOUTB(), // 1-bit output: Inverted CLKFBOUT
.CLKOUT0(clk_800MHz), // 1-bit output: CLKOUT0
.CLKOUT0B(), // 1-bit output: Inverted CLKOUT0
.CLKOUT1(), // 1-bit output: CLKOUT1
.CLKOUT1B(), // 1-bit output: Inverted CLKOUT1
.CLKOUT2(), // 1-bit output: CLKOUT2
.CLKOUT2B(), // 1-bit output: Inverted CLKOUT2
.CLKOUT3(), // 1-bit output: CLKOUT3
.CLKOUT3B(), // 1-bit output: Inverted CLKOUT3
.CLKOUT4(), // 1-bit output: CLKOUT4
.CLKOUT5(), // 1-bit output: CLKOUT5
.CLKOUT6(), // 1-bit output: CLKOUT6
.LOCKED(mmcm_locked_s), // 1-bit output: LOCK
.CLKFBIN(mmcm_fbclk_s), // 1-bit input: Feedback clock pin to the MMCM
.CLKIN1(clk_100MHz), // 1-bit input: Primary clock
.PWRDWN(1'b0), // 1-bit input: Power-down
.RST(reset) // 1-bit input: Reset
);
// clk_800MHz
logic channel_rx_d1_r = 0, channel_rx_d2_r = 0, channel_rx_d3_r = 0;
always_ff @(posedge clk_800MHz) begin
channel_rx_d3_r <= channel_rx_d2_r;
channel_rx_d2_r <= channel_rx_d1_r;
channel_rx_d1_r <= channel_rx;
end
logic [31:0] cfg_datarate_i;
logic cfg_datafresh_i;
logic [31:0] cfg_datarate_r = 0;
logic [31:0] cfg_datarate_sub1_r = 0;
logic [31:0] cfg_datarate_sub2_r = 0;
logic [31:0] cfg_datarate_m3d4_r = 0;
logic [31:0] cfg_datarate_m1d4_r = 0;
logic cfg_datafresh_r = 0;
vio_0 vio_0_inst (
.clk(clk_800MHz), // input wire clk
.probe_out0(cfg_datafresh_i), // output wire [0 : 0] probe_out0
.probe_out1(cfg_datarate_i) // output wire [31 : 0] probe_out1
);
logic startbit_detected_s;
assign startbit_detected_s = channel_rx_d3_r & ~channel_rx_d2_r;
ila_0 ila_uartio_inst (
.clk(clk_800MHz), // input wire clk
.probe0(channel_rx_d3_r), // input wire [0:0] probe0
.probe1(state_r), // input wire [7:0] probe1
.probe2(channel_tx) // input wire [0:0] probe2
);
enum logic[5:0] {
RESET ,
IDLE ,
GET_STARTBIT ,
GET_DATA ,
GET_PARITY ,
GET_STOPBIT
} state_r, state_s;
logic [2:0] rx_getdata_cnt_r;
logic [7:0] rx_data_r;
logic rx_valid_r;
logic rx_error_flag_s;
logic parity_error_flag_r;
logic undetect_error_flag_r;
logic nostop_error_flag_r;
assign rx_error_flag_s = parity_error_flag_r | undetect_error_flag_r | nostop_error_flag_r;
always_ff @(posedge clk_800MHz) begin
if (reset)
state_r <= RESET;
else
state_r <= state_s;
end
logic next_state_flag_r;
logic capture_value_r;
always_comb begin
case (state_r)
RESET: state_s = IDLE;
IDLE: begin
if (startbit_detected_s)
state_s = GET_STARTBIT;
else
state_s = IDLE;
end
GET_STARTBIT: begin
if (next_state_flag_r) begin
if (~capture_value_r)
state_s = GET_DATA;
else
state_s = IDLE;
end else begin
state_s = GET_STARTBIT;
end
end
GET_DATA: begin
if (next_state_flag_r && rx_getdata_cnt_r == 0)
state_s = GET_PARITY;
else
state_s = GET_DATA;
end
GET_PARITY: begin
if (next_state_flag_r)
state_s = GET_STOPBIT;
else
state_s = GET_PARITY;
end
GET_STOPBIT: begin
if (next_state_flag_r)
if (startbit_detected_s)
state_s = GET_STARTBIT;
else
state_s = IDLE;
else
state_s = GET_STOPBIT;
end
default: state_s = IDLE;
endcase
end
logic [31:0] capture_asserted_cnt_r = 'd0;
logic [31:0] capture_total_cnt_r = 'd0;
logic cnt_fresh_s;
assign cnt_fresh_s = (capture_total_cnt_r == cfg_datarate_sub1_r) ? 1'b1 : 1'b0;
always_ff @(posedge clk_800MHz) begin
case (state_s)
IDLE: begin
capture_asserted_cnt_r <= 'd0;
end
default: begin
if (cnt_fresh_s)
capture_asserted_cnt_r <= 'd0;
else if (channel_rx_d3_r)
capture_asserted_cnt_r <= capture_asserted_cnt_r + 'd1;
end
endcase
end
always_ff @(posedge clk_800MHz) begin
case (state_s)
IDLE: begin
capture_total_cnt_r <= 'd0;
end
default: begin
if (cnt_fresh_s)
capture_total_cnt_r <= 'd0;
else
capture_total_cnt_r <= capture_total_cnt_r + 'd1;
end
endcase
end
always_ff @(posedge clk_800MHz) begin
case (state_s)
RESET, IDLE: begin
rx_valid_r <= 1'b0;
end
GET_STOPBIT: begin
if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
rx_valid_r <= 1'b1;
end
end
endcase
end
always_ff @(posedge clk_800MHz) begin
case (state_s)
RESET, IDLE: begin
nostop_error_flag_r <= 1'b0;
end
GET_STOPBIT: begin
if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
if (~capture_value_r) begin
nostop_error_flag_r <= 1'b1;
end
end
end
endcase
end
always_ff @(posedge clk_800MHz) begin
case (state_s)
RESET, IDLE: begin
undetect_error_flag_r <= 1'b0;
end
GET_STARTBIT, GET_DATA, GET_PARITY, GET_STOPBIT: begin
if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
if (capture_asserted_cnt_r > cfg_datarate_m3d4_r) begin
// undetect_error_flag_r <= 1'b0;
end else if (capture_asserted_cnt_r < cfg_datarate_m1d4_r) begin
// undetect_error_flag_r <= 1'b0;
end else begin
undetect_error_flag_r <= 1'b1;
end
end
end
endcase
end
always_ff @(posedge clk_800MHz) begin
case (state_s)
RESET: begin
parity_error_flag_r <= 1'b0;
end
GET_PARITY: begin
if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
if (capture_value_r == ^rx_data_r[7:0]) begin
parity_error_flag_r <= 1'b0;
end else begin
parity_error_flag_r <= 1'b1;
end
end
end
endcase
end
always_ff @(posedge clk_800MHz) begin
case (state_s)
IDLE: begin
next_state_flag_r <= 1'b0;
end
default: begin
if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
next_state_flag_r <= 1'b1;
end else if (capture_total_cnt_r == 0) begin
next_state_flag_r <= 1'b0;
end
end
endcase
end
always_ff @(posedge clk_800MHz) begin
case (state_s)
IDLE: begin
capture_value_r <= 1'b0;
end
default: begin
if (capture_total_cnt_r == cfg_datarate_sub2_r) begin
if (capture_asserted_cnt_r > cfg_datarate_m3d4_r) begin
capture_value_r <= 1'b1;
end else if (capture_asserted_cnt_r < cfg_datarate_m1d4_r) begin
capture_value_r <= 1'b0;
end
end
end
endcase
end
always_ff @(posedge clk_800MHz) begin
case (state_s)
GET_DATA: begin
if (capture_total_cnt_r == cfg_datarate_sub1_r) begin
rx_getdata_cnt_r <= rx_getdata_cnt_r + 'd1;
rx_data_r[rx_getdata_cnt_r] <= capture_value_r;
end
end
default: begin
rx_getdata_cnt_r <= 3'd0;
end
endcase
end
ila_0 ila_0_inst (
.clk(clk_800MHz), // input wire clk
.probe0(rx_valid_r), // input wire [0:0] probe0
.probe1(rx_data_r), // input wire [7:0] probe1
.probe2(rx_error_flag_s) // input wire [0:0] probe2
);
串口发送模块的代码如下,它将收到的未检测出错误的数据转发给主机。
enum logic [5:0] {
TX_RESET ,
TX_IDLE ,
TX_SEND_STARTBIT ,
TX_SEND_DATABIT ,
TX_SEND_PARITYBIT ,
TX_SEND_STOPBIT
} send_state_r, send_state_s;
always_ff @(posedge clk_800MHz) begin
if (reset) begin
send_state_r <= TX_RESET;
end else begin
send_state_r <= send_state_s;
end
end
logic send_nextstate_r;
logic [2:0] tx_senddata_cnt_r;
logic [7:0] tx_data_r;
logic tx_valid_r;
always_comb begin
case (send_state_r)
TX_RESET: send_state_s = TX_IDLE;
TX_IDLE: begin
if (tx_valid_r)
send_state_s = TX_SEND_STARTBIT;
else
send_state_s = TX_IDLE;
end
TX_SEND_STARTBIT: begin
if (send_nextstate_r) begin
send_state_s = TX_SEND_DATABIT;
end else begin
send_state_s = TX_SEND_STARTBIT;
end
end
TX_SEND_DATABIT: begin
if (send_nextstate_r && tx_senddata_cnt_r == 3'd0) begin
send_state_s = TX_SEND_PARITYBIT;
end else begin
send_state_s = TX_SEND_DATABIT;
end
end
TX_SEND_PARITYBIT: begin
if (send_nextstate_r) begin
send_state_s = TX_SEND_STOPBIT;
end else begin
send_state_s = TX_SEND_PARITYBIT;
end
end
TX_SEND_STOPBIT: begin
if (send_nextstate_r) begin
if (tx_valid_r) begin
send_state_s = TX_SEND_STARTBIT;
end else begin
send_state_s = TX_IDLE;
end
end else begin
send_state_s = TX_SEND_STOPBIT;
end
end
default: send_state_s = TX_RESET;
endcase
end
always_ff @(posedge clk_800MHz) begin
case (send_state_s)
TX_IDLE, TX_SEND_STOPBIT: begin
if (rx_valid_r & ~rx_error_flag_s) begin
tx_valid_r <= rx_valid_r;
tx_data_r <= rx_data_r;
end else if (~rx_valid_r & tx_valid_r) begin
tx_valid_r <= 1'b0;
end
end
endcase
end
always_ff @(posedge clk_800MHz) begin
case (send_state_s)
TX_IDLE, TX_SEND_STOPBIT: begin
if (~rx_valid_r) begin
cfg_datafresh_r <= cfg_datafresh_i;
if (cfg_datafresh_i) begin
cfg_datarate_r <= cfg_datarate_i;
cfg_datarate_sub1_r <= cfg_datarate_i - 1;
cfg_datarate_sub2_r <= cfg_datarate_i - 2;
cfg_datarate_m3d4_r <= (cfg_datarate_i >> 1) + (cfg_datarate_i >> 2);
cfg_datarate_m1d4_r <= (cfg_datarate_i >> 2);
end
end
end
endcase
end
logic [31:0] sent_total_cnt_r;
always_ff @(posedge clk_800MHz) begin
case (send_state_s)
default: begin
if (sent_total_cnt_r == cfg_datarate_sub1_r) begin
send_nextstate_r <= 1'b1;
end else begin
send_nextstate_r <= 1'b0;
end
end
TX_IDLE: begin
end
endcase
end
always_ff @(posedge clk_800MHz) begin
case (send_state_s)
default: begin
if (sent_total_cnt_r == cfg_datarate_sub1_r) begin
sent_total_cnt_r <= 'd0;
end else begin
sent_total_cnt_r <= sent_total_cnt_r + 1;
end
end
TX_IDLE: sent_total_cnt_r <= 'd0;
endcase
end
always_ff @(posedge clk_800MHz) begin
case (send_state_s)
TX_RESET, TX_IDLE, TX_SEND_STOPBIT: channel_tx <= 1'b1;
TX_SEND_STARTBIT: channel_tx <= 1'b0;
TX_SEND_DATABIT: channel_tx <= tx_data_r[tx_senddata_cnt_r];
TX_SEND_PARITYBIT: channel_tx <= ^tx_data_r[7:0];
endcase
end
always_ff @(posedge clk_800MHz) begin
case (send_state_s)
TX_SEND_STARTBIT: begin
tx_senddata_cnt_r <= 3'd0;
end
TX_SEND_DATABIT: begin
if (sent_total_cnt_r == cfg_datarate_sub1_r) begin
tx_senddata_cnt_r <= tx_senddata_cnt_r + 1;
end
end
endcase
end
endmodule
约束文件实现(ft4232hl_uart.xdc)
对应约束文件如下:
set_property BOARD_PART_PIN default_100mhz_clk_p [get_ports default_clk_p]
set_property BOARD_PART_PIN default_100mhz_clk_n [get_ports default_clk_n]
set_property BOARD_PART_PIN CPU_RESET [get_ports reset]
set_property BOARD_PART_PIN USB_UART1_TX [get_ports channel_tx]
set_property BOARD_PART_PIN USB_UART1_RX [get_ports channel_rx]
set_property BOARD_PART_PIN USB_UART1_CTS [get_ports channel_cts]
set_property BOARD_PART_PIN USB_UART1_RTS [get_ports channel_rts]
# auto generate
set_property IOSTANDARD DIFF_SSTL12 [get_ports default_clk_p]
set_property IOSTANDARD DIFF_SSTL12 [get_ports default_clk_n]
set_property PACKAGE_PIN BH51 [get_ports default_clk_p]
set_property PACKAGE_PIN BJ51 [get_ports default_clk_n]
set_property IOSTANDARD LVCMOS12 [get_ports reset]
set_property PACKAGE_PIN BM29 [get_ports reset]
set_property IOSTANDARD LVCMOS18 [get_ports channel_tx]
set_property PACKAGE_PIN BN26 [get_ports channel_tx]
set_property IOSTANDARD LVCMOS18 [get_ports channel_rx]
set_property PACKAGE_PIN BP26 [get_ports channel_rx]
# STA constraint
create_clock -period 10.000 -waveform {0.000 5.000} [get_ports default_clk_p]
create_generated_clock -source [get_ports default_clk_p] -divide_by 1 [get_pins IBUFDS_inst/O]
# create_clock -period 2.500 -waveform {0.000 1.250} [get_pins MMCME4_BASE_inst/CLKOUT0]
set_property C_CLK_INPUT_FREQ_HZ 300000000 [get_debug_cores dbg_hub]
set_property C_ENABLE_CLK_DIVIDER false [get_debug_cores dbg_hub]
set_property C_USER_SCAN_CHAIN 1 [get_debug_cores dbg_hub]
connect_debug_port dbg_hub/clk [get_nets clk_800MHz_BUFG]
仿真文件实现(ft4232hl_uart_tb.sv)
`timescale 1ns / 1ps
//
// Company:
// Engineer: wjh776a68
//
// Create Date: 03/15/2024 10:35:44 PM
// Design Name:
// Module Name: ft4232hl_uart_tb
// Project Name:
// Target Devices: XCVU37P
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module ft4232hl_uart_tb();
bit clk_100MHz ;
logic reset ;
bit channel_rx= 1'b0;
logic channel_tx ;
always #5 clk_100MHz = ~clk_100MHz;
ft4232hl_uart ft4232hl_uart_inst(
.default_clk_p (clk_100MHz),
.default_clk_n (~clk_100MHz),
.reset (reset ),
.channel_rx (channel_rx),
.channel_tx (channel_tx)
);
initial begin
ft4232hl_uart_inst.cfg_datafresh_i <= 1'b0;
ft4232hl_uart_inst.cfg_datarate_i <= 0;
@(posedge ft4232hl_uart_inst.mmcm_locked_s);
ft4232hl_uart_inst.cfg_datafresh_i <= 1'b1;
ft4232hl_uart_inst.cfg_datarate_i <= 217;
@(posedge clk_100MHz);
ft4232hl_uart_inst.cfg_datafresh_i <= 1'b0;
ft4232hl_uart_inst.cfg_datarate_i <= 0;
end
bit clk_1_8432MHz ;
bit [2:0] cnt;
always #(500 / 1.8432) clk_1_8432MHz = ~clk_1_8432MHz;
initial begin
reset = 1'b1;
@(posedge clk_1_8432MHz);
reset <= 1'b0;
end
enum logic [3:0] {
IDLE = 4'd0 ,
START_BIT = 4'd1 ,
DATA_BIT = 4'd2 ,
PARITY_BIT = 4'd3 ,
STOP_BIT = 4'd4
} state_r, state_s;
always_ff @(posedge clk_1_8432MHz) begin
if (reset) begin
state_r <= IDLE;
end else begin
state_r <= state_s;
end
end
logic [4:0] idle_cnt;
always_comb begin
case (state_r)
IDLE: begin
if (idle_cnt == 20) begin
state_s = START_BIT;
end else begin
state_s = IDLE;
end
end
START_BIT: state_s = DATA_BIT;
DATA_BIT: begin
if (cnt == 0)
state_s = PARITY_BIT;
else
state_s = DATA_BIT;
end
PARITY_BIT: state_s = STOP_BIT;
STOP_BIT: begin
state_s = START_BIT;
// state_s = IDLE;
end
endcase
end
logic [7:0] data_tosend = 8'h35;
always_ff @(posedge clk_1_8432MHz) begin
case (state_s)
IDLE: channel_rx <= 1'b1;
START_BIT: begin
cnt <= 'd0;
channel_rx <= 1'b0;
end
DATA_BIT: begin
cnt <= cnt + 1;
channel_rx <= data_tosend[cnt];
end
PARITY_BIT: begin
channel_rx <= ^data_tosend[7:0];
end
STOP_BIT: begin
channel_rx <= 1'b1;
end
endcase
end
always_ff @(posedge clk_1_8432MHz) begin
case (state_s)
IDLE: idle_cnt <= idle_cnt + 1;
default: idle_cnt <= 0;
endcase
end
endmodule
实机测试
由于是未来科技制造的芯片,需要使用来自未来科技编写的VCP驱动程序将一个USB设备拓展为4个串口设备,方能进行串口通信。
官方提供了多平台的驱动程序,然而其中仅Windows驱动存在近期更新,故本文串口通信测试在Windows虚拟机上进行。
参考链接:
- 串口通讯UART/RS232/RS485/RS-422笔记
- 俺也学不会FPGA的博客