diff --git a/uart/tb_uart.v b/uart/tb_uart.v new file mode 100644 index 0000000..03d7dd3 --- /dev/null +++ b/uart/tb_uart.v @@ -0,0 +1,56 @@ +`timescale 1ns/1ns +module tb_uart(); + +reg sys_clk; +reg sys_rst; +reg rxd; +wire txd; + +always #10 sys_clk = ~sys_clk; + +// 发送数据 8'b0101_0101 +parameter [7:0]data = 8'b0101_0101; + +initial begin + sys_clk <= 1'b0; + rxd <= 1'b1; + + sys_rst <= 1'b0; + #50 + sys_rst <= 1'b1; + + #1000 + // 开始发送数据 + rxd <= 1'b0; // 起始位 + #8680 + rxd <= data[0]; + #8680 + rxd <= data[1]; + #8680 + rxd <= data[2]; + #8680 + rxd <= data[3]; + #8680 + rxd <= data[4]; + #8680 + rxd <= data[5]; + #8680 + rxd <= data[6]; + #8680 + rxd <= data[7]; + #8680 + rxd <= 1'b1; // 停止位 + #8680 + rxd <= 1'b1; // 空闲了 +end + + +uart_top u_uart_top( + .sys_clk(sys_clk), // U18 + .sys_rst(sys_rst), //J15 + .rxd(rxd), + .txd(txd) +); + + +endmodule \ No newline at end of file diff --git a/uart/tx_时序图.png b/uart/tx_时序图.png new file mode 100644 index 0000000..01d9504 Binary files /dev/null and b/uart/tx_时序图.png differ diff --git a/uart/uart_rx.v b/uart/uart_rx.v index 01ec4dc..af4ccd2 100644 --- a/uart/uart_rx.v +++ b/uart/uart_rx.v @@ -51,7 +51,7 @@ always @(posedge sys_clk or negedge sys_rst) begin end // 如果当前接收到了第9个数据(), 并且 到了最大波特率计数器的一半也就是停止位一半的之前(216)时候, 数据其实就全部接收完毕了 // 提前拉低该信号, 防止停止位后紧跟这开始位导致采集不到 - else if(rx_d_cnt == 'b1001 && baud_cnt == B_MAX>>1 - 1) begin + else if(rx_d_cnt == 'b1001 && baud_cnt == (B_MAX>>1) - 1) begin rx_busy <= 'b0; end else begin @@ -109,7 +109,7 @@ always @(posedge sys_clk or negedge sys_rst) begin end else if (rx_busy) begin // 波特率计数到一半的时候进行采样, 最准确 - if (baud_cnt == B_MAX>>1 - 1) begin + if (baud_cnt == (B_MAX>>1) - 1) begin case (rx_d_cnt) 'b0001: temp_rx_data[0] <= rxd_d2; 'b0010: temp_rx_data[1] <= rxd_d2; @@ -140,7 +140,7 @@ always @(posedge sys_clk or negedge sys_rst) begin rx_data <= 'b0; end // 这个其实就是 busy 拉低的条件, 只要满足busy拉低条件,说明数据已经可用, 那立即拉高done(1个周期) - else if(rx_d_cnt == 'b1001 && baud_cnt == B_MAX>>1 - 1) begin + else if(rx_d_cnt == 'b1001 && baud_cnt == (B_MAX>>1) - 1) begin rx_done <= 'b1; rx_data <= temp_rx_data; end diff --git a/uart/uart_top.v b/uart/uart_top.v new file mode 100644 index 0000000..dce1db9 --- /dev/null +++ b/uart/uart_top.v @@ -0,0 +1,41 @@ +// 该模块是把通过串口接收到的数据, 通过 uart_rx模块 把串行数据转为并行, 并保存到 rx_data中 +// 接收一帧之后 拉高rx_done, rx_done和uart_tx模块的start_en相连,通过uart_tx模块再原封不动的把rx_data中的并行数据, 通过txd 再发送出去 +module uart_top( + input wire sys_clk, // U18 + input wire sys_rst, //J15 + input wire rxd, + output wire txd +); + +parameter CLK_FREQ = 5000_0000; +parameter BPS = 115200; + +wire rx_done; +wire [7:0] rx_data; +uart_rx #( + .CLK_FREQ(CLK_FREQ), + .BPS(BPS) +)u_uart_rx( + .sys_clk(sys_clk), + .sys_rst(sys_rst), + .rxd(rxd), + .rx_done(rx_done), + .rx_data(rx_data) +); + +uart_tx #( + .CLK_FREQ(CLK_FREQ), + .BPS(BPS) +)u_uart_tx( + .sys_clk(sys_clk), + .sys_rst(sys_rst), + .start_en(rx_done), + .tx_data(rx_data), + .txd(txd), + .tx_busy() +); + + + + +endmodule \ No newline at end of file diff --git a/uart/uart_tx.v b/uart/uart_tx.v new file mode 100644 index 0000000..fe6cb5c --- /dev/null +++ b/uart/uart_tx.v @@ -0,0 +1,105 @@ +module uart_tx( + input sys_clk, // U18 + input sys_rst, //J15 + input start_en, + input [7:0] tx_data, // 需要发送出去的并行数据 + + output reg txd, // 连接到了外部的 rx, 该信号持续发送串行信号 + output reg tx_busy +); + +parameter CLK_FREQ = 5000_0000; +parameter BPS = 115200; +localparam B_MAX = CLK_FREQ / BPS; + +reg [3:0]tx_d_cnt; +reg [15:0] baud_cnt; + +// 当start_en 为高的时候, 临时保存输入的并行数据, 并拉高busy信号 +reg [7:0] temp_tx_data; // 临时保存数据的寄存器 +always @(posedge sys_clk or negedge sys_rst) begin + if (sys_rst == 1'b0) begin + temp_tx_data <= 'b0; + tx_busy <= 'b0; + end + else if(start_en) begin + temp_tx_data <= tx_data; // 记录外部传过来的并行数据备份 + tx_busy <= 'b1; // 拉高 忙信号 + end + else if (tx_d_cnt == 'b1001 && baud_cnt == (B_MAX / 16 * 15)-1) begin // 提前结束, 和下次发送拉开时间 + temp_tx_data <= 'b0; + tx_busy <= 0; + end + else begin + temp_tx_data <= temp_tx_data; + tx_busy <= tx_busy; + end +end + +// 波特率计数器 +always @(posedge sys_clk or negedge sys_rst) begin + if (sys_rst == 1'b0) begin + baud_cnt <= 'b0; + end + // 必须要开始传输数据 + else if (tx_busy) begin + if (baud_cnt == B_MAX-'b1) begin // 从0开始计数的 0~433 一共计数434次 + baud_cnt <= 'b0; + end + else begin + baud_cnt <= baud_cnt + 'b1; + end + end + else begin + baud_cnt <= 'b0; + end +end + +// 当前发送数据的数据数量 +always @(posedge sys_clk or negedge sys_rst) begin + if (sys_rst == 1'b0) begin + tx_d_cnt <= 'b0; + end + // 必须要开始传输数据 + else if (tx_busy) begin + if (baud_cnt == B_MAX-'b1) begin + tx_d_cnt <= tx_d_cnt + 'b1; + end + else begin + tx_d_cnt <= tx_d_cnt; + end + end + else begin + tx_d_cnt <= 'b0; + end +end + + +always @(posedge sys_clk or negedge sys_rst) begin + if (sys_rst == 1'b0) begin + txd <= 'b1; + end + else if (tx_busy) begin + case (tx_d_cnt) + 'b0000: txd <= 'b0; + 'b0001: txd <= temp_tx_data[0]; + 'b0010: txd <= temp_tx_data[1]; + 'b0011: txd <= temp_tx_data[2]; + 'b0100: txd <= temp_tx_data[3]; + 'b0101: txd <= temp_tx_data[4]; + 'b0110: txd <= temp_tx_data[5]; + 'b0111: txd <= temp_tx_data[6]; + 'b1000: txd <= temp_tx_data[7]; + 'b1001: txd <= 'b1; + default: txd <= 'b1; + endcase + end + else begin + txd <= 'b1; + end +end + + + + +endmodule \ No newline at end of file diff --git a/uart/uart_tx.v.out b/uart/uart_tx.v.out new file mode 100755 index 0000000..7d0c62e --- /dev/null +++ b/uart/uart_tx.v.out @@ -0,0 +1,168 @@ +#! /usr/local/Cellar/icarus-verilog/12.0/bin/vvp +:ivl_version "12.0 (stable)"; +:ivl_delay_selection "TYPICAL"; +:vpi_time_precision + 0; +:vpi_module "/usr/local/Cellar/icarus-verilog/12.0/lib/ivl/system.vpi"; +:vpi_module "/usr/local/Cellar/icarus-verilog/12.0/lib/ivl/vhdl_sys.vpi"; +:vpi_module "/usr/local/Cellar/icarus-verilog/12.0/lib/ivl/vhdl_textio.vpi"; +:vpi_module "/usr/local/Cellar/icarus-verilog/12.0/lib/ivl/v2005_math.vpi"; +:vpi_module "/usr/local/Cellar/icarus-verilog/12.0/lib/ivl/va_math.vpi"; +S_0x7fbaa1007ac0 .scope module, "uart_tx" "uart_tx" 2 1; + .timescale 0 0; + .port_info 0 /INPUT 1 "sys_clk"; + .port_info 1 /INPUT 1 "sys_rst"; + .port_info 2 /INPUT 1 "start_en"; + .port_info 3 /INPUT 8 "tx_data"; + .port_info 4 /OUTPUT 1 "txd"; + .port_info 5 /OUTPUT 1 "tx_busy"; +P_0x7fbaa1005e00 .param/l "BPS" 0 2 14, +C4<00000000000000011100001000000000>; +P_0x7fbaa1005e40 .param/l "B_MAX" 1 2 16, +C4<00000000000000000000000110110010>; +P_0x7fbaa1005e80 .param/l "CLK_FREQ" 0 2 13, +C4<00000010111110101111000010000000>; +v0x7fbaa10069a0_0 .var "baud_cnt", 15 0; +o0x7fbaa1432038 .functor BUFZ 1, C4; HiZ drive +v0x7fbaa10179c0_0 .net "start_en", 0 0, o0x7fbaa1432038; 0 drivers +o0x7fbaa1432068 .functor BUFZ 1, C4; HiZ drive +v0x7fbaa1017a60_0 .net "sys_clk", 0 0, o0x7fbaa1432068; 0 drivers +o0x7fbaa1432098 .functor BUFZ 1, C4; HiZ drive +v0x7fbaa1017af0_0 .net "sys_rst", 0 0, o0x7fbaa1432098; 0 drivers +v0x7fbaa1017b90_0 .var "temp_tx_data", 7 0; +v0x7fbaa1017c80_0 .var "tx_busy", 0 0; +v0x7fbaa1017d20_0 .var "tx_d_cnt", 3 0; +o0x7fbaa1432158 .functor BUFZ 8, C4; HiZ drive +v0x7fbaa1017dd0_0 .net "tx_data", 7 0, o0x7fbaa1432158; 0 drivers +v0x7fbaa1017e80_0 .var "txd", 0 0; +E_0x7fbaa1005110/0 .event negedge, v0x7fbaa1017af0_0; +E_0x7fbaa1005110/1 .event posedge, v0x7fbaa1017a60_0; +E_0x7fbaa1005110 .event/or E_0x7fbaa1005110/0, E_0x7fbaa1005110/1; + .scope S_0x7fbaa1007ac0; +T_0 ; + %wait E_0x7fbaa1005110; + %load/vec4 v0x7fbaa1017af0_0; + %cmpi/e 0, 0, 1; + %jmp/0xz T_0.0, 4; + %pushi/vec4 0, 0, 8; + %assign/vec4 v0x7fbaa1017b90_0, 0; + %jmp T_0.1; +T_0.0 ; + %load/vec4 v0x7fbaa10179c0_0; + %flag_set/vec4 8; + %jmp/0xz T_0.2, 8; + %load/vec4 v0x7fbaa1017dd0_0; + %assign/vec4 v0x7fbaa1017b90_0, 0; +T_0.2 ; +T_0.1 ; + %jmp T_0; + .thread T_0; + .scope S_0x7fbaa1007ac0; +T_1 ; + %wait E_0x7fbaa1005110; + %load/vec4 v0x7fbaa1017af0_0; + %cmpi/e 0, 0, 1; + %jmp/0xz T_1.0, 4; + %pushi/vec4 0, 0, 1; + %assign/vec4 v0x7fbaa1017c80_0, 0; + %jmp T_1.1; +T_1.0 ; + %load/vec4 v0x7fbaa10179c0_0; + %flag_set/vec4 8; + %jmp/0xz T_1.2, 8; + %pushi/vec4 1, 0, 1; + %assign/vec4 v0x7fbaa1017c80_0, 0; + %jmp T_1.3; +T_1.2 ; + %load/vec4 v0x7fbaa1017d20_0; + %pad/u 32; + %cmpi/e 9, 0, 32; + %flag_get/vec4 4; + %jmp/0 T_1.6, 4; + %load/vec4 v0x7fbaa10069a0_0; + %pad/u 32; + %pushi/vec4 404, 0, 32; + %cmp/e; + %flag_get/vec4 4; + %and; +T_1.6; + %flag_set/vec4 8; + %jmp/0xz T_1.4, 8; + %pushi/vec4 0, 0, 1; + %assign/vec4 v0x7fbaa1017c80_0, 0; + %jmp T_1.5; +T_1.4 ; + %load/vec4 v0x7fbaa1017c80_0; + %assign/vec4 v0x7fbaa1017c80_0, 0; +T_1.5 ; +T_1.3 ; +T_1.1 ; + %jmp T_1; + .thread T_1; + .scope S_0x7fbaa1007ac0; +T_2 ; + %wait E_0x7fbaa1005110; + %load/vec4 v0x7fbaa1017af0_0; + %cmpi/e 0, 0, 1; + %jmp/0xz T_2.0, 4; + %pushi/vec4 0, 0, 16; + %assign/vec4 v0x7fbaa10069a0_0, 0; + %jmp T_2.1; +T_2.0 ; + %load/vec4 v0x7fbaa1017c80_0; + %flag_set/vec4 8; + %jmp/0xz T_2.2, 8; + %load/vec4 v0x7fbaa10069a0_0; + %pad/u 32; + %cmpi/e 433, 0, 32; + %jmp/0xz T_2.4, 4; + %pushi/vec4 0, 0, 16; + %assign/vec4 v0x7fbaa10069a0_0, 0; + %jmp T_2.5; +T_2.4 ; + %load/vec4 v0x7fbaa10069a0_0; + %addi 1, 0, 16; + %assign/vec4 v0x7fbaa10069a0_0, 0; +T_2.5 ; + %jmp T_2.3; +T_2.2 ; + %pushi/vec4 0, 0, 16; + %assign/vec4 v0x7fbaa10069a0_0, 0; +T_2.3 ; +T_2.1 ; + %jmp T_2; + .thread T_2; + .scope S_0x7fbaa1007ac0; +T_3 ; + %wait E_0x7fbaa1005110; + %load/vec4 v0x7fbaa1017af0_0; + %cmpi/e 0, 0, 1; + %jmp/0xz T_3.0, 4; + %pushi/vec4 0, 0, 4; + %assign/vec4 v0x7fbaa1017d20_0, 0; + %jmp T_3.1; +T_3.0 ; + %load/vec4 v0x7fbaa1017c80_0; + %flag_set/vec4 8; + %jmp/0xz T_3.2, 8; + %load/vec4 v0x7fbaa10069a0_0; + %pad/u 32; + %cmpi/e 433, 0, 32; + %jmp/0xz T_3.4, 4; + %load/vec4 v0x7fbaa1017d20_0; + %addi 1, 0, 4; + %assign/vec4 v0x7fbaa1017d20_0, 0; + %jmp T_3.5; +T_3.4 ; + %load/vec4 v0x7fbaa1017d20_0; + %assign/vec4 v0x7fbaa1017d20_0, 0; +T_3.5 ; + %jmp T_3.3; +T_3.2 ; + %pushi/vec4 0, 0, 4; + %assign/vec4 v0x7fbaa1017d20_0, 0; +T_3.3 ; +T_3.1 ; + %jmp T_3; + .thread T_3; +# The file index is used to find the file name in the following table. +:file_names 3; + "N/A"; + ""; + "uart_tx.v";