赞
踩
// Description: 串口收发:串口接收数据,内部生成数据,串口间隔特定时间发送数据
// 串口接收数据:串行信号线 1101_1000 ,转为并行数据,取反截取低4位 传递给led,再传递给data_gen,
// 发送数据: 0.1ms生成/发送一位数据,发送字符串"==HELLO WORLD=" (对应ASCII码),以及led值
uart_rx.v
uart_tx.v
uart_data_gen.v
uart_top.v
uart_data_gen.v
`timescale 1ns / 1ps // // Company: Myminieye // Engineer: Nill // // Create Date: 2019-08-20 14:36 // Design Name: // Module Name: uart_data_gen // Project Name: // Target Devices: // Tool Versions: // Description: 产生的数据,用于串口发送 // 字符串"====HELLO WORLD===" // 每秒1s产生一个字符,有可能再加上串口接收的数据( write_max_num 比8'h14 大时) // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // `define UD #1 module uart_data_gen #( parameter CLK_CNT_1S = 26'h0FA0 //定义参数,计数次数 //晶振40MHz,1s需要计数的 40M次 = 40*10^6 = 0x2625A00 //parameter clk_cnt = 26'h2625A00; ) ( input clk, input rst_n, input [7:0] read_data, input tx_busy, input [7:0] write_max_num, //字符串长度,发送的最大字符数 8'h14(20个) output reg [7:0] write_data, //生成的数据,传出给到串口发送模块 uart_tx output reg write_en //开始生成数据,传递给uart_tx模块,开启串口发送状态tx_en ); //========================================================================== //wire and reg 定义:信号与参数 //========================================================================== reg [25:0] time_cnt=0; //计数次数,记到26'h2625A00 reg [ 7:0] data_num; //8位数字,位数 // 设置串口发射工作区间 reg work_en=0; reg work_en_1d=0; //========================================================================== //内部信号触发,生成数据使能 work_en :1s计时, 每秒产生一个字符 //========================================================================== //计数递增,与计数截止的if分开 always @(posedge clk) begin time_cnt <= `UD time_cnt + 26'd1; end //============ work_en使能,开始生成数据:内部计时1s, ============ //计数时间到,work_en生成数据使能,一个字符串/每秒 always @(posedge clk) begin if(time_cnt == CLK_CNT_1S) //计数到1s //if(time_cnt == 5'd2048) work_en <= `UD 1'b1; //开始生成数据 else if(data_num == write_max_num-1'b1)//第19个字符 work_en <= `UD 1'b0; //生成数据结束 end //生成数据使能,打一拍:work_en_1d比work_en,慢一拍 //计时刚到1s,work_en为1,work_en_1d为0 always @(posedge clk) begin work_en_1d <= `UD work_en; end //========================================================================== //外部信号触发:串口发送模块接触忙碌,获取tx_busy的下降沿 //========================================================================== //===========发送寄存器,输入状态 tx_busy_f :忙->空闲============ //获取tx_busy的下降沿,从忙变成不忙:1变成0 reg tx_busy_reg = 0; wire tx_busy_f; //发送忙状态,打一拍 always @ (posedge clk) tx_busy_reg <= `UD tx_busy; //tx_busy_f,下降沿fall assign tx_busy_f = (!tx_busy) && (tx_busy_reg); //========================================================================== //内部信号和外部信号,综合触发 //1、内部状态,触发产生数据动作:write_pluse 高有效,每秒产生一个字符 //2、外部状态,write_pluse -> write_en -> 输出,触发串口发送 //========================================================================== //===============触发信号:产生数据 & 发送数据================== /* @功能1: 计时刚到1s,可以发送,字符串的下一个数据,给出触发信号 (若发送状态机没走完,还是忙,等走完再发送) @数据流1: work_en + work_en_1d -> write_pulse(data_gen) -> write_en(data_gen) -> tx_en(top) -> tx_pulse(uart_tx) @功能2: (计时1s多)解除忙状态,执行发送,字符串的下一个数据,给出触发信号 @数据流2: tx_busy -> tx_busy_f -> write_pulse(data_gen) -> write_en(data_gen) -> tx_en(top) -> tx_pulse(uart_tx) @功能3: 1s刚到,此时发送也解除了忙,执行发送,给出触发信号 */ //========1、 触发产生数据动作:write_pluse 高有效======= reg write_pluse; always @ (posedge clk) begin if(!rst_n) write_pluse <= `UD 1'b0; else if(work_en) //此时,计时到了1s begin if(~work_en_1d || tx_busy_f) //1、work_en_1d=0,刚到1s(上一刻还没计数完,还是0),每1s发送 //2、tx_busy_f=1,早就到了1s,而且刚解除发送忙(下降沿)。 -> 相当于发送模块这8位发完了,传出tx_busy不忙,进入data_gen,又返回发送模块,触发下一组8位发送 write_pluse <= `UD 1'b1; else write_pluse <= `UD 1'b0; /*// 把 或关系||拆开,换个写法 if(~work_en_1d) write_pluse <= `UD 1'b1; else if ( tx_busy_f) write_pluse <= `UD 1'b1; else write_pluse <= `UD 1'b0; */ end else write_pluse <= `UD 1'b0; end //==========2、开始生成数据状态 write_en,高有效,输出 ======== //打一拍,作为发送触发,反馈给输出模块 always @(posedge clk) begin write_en <= `UD write_pluse; //write_en比 write_pluse 慢一拍 end //=================产生数据1:数据位递增====================== // always @ (posedge clk) begin if ( !rst_n ) data_num <= `UD 8'h0; else if ( ~work_en & tx_busy_f ) //从0开始发送新的字符串:字符串(20个字符)发完了,并且发送状态刚解除忙(下降沿) data_num <= 8'h0; //源代码为 7'h0? else if ( write_pluse ) //此字符串,发送下一个字符:计时刚到1s,或者1s多了解除了发送忙状态 data_num <= data_num + 8'h1; end //================== 产生数据2:向外送出字符串 "====HELLO WORLD===" // 字符的对应ASCII码 // H:0x48 E:0x45 L:0x4C O:0x4F W:0x57 R:0x52 D:0x44 always @ (posedge clk) begin case(data_num) 8'h0 , 8'h1 , 8'h2 , 8'h3 : write_data <= `UD 8'h3D;// ASCII code is = 8'h4 : write_data <= `UD 8'h48;// ASCII code is H 8'h5 : write_data <= `UD 8'h45;// ASCII code is E 8'h6 : write_data <= `UD 8'h4C;// ASCII code is L 8'h7 : write_data <= `UD 8'h4C;// ASCII code is L 8'h8 : write_data <= `UD 8'h4F;// ASCII code is 0 8'h9 : write_data <= `UD 8'h20;// ASCII code is 8'ha : write_data <= `UD 8'h57;// ASCII code is W 8'hb : write_data <= `UD 8'h4F;// ASCII code is O 8'hc : write_data <= `UD 8'h52;// ASCII code is R 8'hd : write_data <= `UD 8'h4C;// ASCII code is L 8'he : write_data <= `UD 8'h44;// ASCII code is D 8'hf , 8'h10 , 8'h11 : write_data <= `UD 8'h3D;// ASCII code is = 8'h12 : write_data <= `UD 8'h0d;//回车 8'h13 : write_data <= `UD 8'h0a;//换行 default: write_data <= `UD read_data;//默认态,数字保持不变,输入数据转发出去 //在top模块中修改 write_max_num,比8'h14 大时,可以把串口接收的数据(给到led)再发出去 endcase end endmodule
uart_top.v
`timescale 1ns / 1ps // // Company: Myminieye // Engineer: Nill // // Create Date: 2019-08-20 14:36 // Design Name: // Module Name: uart // Project Name: // Target Devices: // Tool Versions: // Description: 串口收发数据 // 串口接收的数据,取反,点亮LED灯: // 高电平点亮, 发送数据为 0xF0 或者 11110000 // 串口发送的数据,由data_gen生成,发送字符串"====HELLO WORLD===",每秒发一个字符,加换行回车共计19个 // 若 write_max_num比8'h14大,可以把串口接收的数据(给到led)再发出去 // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // `define UD #1 module uart_top #( parameter BPS_NUM = 16'd347, // 时钟/波特率,1 bit位宽所需时钟周期的个数 CLK_CNT_1S = 16'h0FA0 //1s发一次,40M次 = 40*10^6 = 0x2625A00 //0.1ms一次, 40M*10^(-4) = 40*10^6*10^(-4) = 40*100=4000 // parameter BPS_4800: 40MHz set 8333 ; 50MHz set 10417 // parameter BPS_9600: 40MHz set 4167 ; 50MHz set 5208 // parameter BPS_115200: 40MHz set 347; 50MHz set 434 ) ( //input ports input clk, input rst_n, input uart_rx, //output ports output [3:0] led, output uart_tx, //仿真追加:传递以下变量 output [7:0] rx_data, //串口接收的数据 output rx_finish, //串口接收数据有效,接收完成拉高1个BPS output [7:0] tx_data, //产生的数据(含接收的数据) output tx_finish //串口发送数据结束标志,8位数据发完,拉高一个BPS ); //LED指示灯:BK12_W15,17,14,16 -> led[0],1,2,3(变量命名)-> LED4,3,2,1 (丝印从下到上) //========================================================================== //wire and reg 定义:信号与参数 //========================================================================== wire tx_busy; //transmitter is free. wire tx_en; //开启串口发送,整个发送周期都拉高 wire [7:0] write_data; assign tx_data = write_data; reg [7:0] write_max_num = 8'h15;//发送字符串位数,20位 //在top模块中修改 write_max_num,比8'h14 大时,可以把串口接收的数据(给到led)再发出去 reg [7:0] receive_data; always @(posedge clk) receive_data <= led ; //========================================================================== //调用top模块 //========================================================================== //产生数据,1s一个 uart_data_gen #( .CLK_CNT_1S ( CLK_CNT_1S ) ) u_uart_data_gen( .clk ( clk ),//input clk, .rst_n ( rst_n ),//input rst_n, .read_data ( receive_data ),//input [7:0] read_data, .tx_busy ( tx_busy ),//input 输入,串口发送忙状态 .write_max_num( write_max_num),//input [7:0] write_max_num, .write_data ( write_data ),//output reg [7:0] 生成的数据 .write_en ( tx_en ) //output reg 输出,当前为生成数据状态 ); //串口发送 uart_tx #( //.CLK_SYS ( SYS_CLK_FRE), //系统时钟 .BPS_NUM ( BPS_NUM ) // 时钟/波特率,1 bit位宽所需时钟周期的个数 ) u_uart_tx( .clk ( clk ),// input clk, .tx_data ( tx_data ),// input [7:0] tx_data, .tx_pulse ( tx_en ),// input 外部输入,开始产生数据->开启串口发送状态 .uart_tx ( uart_tx ),// output reg uart_tx, .tx_busy ( tx_busy ), // output 输出,串口接收忙状态 .tx_finish( tx_finish) // output //串口发送数据结束标志,8位数据发完,拉高一个BPS ); //串口接收 uart_rx #( //.CLK_SYS ( SYS_CLK_FRE), //系统时钟 .BPS_NUM ( BPS_NUM ) // 时钟/波特率,1 bit位宽所需时钟周期的个数 ) u_uart_rx ( .clk ( clk ),// input clk, .uart_rx ( uart_rx ),// input uart_rx, .rx_data ( rx_data ),// output reg [7:0] rx_data, .rx_finish ( rx_finish ) // output 串口接收数据有效,接收完成拉高1个BPS //.rx_end ( rx_end ) // output //接收到停止位,拉高1个clk,没啥用 ); assign led = ~rx_data;//8位数据截断高4位,只剩下低4位 endmodule
tb_uart_top.v
`timescale 1ns / 1ps //SimulationCode:Test Bench/ // Company: // Engineer: // // Create Date: 2020/05/21 // Design Name: // Module Name: tb_uart_top // Project Name: 串口收发的测试文件 // Target Devices: // Tool Versions: // Description: 串口收发:串口接收数据,内部生成数据,串口间隔特定时间发送数据 // 串口接收数据:串行信号线 1101_1000 ,转为并行数据,取反截取低4位 传递给led,再传递给data_gen, // 发送数据: 0.1ms生成/发送一位数据,发送字符串"====HELLO WORLD===" (对应ASCII码),以及led值 // Dependencies: // 仿真周期 run 3ms // Revision: // Revision 0.01 - File Created // Additional Comments: // // `define UD #1 module uart_top_tb; //========================================================================== //定义:信号与参数 //========================================================================== reg sim_clk; //模拟时钟信号 //reg tx_pulse; // active posedge reg sim_rst_n; // input to rx module reg sim_uart_rx; //串口发送信号线 //output from rx module wire [3:0] sim_led; //接收数据,取反,取低4位,传给led wire sim_uart_tx; //串口接收数据有效,接收完成拉高1个BPS //================================ //仿真追加:将以下变量从下级模块中传出来 // uart_rx/tx -> uart_top -> uart_top_tb //================================ wire rx_finish ; //串口接收数据结束,接收完成拉高1个BPS reg rx_finish_reg; wire tx_finish ; //串口发送数据结束,8位数据发完,拉高一个BPS reg tx_finish_reg; wire [7:0] rx_data ; //串口接收的数据(8位) wire [7:0] tx_data ; //串口发送的数据(生成的字符串 + 接收的8位数据) //时钟参数 parameter SYS_CLK_FRE = 40_000_000; //系统频率40MHz 40_000_000 parameter SYS_CLK_PERIOD = 1_000_000_000/SYS_CLK_FRE; //周期25ns parameter RST_CYCLE = 5; //复位持续时间,clk时钟周期数 parameter RST_TIME = RST_CYCLE * SYS_CLK_PERIOD; //复位时间:5个时钟周期 //波特率参数 parameter BAUD_RATE = 115200; //串口波特率 parameter BAUD_RATE_PERIOD = 1_000_000_000/BAUD_RATE; //波特率周期,0.104ms = 104us,1/9600 s = 1^9 /9600 ns = 4167 sim_clk //波特率周期, 1/115200 = 8680 ns = 8.7us = 347 sim_clk //波特率+时钟参数 parameter [15:0] BPS_NUM = SYS_CLK_FRE / BAUD_RATE; //时钟/波特率,用时钟周期构造波特率周期 // BPS_NUM = 40_000_000/115200 = 347.22 = 16'd347 // 1 bit位宽所需时钟周期的个数。最长的波特率计数,10417,二进制有14位,取16位 // parameter BPS_4800: 40MHz set 8333 ; 50MHz set 10417 // parameter BPS_9600: 40MHz set 4167 ; 50MHz set 5208 // parameter BPS_115200: 40MHz set 347; 50MHz set 434 //串口发送数据的周期 parameter CLK_CNT_1S = 16'h0FA0; //仿真改为:0.1ms一次, 40M*10^(-4) = 40*10^6*10^(-4) =4000 = 0xFA0 //实际:1s发一次,40M次 = 40*10^6 = 0x2625A00 //CLK_CNT_1S = 26'h2625A00 //========================================================================== //logic:逻辑信号初始化与判断 //========================================================================== //模拟系统时钟:40MHz,25ns always #((SYS_CLK_PERIOD+1)/2-1) sim_clk = ~sim_clk; //延时,电平翻转 //若以rx_finish/tx_finish 触发,则rx_data/tx_data还是上一刻的数据 //打一拍,延迟,rx_data/tx_data数值已改变 always @(posedge sim_clk) begin rx_finish_reg <= `UD rx_finish; tx_finish_reg <= `UD tx_finish; end always @(posedge rx_finish_reg) $display("Note 2 :The rx_data 8'h%h has received.",rx_data); //命令行显示:rx_data always @(posedge tx_finish_reg) $display("Note 4 :The rx_data 8'h%h has received.",tx_data); //命令行显示:tx_data /* always @(posedge sim_clk) begin if(tx_finish_reg && ~tx_finish) begin //接收数据完成(触发1次) sim_tx_data <= `UD tx_data; $display("Note 4 :The tx_data 8'h%h has been sent.",sim_tx_data); //命令行显示:tx_data end end */ //========================================================================== //模拟:信号的输入,显示输出结果 //========================================================================== initial begin //模拟复位信号:拉低一次 #0; sim_clk = 1'b0; sim_rst_n = 1'b0; //复位拉低,有效, sim_uart_rx = 1'b1; //串口线,默认高,起始拉低 //rx_data <= `UD 8'h00; //rx_data 初始化,在 uart_rx中完成 //#RST_TIME; //延时:保持足够长时间(至少5个clk) #BAUD_RATE_PERIOD; //5个clk时间轴太短,仿真改为1个BPS,更明显 sim_rst_n = 1'b1; //解除复位 //========================================================================== //模拟串口接收:串行信号输入,转化成并行数据,并显示 //========================================================================== sim_uart_rx = 1'b1; //串口发送线,默认拉高 repeat( BPS_NUM*1 ) @(posedge sim_clk); //循环347个时钟周期,即一个波特率周期 //#BAUD_RATE_PERIOD; //直接延时,一个波特率周期 $display("Note 1 : Initialization complete. BAUD_RATE is %d",BAUD_RATE); //命令行显示初始化完成,输出BAUD_RATE //串口:起始位 sim_uart_rx = 1'b0; #BAUD_RATE_PERIOD; //串行数据,一位一位送入接收信号线:***从位0到位7***,依次发送 //测试数据为8'hD8=8'b1101_1000 sim_uart_rx = 1'b0; #BAUD_RATE_PERIOD; sim_uart_rx = 1'b0; #BAUD_RATE_PERIOD; sim_uart_rx = 1'b0; #BAUD_RATE_PERIOD; sim_uart_rx = 1'b1; #BAUD_RATE_PERIOD; sim_uart_rx = 1'b1; #BAUD_RATE_PERIOD; sim_uart_rx = 1'b0; #BAUD_RATE_PERIOD; sim_uart_rx = 1'b1; #BAUD_RATE_PERIOD; sim_uart_rx = 1'b1; $display("Note 2 : The uart_rx 8'hD8 = 8'b1101_1000 has been sent."); //命令行显示:串口信号线数据已发送 #BAUD_RATE_PERIOD; //串口:结束位 sim_uart_rx = 1'b1; #BAUD_RATE_PERIOD; $display("Note 3 :*** The led data 8'h%h has received.",sim_led); //命令行显示:sim_led //命令行显示:串口信号线接收已结束,显示接收到的数据 //========================================================================== //模拟串口发送:并行数据,串行输出 //========================================================================== //*** data_gen模块生成字符串,以及串口接收的数据,依次从串口输出*** repeat( BPS_NUM*1 ) @(posedge sim_clk); //循环347个时钟周期,即一个波特率周期 //#BAUD_RATE_PERIOD; //直接延时,一个波特率周期 //传递 第一组数据:8位并行数据,一次性送入串口发送模块 //sim_tx_data = rx_data; //串口接收数据,传给串口发送 #BAUD_RATE_PERIOD; //开启触发信号:串口发送 //tx_pulse = 1; //用 rx_en 代替 模拟tx_pulse,直接触发 #BAUD_RATE_PERIOD; //rx_en 串口接收数据有效,接收完成拉高1个BPS //结束触发:串口发送 //tx_pulse = 0; #BAUD_RATE_PERIOD; //0.1ms发一次数据,20位有2ms = 250个 BPS repeat( BPS_NUM*250 ) @(posedge sim_clk);//等待发送数据完成(触发20次) $stop; //结束仿真 end //initial //========================================================================== //调用top模块 //========================================================================== uart_top #( .BPS_NUM ( BPS_NUM ), // 时钟/波特率,1 bit位宽所需时钟周期的个数 .CLK_CNT_1S ( CLK_CNT_1S ) //串口发送数据的周期 ) sim_uart_top( .clk (sim_clk), .rst_n (sim_rst_n), .uart_rx (sim_uart_rx), .led (sim_led), .uart_tx (sim_uart_tx), //仿真追加:将以下变量从下级模块中传出来 // uart_rx/tx -> uart_top -> uart_top_tb .rx_data (rx_data ), .rx_finish(rx_finish), .tx_data (tx_data ), .tx_finish(tx_finish) ); endmodule //module uart_top_tb //循环移位发送数据 /* parameter uart_rx_data = initial begin //模拟复位信号:一次,低电平5个clk #0; sim_clk = 1'b0; sim_rst_n = 1'b0; //复位拉低,有效, sim_uart_rx = 1'b1; //串口发送线,默认拉高 #RST_TIME; //延时:保持足够长时间(5个clk) sim_rst_n = 1'b1; //解除复位 repeat( 10 ) begin //串口起始信号:高电平到低电平(下降沿) #BAUD_RATE_PERIOD; sim_uart_rx = 1'b1; //时间延迟BPS_CNT ns, 检查`timescale 1 ns/ 1 ps #BAUD_RATE_PERIOD; sim_uart_rx = 1'b0; repeat(12560) @(posedge sim_clk); end end //串口数据信号:从第0位到第7位, 第8位停止位 always @(posedge data_clk) begin if( cnt == 4'h8 ) begin cnt <= 1'b0; sim_uart_rx = 1'b1; //第8位后,停止位也为1 $display("Running testbench:Pass"); //在命令行显示pass这串字符。 end else begin cnt <= cnt +1'b1; sim_uart_rx <= uart_rx_data[cnt]; end end */
ModelSim波形
忘了标记,接收数据取反,截取,最后一位 07,也输出了
命令行:
生成数据代码对比
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。