• 【RS422】基于未来科技FT4232HL芯片的多波特率串口通信收发实现


    功能简介

      串行通信接口常常用于在计算机和低速外部设备之间传输数据。串口通信存在多种标准,以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]
    
    • 1
    • 2

    在这里插入图片描述

    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
        );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376

       串口发送模块的代码如下,它将收到的未检测出错误的数据转发给主机。

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149

    约束文件实现(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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    仿真文件实现(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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136

    实机测试

       由于是未来科技制造的芯片,需要使用来自未来科技编写的VCP驱动程序将一个USB设备拓展为4个串口设备,方能进行串口通信。
       官方提供了多平台的驱动程序,然而其中仅Windows驱动存在近期更新,故本文串口通信测试在Windows虚拟机上进行。

    在这里插入图片描述

    参考链接:

    1. 串口通讯UART/RS232/RS485/RS-422笔记
    2. 俺也学不会FPGA的博客
  • 相关阅读:
    C++ 使用vector,vector中元素是pair的排序方法
    excel表中复制粘贴有隐藏行的情况
    CLIP(Contrastive Language-Image Pre-Training)简介
    nrf52832 PWM配置
    【java】mybatis拦截器实现单表对于geometry类型字段的新增查询更新
    前端webpack项目如何删除文件中的无用文件
    Springboot 一行代码实现文件上传 20个平台!少写代码到极致
    Linux命令的基本使用
    在vite(vue)项目中使用mockjs
    SRIO系列-基本概念及IP核使用
  • 原文地址:https://blog.csdn.net/qq_45434284/article/details/136769799