diff --git a/uart/rx_时序图.png b/uart/rx_时序图.png new file mode 100644 index 0000000..54b0cc6 Binary files /dev/null and b/uart/rx_时序图.png differ diff --git a/uart/uart_rx.v b/uart/uart_rx.v new file mode 100644 index 0000000..01ec4dc --- /dev/null +++ b/uart/uart_rx.v @@ -0,0 +1,156 @@ +module uart_rx( + input wire sys_clk, // U18 + input wire sys_rst, //J15 + + input wire rxd, // 连接到了外部的 tx, 持续接收串行信号 + + output reg rx_done, // 接收完毕 + output reg [7:0] rx_data // 接收到的串行信号转成的并行信号 +); + +parameter CLK_FREQ = 5000_0000; +parameter BPS = 115200; +// parameter MAX = 434; // 波特率如果是115200 那就需要 当前50m的时钟计数434次 才能和 1s/115200 相等 +localparam B_MAX = CLK_FREQ / BPS; + + +// 对异步来的数据 打拍变成同步到当前时钟作用域下的数据 +reg rxd_d0; +reg rxd_d1; +reg rxd_d2; + +always @(posedge sys_clk or negedge sys_rst) begin + if (sys_rst == 1'b0) begin + rxd_d0 <= 'b0; + rxd_d1 <= 'b0; + rxd_d2 <= 'b0; + end + else begin + rxd_d0 <= rxd; + rxd_d1 <= rxd_d0; + rxd_d2 <= rxd_d1; + end +end + +reg rx_busy; // 是否在接收数据了, 这个在时序图中名字是 rx_flag, 这个在计数到216的时候拉低 (434/2)-1=216 +wire start_en = rxd_d2 && !rxd_d1 && !rx_busy; // 使用组合逻辑(相当于提前一个时钟周期), 只有rx2是高 rx1是低的时候 此时形成rx的下降沿, 且还没有忙碌的时候, 当前信号的高电平只持续一个时钟周期哦, 意味着他的在拉高的下一个时钟周期, 数据就开始传输了 +reg [3:0]rx_d_cnt; // 当前接收数据的数据数量, 注意 首个数据第零位是起始位, 最后末位第九位是结束位, 接收10次数据, 有效数据是1~8 一共8个数据 +reg [15:0] baud_cnt; // 波特率计数器, 在当前第n个时钟周期 代表了传输一位数据, 在开始接收数据rx_busy为高电平的时候 开始计数, 不在接收数据的时候 立刻清零 +reg [7:0] temp_rx_data; // 临时保存数据的寄存器 + +// rx_d_cnt 和 baud_cnt 会在 rx_busy 拉低的 下一个时钟周期跟着拉低 不再进行各种计数 +// start_en 因为是组合逻辑电路会在 结束位之前的一个时钟周期 提前拉高, 使得在结束位结束 相同的时钟周期 busy会立即因为 之前的start_en是高电平而变高 +// start_en 会在busy 开始处理数据的时候 进行拉低, 所以该信号只持续一个时钟周期, 只是作为采集 结束位下降沿 + +always @(posedge sys_clk or negedge sys_rst) begin + if (sys_rst == 1'b0) begin + rx_busy <= 'b0; + end + else if(start_en) begin // 如果开始接收数据了, 就改为繁忙(由于rx_busy拉高, 组合逻辑电路会导致start_en立即拉低) + rx_busy <= 'b1; + end + // 如果当前接收到了第9个数据(), 并且 到了最大波特率计数器的一半也就是停止位一半的之前(216)时候, 数据其实就全部接收完毕了 + // 提前拉低该信号, 防止停止位后紧跟这开始位导致采集不到 + else if(rx_d_cnt == 'b1001 && baud_cnt == B_MAX>>1 - 1) begin + rx_busy <= 'b0; + end + else begin + rx_busy <= rx_busy; + end +end + + +// 波特率计数器 +always @(posedge sys_clk or negedge sys_rst) begin + if (sys_rst == 1'b0) begin + baud_cnt <= 'b0; + end + // 必须要开始传输数据 + else if (rx_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 + rx_d_cnt <= 'b0; + end + // 必须要开始传输数据 + else if (rx_busy) begin + // 波特率计数器 累加到最大的时候, 说明当前该bit数据接收完了, 加1 准备接收下一个数据 + if (baud_cnt == B_MAX-'b1) begin + rx_d_cnt <= rx_d_cnt + 'b1; + end + else begin + rx_d_cnt <= rx_d_cnt; + end + end + else begin + rx_d_cnt <= 'b0; + end +end + + +// 根据 rx_d_cnt 来寄存 rxd端口串行的数据到指定的 bit +// 在中间采样精确一点 +always @(posedge sys_clk or negedge sys_rst) begin + if (sys_rst == 1'b0) begin + temp_rx_data <= 'b0; + end + else if (rx_busy) 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; + 'b0011: temp_rx_data[2] <= rxd_d2; + 'b0100: temp_rx_data[3] <= rxd_d2; + 'b0101: temp_rx_data[4] <= rxd_d2; + 'b0110: temp_rx_data[5] <= rxd_d2; + 'b0111: temp_rx_data[6] <= rxd_d2; + 'b1000: temp_rx_data[7] <= rxd_d2; + default :; + endcase + end + else begin + temp_rx_data <= temp_rx_data; + end + end + else begin + // 当不再接收的时候 需要清零等待下次接收 + temp_rx_data <= 'b0; + end +end + + +// 接收完毕, 给设置相应的信号和数据 +always @(posedge sys_clk or negedge sys_rst) begin + if (sys_rst == 1'b0) begin + rx_done <= 'b0; + rx_data <= 'b0; + end + // 这个其实就是 busy 拉低的条件, 只要满足busy拉低条件,说明数据已经可用, 那立即拉高done(1个周期) + else if(rx_d_cnt == 'b1001 && baud_cnt == B_MAX>>1 - 1) begin + rx_done <= 'b1; + rx_data <= temp_rx_data; + end + else begin + // 否则 就是没有完成 done持续的就是一个时钟周期(busy 拉低会让rx_d_cnt和baud_cnt 也清零) + rx_done <= 'b0; + // 没有完成 就继续保持data, 只有继续为 done的时候 才会继续更新 rx_data的值, 就是一直保持呗, 防止外部对done打拍, 采集不到data数据 + rx_data <= rx_data; + end +end + + +endmodule diff --git a/uart/uart_rx.v.out b/uart/uart_rx.v.out new file mode 100755 index 0000000..25f6a5c --- /dev/null +++ b/uart/uart_rx.v.out @@ -0,0 +1,343 @@ +#! /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_0x7fc93e114320 .scope module, "uart_rx" "uart_rx" 2 1; + .timescale 0 0; + .port_info 0 /INPUT 1 "sys_clk"; + .port_info 1 /INPUT 1 "sys_rst"; + .port_info 2 /INPUT 1 "rxd"; + .port_info 3 /OUTPUT 1 "rx_done"; + .port_info 4 /OUTPUT 8 "rx_data"; +P_0x7fc93e114d20 .param/l "BPS" 0 2 12, +C4<00000000000000011100001000000000>; +P_0x7fc93e114d60 .param/l "B_MAX" 1 2 14, +C4<00000000000000000000000110110010>; +P_0x7fc93e114da0 .param/l "CLK_FREQ" 0 2 11, +C4<00000010111110101111000010000000>; +L_0x7fc93e129c60 .functor AND 1, v0x7fc93e129810_0, L_0x7fc93e129bc0, C4<1>, C4<1>; +L_0x7fc93e129e10 .functor AND 1, L_0x7fc93e129c60, L_0x7fc93e129d50, C4<1>, C4<1>; +v0x7fc93e114e30_0 .net *"_ivl_1", 0 0, L_0x7fc93e129bc0; 1 drivers +v0x7fc93e129110_0 .net *"_ivl_3", 0 0, L_0x7fc93e129c60; 1 drivers +v0x7fc93e1291b0_0 .net *"_ivl_5", 0 0, L_0x7fc93e129d50; 1 drivers +v0x7fc93e129240_0 .var "baud_cnt", 15 0; +v0x7fc93e1292f0_0 .var "rx_busy", 0 0; +v0x7fc93e1293d0_0 .var "rx_d_cnt", 3 0; +v0x7fc93e129480_0 .var "rx_data", 7 0; +v0x7fc93e129530_0 .var "rx_done", 0 0; +o0x7fc93e232188 .functor BUFZ 1, C4; HiZ drive +v0x7fc93e1295d0_0 .net "rxd", 0 0, o0x7fc93e232188; 0 drivers +v0x7fc93e1296e0_0 .var "rxd_d0", 0 0; +v0x7fc93e129770_0 .var "rxd_d1", 0 0; +v0x7fc93e129810_0 .var "rxd_d2", 0 0; +v0x7fc93e1298b0_0 .net "start_en", 0 0, L_0x7fc93e129e10; 1 drivers +o0x7fc93e232278 .functor BUFZ 1, C4; HiZ drive +v0x7fc93e129950_0 .net "sys_clk", 0 0, o0x7fc93e232278; 0 drivers +o0x7fc93e2322a8 .functor BUFZ 1, C4; HiZ drive +v0x7fc93e1299f0_0 .net "sys_rst", 0 0, o0x7fc93e2322a8; 0 drivers +v0x7fc93e129a90_0 .var "temp_rx_data", 7 0; +E_0x7fc93e106d90/0 .event negedge, v0x7fc93e1299f0_0; +E_0x7fc93e106d90/1 .event posedge, v0x7fc93e129950_0; +E_0x7fc93e106d90 .event/or E_0x7fc93e106d90/0, E_0x7fc93e106d90/1; +L_0x7fc93e129bc0 .reduce/nor v0x7fc93e129770_0; +L_0x7fc93e129d50 .reduce/nor v0x7fc93e1292f0_0; + .scope S_0x7fc93e114320; +T_0 ; + %wait E_0x7fc93e106d90; + %load/vec4 v0x7fc93e1299f0_0; + %cmpi/e 0, 0, 1; + %jmp/0xz T_0.0, 4; + %pushi/vec4 0, 0, 1; + %assign/vec4 v0x7fc93e1296e0_0, 0; + %pushi/vec4 0, 0, 1; + %assign/vec4 v0x7fc93e129770_0, 0; + %pushi/vec4 0, 0, 1; + %assign/vec4 v0x7fc93e129810_0, 0; + %jmp T_0.1; +T_0.0 ; + %load/vec4 v0x7fc93e1295d0_0; + %assign/vec4 v0x7fc93e1296e0_0, 0; + %load/vec4 v0x7fc93e1296e0_0; + %assign/vec4 v0x7fc93e129770_0, 0; + %load/vec4 v0x7fc93e129770_0; + %assign/vec4 v0x7fc93e129810_0, 0; +T_0.1 ; + %jmp T_0; + .thread T_0; + .scope S_0x7fc93e114320; +T_1 ; + %wait E_0x7fc93e106d90; + %load/vec4 v0x7fc93e1299f0_0; + %cmpi/e 0, 0, 1; + %jmp/0xz T_1.0, 4; + %pushi/vec4 0, 0, 1; + %assign/vec4 v0x7fc93e1292f0_0, 0; + %jmp T_1.1; +T_1.0 ; + %load/vec4 v0x7fc93e1298b0_0; + %flag_set/vec4 8; + %jmp/0xz T_1.2, 8; + %pushi/vec4 1, 0, 1; + %assign/vec4 v0x7fc93e1292f0_0, 0; + %jmp T_1.3; +T_1.2 ; + %load/vec4 v0x7fc93e1293d0_0; + %pad/u 32; + %cmpi/e 9, 0, 32; + %flag_get/vec4 4; + %jmp/0 T_1.6, 4; + %load/vec4 v0x7fc93e129240_0; + %pad/u 32; + %pushi/vec4 434, 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 v0x7fc93e1292f0_0, 0; + %jmp T_1.5; +T_1.4 ; + %load/vec4 v0x7fc93e1292f0_0; + %assign/vec4 v0x7fc93e1292f0_0, 0; +T_1.5 ; +T_1.3 ; +T_1.1 ; + %jmp T_1; + .thread T_1; + .scope S_0x7fc93e114320; +T_2 ; + %wait E_0x7fc93e106d90; + %load/vec4 v0x7fc93e1299f0_0; + %cmpi/e 0, 0, 1; + %jmp/0xz T_2.0, 4; + %pushi/vec4 0, 0, 16; + %assign/vec4 v0x7fc93e129240_0, 0; + %jmp T_2.1; +T_2.0 ; + %load/vec4 v0x7fc93e1292f0_0; + %flag_set/vec4 8; + %jmp/0xz T_2.2, 8; + %load/vec4 v0x7fc93e129240_0; + %pad/u 32; + %cmpi/e 433, 0, 32; + %jmp/0xz T_2.4, 4; + %pushi/vec4 0, 0, 16; + %assign/vec4 v0x7fc93e129240_0, 0; + %jmp T_2.5; +T_2.4 ; + %load/vec4 v0x7fc93e129240_0; + %addi 1, 0, 16; + %assign/vec4 v0x7fc93e129240_0, 0; +T_2.5 ; + %jmp T_2.3; +T_2.2 ; + %pushi/vec4 0, 0, 16; + %assign/vec4 v0x7fc93e129240_0, 0; +T_2.3 ; +T_2.1 ; + %jmp T_2; + .thread T_2; + .scope S_0x7fc93e114320; +T_3 ; + %wait E_0x7fc93e106d90; + %load/vec4 v0x7fc93e1299f0_0; + %cmpi/e 0, 0, 1; + %jmp/0xz T_3.0, 4; + %pushi/vec4 0, 0, 4; + %assign/vec4 v0x7fc93e1293d0_0, 0; + %jmp T_3.1; +T_3.0 ; + %load/vec4 v0x7fc93e1292f0_0; + %flag_set/vec4 8; + %jmp/0xz T_3.2, 8; + %load/vec4 v0x7fc93e129240_0; + %pad/u 32; + %cmpi/e 433, 0, 32; + %jmp/0xz T_3.4, 4; + %load/vec4 v0x7fc93e1293d0_0; + %addi 1, 0, 4; + %assign/vec4 v0x7fc93e1293d0_0, 0; + %jmp T_3.5; +T_3.4 ; + %load/vec4 v0x7fc93e1293d0_0; + %assign/vec4 v0x7fc93e1293d0_0, 0; +T_3.5 ; + %jmp T_3.3; +T_3.2 ; + %pushi/vec4 0, 0, 4; + %assign/vec4 v0x7fc93e1293d0_0, 0; +T_3.3 ; +T_3.1 ; + %jmp T_3; + .thread T_3; + .scope S_0x7fc93e114320; +T_4 ; + %wait E_0x7fc93e106d90; + %load/vec4 v0x7fc93e1299f0_0; + %cmpi/e 0, 0, 1; + %jmp/0xz T_4.0, 4; + %pushi/vec4 0, 0, 8; + %assign/vec4 v0x7fc93e129a90_0, 0; + %jmp T_4.1; +T_4.0 ; + %load/vec4 v0x7fc93e1292f0_0; + %flag_set/vec4 8; + %jmp/0xz T_4.2, 8; + %load/vec4 v0x7fc93e129240_0; + %pad/u 32; + %cmpi/e 434, 0, 32; + %jmp/0xz T_4.4, 4; + %load/vec4 v0x7fc93e1293d0_0; + %dup/vec4; + %pushi/vec4 1, 0, 4; + %cmp/u; + %jmp/1 T_4.6, 6; + %dup/vec4; + %pushi/vec4 2, 0, 4; + %cmp/u; + %jmp/1 T_4.7, 6; + %dup/vec4; + %pushi/vec4 3, 0, 4; + %cmp/u; + %jmp/1 T_4.8, 6; + %dup/vec4; + %pushi/vec4 4, 0, 4; + %cmp/u; + %jmp/1 T_4.9, 6; + %dup/vec4; + %pushi/vec4 5, 0, 4; + %cmp/u; + %jmp/1 T_4.10, 6; + %dup/vec4; + %pushi/vec4 6, 0, 4; + %cmp/u; + %jmp/1 T_4.11, 6; + %dup/vec4; + %pushi/vec4 7, 0, 4; + %cmp/u; + %jmp/1 T_4.12, 6; + %dup/vec4; + %pushi/vec4 8, 0, 4; + %cmp/u; + %jmp/1 T_4.13, 6; + %jmp T_4.15; +T_4.6 ; + %load/vec4 v0x7fc93e129810_0; + %ix/load 4, 0, 0; + %ix/load 5, 0, 0; + %flag_set/imm 4, 0; + %assign/vec4/off/d v0x7fc93e129a90_0, 4, 5; + %jmp T_4.15; +T_4.7 ; + %load/vec4 v0x7fc93e129810_0; + %ix/load 4, 1, 0; + %ix/load 5, 0, 0; + %flag_set/imm 4, 0; + %assign/vec4/off/d v0x7fc93e129a90_0, 4, 5; + %jmp T_4.15; +T_4.8 ; + %load/vec4 v0x7fc93e129810_0; + %ix/load 4, 2, 0; + %ix/load 5, 0, 0; + %flag_set/imm 4, 0; + %assign/vec4/off/d v0x7fc93e129a90_0, 4, 5; + %jmp T_4.15; +T_4.9 ; + %load/vec4 v0x7fc93e129810_0; + %ix/load 4, 3, 0; + %ix/load 5, 0, 0; + %flag_set/imm 4, 0; + %assign/vec4/off/d v0x7fc93e129a90_0, 4, 5; + %jmp T_4.15; +T_4.10 ; + %load/vec4 v0x7fc93e129810_0; + %ix/load 4, 4, 0; + %ix/load 5, 0, 0; + %flag_set/imm 4, 0; + %assign/vec4/off/d v0x7fc93e129a90_0, 4, 5; + %jmp T_4.15; +T_4.11 ; + %load/vec4 v0x7fc93e129810_0; + %ix/load 4, 5, 0; + %ix/load 5, 0, 0; + %flag_set/imm 4, 0; + %assign/vec4/off/d v0x7fc93e129a90_0, 4, 5; + %jmp T_4.15; +T_4.12 ; + %load/vec4 v0x7fc93e129810_0; + %ix/load 4, 6, 0; + %ix/load 5, 0, 0; + %flag_set/imm 4, 0; + %assign/vec4/off/d v0x7fc93e129a90_0, 4, 5; + %jmp T_4.15; +T_4.13 ; + %load/vec4 v0x7fc93e129810_0; + %ix/load 4, 7, 0; + %ix/load 5, 0, 0; + %flag_set/imm 4, 0; + %assign/vec4/off/d v0x7fc93e129a90_0, 4, 5; + %jmp T_4.15; +T_4.15 ; + %pop/vec4 1; + %jmp T_4.5; +T_4.4 ; + %load/vec4 v0x7fc93e129a90_0; + %assign/vec4 v0x7fc93e129a90_0, 0; +T_4.5 ; + %jmp T_4.3; +T_4.2 ; + %pushi/vec4 0, 0, 8; + %assign/vec4 v0x7fc93e129a90_0, 0; +T_4.3 ; +T_4.1 ; + %jmp T_4; + .thread T_4; + .scope S_0x7fc93e114320; +T_5 ; + %wait E_0x7fc93e106d90; + %load/vec4 v0x7fc93e1299f0_0; + %cmpi/e 0, 0, 1; + %jmp/0xz T_5.0, 4; + %pushi/vec4 0, 0, 1; + %assign/vec4 v0x7fc93e129530_0, 0; + %pushi/vec4 0, 0, 8; + %assign/vec4 v0x7fc93e129480_0, 0; + %jmp T_5.1; +T_5.0 ; + %load/vec4 v0x7fc93e1293d0_0; + %pad/u 32; + %cmpi/e 9, 0, 32; + %flag_get/vec4 4; + %jmp/0 T_5.4, 4; + %load/vec4 v0x7fc93e129240_0; + %pad/u 32; + %pushi/vec4 434, 0, 32; + %cmp/e; + %flag_get/vec4 4; + %and; +T_5.4; + %flag_set/vec4 8; + %jmp/0xz T_5.2, 8; + %pushi/vec4 1, 0, 1; + %assign/vec4 v0x7fc93e129530_0, 0; + %load/vec4 v0x7fc93e129a90_0; + %assign/vec4 v0x7fc93e129480_0, 0; + %jmp T_5.3; +T_5.2 ; + %pushi/vec4 0, 0, 1; + %assign/vec4 v0x7fc93e129530_0, 0; + %load/vec4 v0x7fc93e129480_0; + %assign/vec4 v0x7fc93e129480_0, 0; +T_5.3 ; +T_5.1 ; + %jmp T_5; + .thread T_5; +# The file index is used to find the file name in the following table. +:file_names 3; + "N/A"; + ""; + "uart_rx.v";